mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 14:20:59 +00:00
my WIP on porting QuakeEd to GNUstep/QuakeForge
This commit is contained in:
parent
3423ed59d5
commit
48a5ad0d36
58 changed files with 27068 additions and 0 deletions
55
tools/Forge/Bundles/MapEdit/Brush.h
Normal file
55
tools/Forge/Bundles/MapEdit/Brush.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
#import <AppKit/AppKit.h>
|
||||
#import "SetBrush.h"
|
||||
#import "EditWindow.h"
|
||||
|
||||
extern id brush_i;
|
||||
|
||||
extern BOOL brushdraw; // YES when drawing cutbrushes and ents
|
||||
|
||||
@interface Brush : SetBrush
|
||||
{
|
||||
id cutbrushes_i;
|
||||
id cutentities_i;
|
||||
boolean updatemask[MAXBRUSHVERTEX];
|
||||
BOOL dontdraw; // for modal instance loops
|
||||
BOOL deleted; // when not visible at all
|
||||
}
|
||||
|
||||
- init;
|
||||
|
||||
- initFromSetBrush: br;
|
||||
|
||||
- deselect;
|
||||
- (BOOL)isSelected;
|
||||
|
||||
- (BOOL)XYmouseDown: (NSPoint *)pt; // return YES if brush handled
|
||||
- (BOOL)ZmouseDown: (NSPoint *)pt; // return YES if brush handled
|
||||
|
||||
- _keyDown:(NSEvent *)theEvent;
|
||||
|
||||
- (NSPoint)centerPoint; // for camera flyby mode
|
||||
|
||||
- InstanceSize;
|
||||
- XYDrawSelf;
|
||||
- ZDrawSelf;
|
||||
- CameraDrawSelf;
|
||||
|
||||
- flipHorizontal: sender;
|
||||
- flipVertical: sender;
|
||||
- rotate90: sender;
|
||||
|
||||
- makeTall: sender;
|
||||
- makeShort: sender;
|
||||
- makeWide: sender;
|
||||
- makeNarrow: sender;
|
||||
|
||||
- placeEntity: sender;
|
||||
|
||||
- cut: sender;
|
||||
- copy: sender;
|
||||
|
||||
- addBrush;
|
||||
|
||||
@end
|
||||
|
||||
|
61
tools/Forge/Bundles/MapEdit/CameraView.h
Normal file
61
tools/Forge/Bundles/MapEdit/CameraView.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
#import <AppKit/AppKit.h>
|
||||
#import "mathlib.h"
|
||||
#import "SetBrush.h"
|
||||
|
||||
extern id cameraview_i;
|
||||
|
||||
extern byte renderlist[1024*1024*4];
|
||||
|
||||
void CameraMoveto(vec3_t p);
|
||||
void CameraLineto(vec3_t p);
|
||||
|
||||
extern BOOL timedrawing;
|
||||
|
||||
@interface CameraView : NSView
|
||||
{
|
||||
float xa, ya, za;
|
||||
float move;
|
||||
|
||||
float *zbuffer;
|
||||
unsigned *imagebuffer;
|
||||
|
||||
BOOL angleChange; // JR 6.8.95
|
||||
|
||||
vec3_t origin;
|
||||
vec3_t matrix[3];
|
||||
|
||||
NSPoint dragspot;
|
||||
|
||||
drawmode_t drawmode;
|
||||
|
||||
// UI links
|
||||
id mode_radio_i;
|
||||
|
||||
}
|
||||
|
||||
- setXYOrigin: (NSPoint *)pt;
|
||||
- setZOrigin: (float)pt;
|
||||
|
||||
- setOrigin: (vec3_t)org angle: (float)angle;
|
||||
- getOrigin: (vec3_t)org;
|
||||
|
||||
- (float)yawAngle;
|
||||
|
||||
- matrixFromAngles;
|
||||
- _keyDown: (NSEvent *)theEvent;
|
||||
|
||||
- drawMode: sender;
|
||||
- setDrawMode: (drawmode_t)mode;
|
||||
|
||||
- homeView: sender;
|
||||
|
||||
- XYDrawSelf; // for drawing viewpoint in XY view
|
||||
- ZDrawSelf; // for drawing viewpoint in XY view
|
||||
- (BOOL)XYmouseDown: (NSPoint *)pt flags:(int)flags; // return YES if brush handled
|
||||
- (BOOL)ZmouseDown: (NSPoint *)pt flags:(int)flags; // return YES if brush handled
|
||||
|
||||
- upFloor:sender;
|
||||
- downFloor: sender;
|
||||
|
||||
@end
|
||||
|
971
tools/Forge/Bundles/MapEdit/CameraView.m
Normal file
971
tools/Forge/Bundles/MapEdit/CameraView.m
Normal file
|
@ -0,0 +1,971 @@
|
|||
#import "qedefs.h"
|
||||
|
||||
id cameraview_i;
|
||||
|
||||
BOOL timedrawing = 0;
|
||||
|
||||
@implementation CameraView
|
||||
|
||||
/*
|
||||
==================
|
||||
initWithFrame:
|
||||
==================
|
||||
*/
|
||||
- initWithFrame:(NSRect)frameRect
|
||||
{
|
||||
int size;
|
||||
|
||||
[super initWithFrame: frameRect];
|
||||
|
||||
cameraview_i = self;
|
||||
|
||||
xa = ya = za = 0;
|
||||
|
||||
[self matrixFromAngles];
|
||||
|
||||
origin[0] = 64;
|
||||
origin[1] = 64;
|
||||
origin[2] = 48;
|
||||
|
||||
move = 16;
|
||||
|
||||
size = _bounds.size.width * _bounds.size.height;
|
||||
zbuffer = malloc (size*4);
|
||||
imagebuffer = malloc (size*4);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- setXYOrigin: (NSPoint *)pt
|
||||
{
|
||||
origin[0] = pt->x;
|
||||
origin[1] = pt->y;
|
||||
return self;
|
||||
}
|
||||
|
||||
- setZOrigin: (float)pt
|
||||
{
|
||||
origin[2] = pt;
|
||||
return self;
|
||||
}
|
||||
|
||||
- setOrigin: (vec3_t)org angle: (float)angle
|
||||
{
|
||||
VectorCopy (org, origin);
|
||||
ya = angle;
|
||||
[self matrixFromAngles];
|
||||
return self;
|
||||
}
|
||||
|
||||
- getOrigin: (vec3_t)org
|
||||
{
|
||||
VectorCopy (origin, org);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (float)yawAngle
|
||||
{
|
||||
return ya;
|
||||
}
|
||||
|
||||
- upFloor:sender
|
||||
{
|
||||
sb_floor_dir = 1;
|
||||
sb_floor_dist = 99999;
|
||||
[map_i makeAllPerform: @selector(feetToFloor)];
|
||||
if (sb_floor_dist == 99999)
|
||||
{
|
||||
qprintf ("already on top floor");
|
||||
return self;
|
||||
}
|
||||
qprintf ("up floor");
|
||||
origin[2] += sb_floor_dist;
|
||||
[quakeed_i updateCamera];
|
||||
return self;
|
||||
}
|
||||
|
||||
- downFloor: sender
|
||||
{
|
||||
sb_floor_dir = -1;
|
||||
sb_floor_dist = -99999;
|
||||
[map_i makeAllPerform: @selector(feetToFloor)];
|
||||
if (sb_floor_dist == -99999)
|
||||
{
|
||||
qprintf ("already on bottom floor");
|
||||
return self;
|
||||
}
|
||||
qprintf ("down floor");
|
||||
origin[2] += sb_floor_dist;
|
||||
[quakeed_i updateCamera];
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
UI TARGETS
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
============
|
||||
homeView
|
||||
============
|
||||
*/
|
||||
- homeView: sender
|
||||
{
|
||||
xa = za = 0;
|
||||
|
||||
[self matrixFromAngles];
|
||||
|
||||
[quakeed_i updateAll];
|
||||
|
||||
qprintf ("homed view angle");
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- drawMode: sender
|
||||
{
|
||||
drawmode = [sender selectedColumn];
|
||||
[quakeed_i updateCamera];
|
||||
return self;
|
||||
}
|
||||
|
||||
- setDrawMode: (drawmode_t)mode
|
||||
{
|
||||
drawmode = mode;
|
||||
//XXX[mode_radio_i selectCellAt:0: mode];
|
||||
[quakeed_i updateCamera];
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
TRANSFORMATION METHODS
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
- matrixFromAngles
|
||||
{
|
||||
if (xa > M_PI*0.4)
|
||||
xa = M_PI*0.4;
|
||||
if (xa < -M_PI*0.4)
|
||||
xa = -M_PI*0.4;
|
||||
|
||||
// vpn
|
||||
matrix[2][0] = cos(xa)*cos(ya);
|
||||
matrix[2][1] = cos(xa)*sin(ya);
|
||||
matrix[2][2] = sin(xa);
|
||||
|
||||
// vup
|
||||
matrix[1][0] = cos(xa+M_PI/2)*cos(ya);
|
||||
matrix[1][1] = cos(xa+M_PI/2)*sin(ya);
|
||||
matrix[1][2] = sin(xa+M_PI/2);
|
||||
|
||||
// vright
|
||||
CrossProduct (matrix[2], matrix[1], matrix[0]);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- inverseTransform: (vec_t *)invec to:(vec_t *)outvec
|
||||
{
|
||||
vec3_t inverse[3];
|
||||
vec3_t temp;
|
||||
int i,j;
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
for (j=0 ; j<3 ; j++)
|
||||
inverse[i][j] = matrix[j][i];
|
||||
|
||||
temp[0] = DotProduct(invec, inverse[0]);
|
||||
temp[1] = DotProduct(invec, inverse[1]);
|
||||
temp[2] = DotProduct(invec, inverse[2]);
|
||||
|
||||
VectorAdd (temp, origin, outvec);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
DRAWING METHODS
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec3_t trans;
|
||||
int clipflags;
|
||||
vec3_t screen; // only valid if clipflags == 0
|
||||
} campt_t;
|
||||
#define CLIP_RIGHT 1
|
||||
#define CLIP_LEFT 2
|
||||
#define CLIP_TOP 4
|
||||
#define CLIP_BOTTOM 8
|
||||
#define CLIP_FRONT 16
|
||||
|
||||
int cam_cur;
|
||||
campt_t campts[2];
|
||||
|
||||
vec3_t r_matrix[3];
|
||||
vec3_t r_origin;
|
||||
float mid_x, mid_y;
|
||||
float topscale = (240.0/3)/160;
|
||||
float bottomscale = (240.0*2/3)/160;
|
||||
|
||||
extern plane_t frustum[5];
|
||||
|
||||
void MakeCampt (vec3_t in, campt_t *pt)
|
||||
{
|
||||
vec3_t temp;
|
||||
float scale;
|
||||
|
||||
// transform the points
|
||||
VectorSubtract (in, r_origin, temp);
|
||||
|
||||
pt->trans[0] = DotProduct(temp, r_matrix[0]);
|
||||
pt->trans[1] = DotProduct(temp, r_matrix[1]);
|
||||
pt->trans[2] = DotProduct(temp, r_matrix[2]);
|
||||
|
||||
// check clip flags
|
||||
if (pt->trans[2] < 1)
|
||||
pt->clipflags = CLIP_FRONT;
|
||||
else
|
||||
pt->clipflags = 0;
|
||||
|
||||
if (pt->trans[0] > pt->trans[2])
|
||||
pt->clipflags |= CLIP_RIGHT;
|
||||
else if (-pt->trans[0] > pt->trans[2])
|
||||
pt->clipflags |= CLIP_LEFT;
|
||||
|
||||
if (pt->trans[1] > pt->trans[2]*topscale )
|
||||
pt->clipflags |= CLIP_TOP;
|
||||
else if (-pt->trans[1] > pt->trans[2]*bottomscale )
|
||||
pt->clipflags |= CLIP_BOTTOM;
|
||||
|
||||
if (pt->clipflags)
|
||||
return;
|
||||
|
||||
// project
|
||||
scale = mid_x/pt->trans[2];
|
||||
pt->screen[0] = mid_x + pt->trans[0]*scale;
|
||||
pt->screen[1] = mid_y + pt->trans[1]*scale;
|
||||
}
|
||||
|
||||
|
||||
void CameraMoveto(vec3_t p)
|
||||
{
|
||||
campt_t *pt;
|
||||
|
||||
if (upath->numberOfPoints > 2048)
|
||||
lineflush ();
|
||||
|
||||
pt = &campts[cam_cur];
|
||||
cam_cur ^= 1;
|
||||
MakeCampt (p,pt);
|
||||
if (!pt->clipflags)
|
||||
{ // onscreen, so move there immediately
|
||||
UPmoveto (upath, pt->screen[0], pt->screen[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void ClipLine (vec3_t p1, vec3_t p2, int planenum)
|
||||
{
|
||||
float d, d2, frac;
|
||||
vec3_t new;
|
||||
plane_t *pl;
|
||||
float scale;
|
||||
|
||||
if (planenum == 5)
|
||||
{ // draw it!
|
||||
scale = mid_x/p1[2];
|
||||
new[0] = mid_x + p1[0]*scale;
|
||||
new[1] = mid_y + p1[1]*scale;
|
||||
UPmoveto (upath, new[0], new[1]);
|
||||
|
||||
scale = mid_x/p2[2];
|
||||
new[0] = mid_x + p2[0]*scale;
|
||||
new[1] = mid_y + p2[1]*scale;
|
||||
UPlineto (upath, new[0], new[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
pl = &frustum[planenum];
|
||||
|
||||
d = DotProduct (p1, pl->normal) - pl->dist;
|
||||
d2 = DotProduct (p2, pl->normal) - pl->dist;
|
||||
if (d <= ON_EPSILON && d2 <= ON_EPSILON)
|
||||
{ // off screen
|
||||
return;
|
||||
}
|
||||
|
||||
if (d >= 0 && d2 >= 0)
|
||||
{ // on front
|
||||
ClipLine (p1, p2, planenum+1);
|
||||
return;
|
||||
}
|
||||
|
||||
frac = d/(d-d2);
|
||||
new[0] = p1[0] + frac*(p2[0]-p1[0]);
|
||||
new[1] = p1[1] + frac*(p2[1]-p1[1]);
|
||||
new[2] = p1[2] + frac*(p2[2]-p1[2]);
|
||||
|
||||
if (d > 0)
|
||||
ClipLine (p1, new, planenum+1);
|
||||
else
|
||||
ClipLine (new, p2, planenum+1);
|
||||
}
|
||||
|
||||
int c_off, c_on, c_clip;
|
||||
|
||||
void CameraLineto(vec3_t p)
|
||||
{
|
||||
campt_t *p1, *p2;
|
||||
int bits;
|
||||
|
||||
p2 = &campts[cam_cur];
|
||||
cam_cur ^= 1;
|
||||
p1 = &campts[cam_cur];
|
||||
MakeCampt (p, p2);
|
||||
|
||||
if (p1->clipflags & p2->clipflags)
|
||||
{
|
||||
c_off++;
|
||||
return; // entirely off screen
|
||||
}
|
||||
|
||||
bits = p1->clipflags | p2->clipflags;
|
||||
|
||||
if (! bits )
|
||||
{
|
||||
c_on++;
|
||||
UPmoveto (upath, p1->screen[0], p1->screen[1]);
|
||||
UPlineto (upath, p2->screen[0], p2->screen[1]);
|
||||
return; // entirely on screen
|
||||
}
|
||||
|
||||
// needs to be clipped
|
||||
c_clip++;
|
||||
|
||||
ClipLine (p1->trans, p2->trans, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
drawSolid
|
||||
=============
|
||||
*/
|
||||
- drawSolid
|
||||
{
|
||||
unsigned char *planes[5];
|
||||
|
||||
//
|
||||
// draw it
|
||||
//
|
||||
VectorCopy (origin, r_origin);
|
||||
VectorCopy (matrix[0], r_matrix[0]);
|
||||
VectorCopy (matrix[1], r_matrix[1]);
|
||||
VectorCopy (matrix[2], r_matrix[2]);
|
||||
|
||||
r_width = _bounds.size.width;
|
||||
r_height = _bounds.size.height;
|
||||
r_picbuffer = imagebuffer;
|
||||
r_zbuffer = zbuffer;
|
||||
|
||||
r_drawflat = (drawmode == dr_flat);
|
||||
|
||||
REN_BeginCamera ();
|
||||
REN_ClearBuffers ();
|
||||
|
||||
//
|
||||
// render the setbrushes
|
||||
//
|
||||
[map_i makeAllPerform: @selector(CameraRenderSelf)];
|
||||
|
||||
//
|
||||
// display the output
|
||||
//
|
||||
[[self window] setBackingType:NSBackingStoreRetained];
|
||||
|
||||
planes[0] = (unsigned char *)imagebuffer;
|
||||
NSDrawBitmap(
|
||||
_bounds,
|
||||
r_width,
|
||||
r_height,
|
||||
8,
|
||||
3,
|
||||
32,
|
||||
r_width*4,
|
||||
NO,
|
||||
NO,
|
||||
@"RGB", //FIXME what should this be?
|
||||
(const unsigned char **const)planes
|
||||
);
|
||||
|
||||
//XXX NSPing ();
|
||||
[[self window] setBackingType:NSBackingStoreBuffered];
|
||||
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
drawWire
|
||||
===================
|
||||
*/
|
||||
- drawWire: (NSRect)rect
|
||||
{
|
||||
// copy current info to globals for the C callbacks
|
||||
mid_x = _bounds.size.width / 2;
|
||||
mid_y = 2 * _bounds.size.height / 3;
|
||||
|
||||
VectorCopy (origin, r_origin);
|
||||
VectorCopy (matrix[0], r_matrix[0]);
|
||||
VectorCopy (matrix[1], r_matrix[1]);
|
||||
VectorCopy (matrix[2], r_matrix[2]);
|
||||
|
||||
r_width = _bounds.size.width;
|
||||
r_height = _bounds.size.height;
|
||||
r_picbuffer = imagebuffer;
|
||||
r_zbuffer = zbuffer;
|
||||
|
||||
REN_BeginCamera ();
|
||||
|
||||
// erase window
|
||||
NSEraseRect (rect);
|
||||
|
||||
// draw all entities
|
||||
linestart (0,0,0);
|
||||
[map_i makeUnselectedPerform: @selector(CameraDrawSelf)];
|
||||
lineflush ();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
drawSelf
|
||||
===================
|
||||
*/
|
||||
- drawSelf:(NSRect)rects :(int)rectCount
|
||||
{
|
||||
static float drawtime; // static to shut up compiler warning
|
||||
|
||||
if (timedrawing)
|
||||
drawtime = I_FloatTime ();
|
||||
|
||||
if (drawmode == dr_texture || drawmode == dr_flat)
|
||||
[self drawSolid];
|
||||
else
|
||||
[self drawWire: rects];
|
||||
|
||||
if (timedrawing)
|
||||
{
|
||||
//XXX NSPing ();
|
||||
drawtime = I_FloatTime() - drawtime;
|
||||
printf ("CameraView drawtime: %5.3f\n", drawtime);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
XYDrawSelf
|
||||
=============
|
||||
*/
|
||||
- XYDrawSelf
|
||||
{
|
||||
|
||||
PSsetrgbcolor (0,0,1.0);
|
||||
PSsetlinewidth (0.15);
|
||||
PSmoveto (origin[0]-16,origin[1]);
|
||||
PSrlineto (16,8);
|
||||
PSrlineto (16,-8);
|
||||
PSrlineto (-16,-8);
|
||||
PSrlineto (-16,8);
|
||||
PSrlineto (32,0);
|
||||
|
||||
PSmoveto (origin[0],origin[1]);
|
||||
PSrlineto (64*cos(ya+M_PI/4), 64*sin(ya+M_PI/4));
|
||||
PSmoveto (origin[0],origin[1]);
|
||||
PSrlineto (64*cos(ya-M_PI/4), 64*sin(ya-M_PI/4));
|
||||
|
||||
PSstroke ();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
ZDrawSelf
|
||||
=============
|
||||
*/
|
||||
- ZDrawSelf
|
||||
{
|
||||
PSsetrgbcolor (0,0,1.0);
|
||||
PSsetlinewidth (0.15);
|
||||
|
||||
PSmoveto (-16,origin[2]);
|
||||
PSrlineto (16,8);
|
||||
PSrlineto (16,-8);
|
||||
PSrlineto (-16,-8);
|
||||
PSrlineto (-16,8);
|
||||
PSrlineto (32,0);
|
||||
|
||||
PSmoveto (-15,origin[2]-47);
|
||||
PSrlineto (29,0);
|
||||
PSrlineto (0,54);
|
||||
PSrlineto (-29,0);
|
||||
PSrlineto (0,-54);
|
||||
|
||||
PSstroke ();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
XYZ mouse view methods
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
================
|
||||
modalMoveLoop
|
||||
================
|
||||
*/
|
||||
- modalMoveLoop: (NSPoint *)basept :(vec3_t)movemod : converter
|
||||
{
|
||||
vec3_t originbase;
|
||||
NSEvent *event = 0; //XXX
|
||||
NSPoint newpt;
|
||||
// NSPoint brushpt;
|
||||
vec3_t delta;
|
||||
// id ent;
|
||||
int i;
|
||||
// vec3_t temp;
|
||||
|
||||
qprintf ("moving camera position");
|
||||
|
||||
VectorCopy (origin, originbase);
|
||||
|
||||
//
|
||||
// modal event loop using instance drawing
|
||||
//
|
||||
goto drawentry;
|
||||
|
||||
while ([event type] != NSLeftMouseUp && [event type] != NSRightMouseUp)
|
||||
{
|
||||
//
|
||||
// calculate new point
|
||||
//
|
||||
newpt = [event locationInWindow];
|
||||
newpt = [converter convertPoint:newpt fromView:NULL];
|
||||
|
||||
delta[0] = newpt.x-basept->x;
|
||||
delta[1] = newpt.y-basept->y;
|
||||
delta[2] = delta[1]; // height change
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
origin[i] = originbase[i]+movemod[i]*delta[i];
|
||||
|
||||
#if 0 // FIXME
|
||||
//
|
||||
// if command is down, look towards brush or entity
|
||||
//
|
||||
if (event->flags & NS_SHIFTMASK)
|
||||
{
|
||||
ent = [quakemap_i selectedEntity];
|
||||
if (ent)
|
||||
{
|
||||
[ent origin: temp];
|
||||
brushpt.x = temp[0];
|
||||
brushpt.y = temp[1];
|
||||
}
|
||||
else
|
||||
brushpt = [brush_i centerPoint];
|
||||
ya = atan2 (brushpt.y - newpt.y, brushpt.x - newpt.x);
|
||||
[self matrixFromAngles];
|
||||
}
|
||||
#endif
|
||||
|
||||
drawentry:
|
||||
//
|
||||
// instance draw new frame
|
||||
//
|
||||
[quakeed_i newinstance];
|
||||
[self display];
|
||||
/*XXX
|
||||
event = [NSApp nextEventMatchingMask: NSLeftMouseUpMask
|
||||
| NSLeftMouseDraggedMask | NSRightMouseUpMask
|
||||
| NSRightMouseDraggedMask | NSApplicationDefinedMask];
|
||||
*/
|
||||
if ([event type] == NSKeyDown)
|
||||
{
|
||||
[self _keyDown: event];
|
||||
[self display];
|
||||
goto drawentry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
/*
|
||||
===============
|
||||
XYmouseDown
|
||||
===============
|
||||
*/
|
||||
- (BOOL)XYmouseDown: (NSPoint *)pt flags:(int)flags // return YES if brush handled
|
||||
{
|
||||
vec3_t movemod;
|
||||
|
||||
if (fabs(pt->x - origin[0]) > 16
|
||||
|| fabs(pt->y - origin[1]) > 16 )
|
||||
return NO;
|
||||
|
||||
#if 0
|
||||
if (flags & NSAlternateKeyMask)
|
||||
{ // up / down drag
|
||||
movemod[0] = 0;
|
||||
movemod[1] = 0;
|
||||
movemod[2] = 1;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
movemod[0] = 1;
|
||||
movemod[1] = 1;
|
||||
movemod[2] = 0;
|
||||
}
|
||||
|
||||
[self modalMoveLoop: pt : movemod : xyview_i];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
ZmouseDown
|
||||
===============
|
||||
*/
|
||||
- (BOOL)ZmouseDown: (NSPoint *)pt flags:(int)flags // return YES if brush handled
|
||||
{
|
||||
vec3_t movemod;
|
||||
|
||||
if (fabs(pt->y - origin[2]) > 16
|
||||
|| pt->x < -8 || pt->x > 8 )
|
||||
return NO;
|
||||
|
||||
movemod[0] = 0;
|
||||
movemod[1] = 0;
|
||||
movemod[2] = 1;
|
||||
|
||||
[self modalMoveLoop: pt : movemod : zview_i];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
===================
|
||||
viewDrag:
|
||||
===================
|
||||
*/
|
||||
- viewDrag:(NSPoint *)pt
|
||||
{
|
||||
float dx,dy;
|
||||
NSEvent *event = 0; //XXX
|
||||
NSPoint newpt;
|
||||
|
||||
//
|
||||
// modal event loop using instance drawing
|
||||
//
|
||||
goto drawentry;
|
||||
|
||||
while ([event type] != NSRightMouseUp)
|
||||
{
|
||||
//
|
||||
// calculate new point
|
||||
//
|
||||
newpt = [event locationInWindow];
|
||||
newpt = [self convertPoint:newpt fromView:NULL];
|
||||
|
||||
dx = newpt.x - pt->x;
|
||||
dy = newpt.y - pt->y;
|
||||
*pt = newpt;
|
||||
|
||||
ya -= dx/_bounds.size.width*M_PI/2 * 4;
|
||||
xa += dy/_bounds.size.width*M_PI/2 * 4;
|
||||
|
||||
[self matrixFromAngles];
|
||||
|
||||
drawentry:
|
||||
[quakeed_i newinstance];
|
||||
[self display];
|
||||
/*XXX
|
||||
event = [NSApp getNextEvent:
|
||||
NSKeyDownMask | NSRightMouseUpMask | NSRightMouseDraggedMask];
|
||||
*/
|
||||
if ([event type] == NSKeyDown)
|
||||
{
|
||||
[self _keyDown: event];
|
||||
[self display];
|
||||
goto drawentry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
===================
|
||||
mouseDown
|
||||
===================
|
||||
*/
|
||||
- (void) mouseDown:(NSEvent *)theEvent
|
||||
{
|
||||
NSPoint pt;
|
||||
int i;
|
||||
vec3_t p1, p2;
|
||||
float forward, right, up;
|
||||
int flags;
|
||||
|
||||
pt = [theEvent locationInWindow];
|
||||
|
||||
pt = [self convertPoint:pt fromView:NULL];
|
||||
|
||||
VectorCopy (origin, p1);
|
||||
forward = 160;
|
||||
right = pt.x - 160;
|
||||
up = pt.y - 240*2/3;
|
||||
for (i=0 ; i<3 ; i++)
|
||||
p2[i] = forward*matrix[2][i] + up*matrix[1][i] + right*matrix[0][i];
|
||||
for (i=0 ; i<3 ; i++)
|
||||
p2[i] = p1[i] + 100*p2[i];
|
||||
|
||||
flags = [theEvent modifierFlags] & (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask);
|
||||
|
||||
//
|
||||
// bare click to select a texture
|
||||
//
|
||||
if (flags == 0)
|
||||
{
|
||||
[map_i getTextureRay: p1 : p2];
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// shift click to select / deselect a brush from the world
|
||||
//
|
||||
if (flags == NSShiftKeyMask)
|
||||
{
|
||||
[map_i selectRay: p1 : p2 : NO];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// cmd-shift click to set a target/targetname entity connection
|
||||
//
|
||||
if (flags == (NSShiftKeyMask|NSCommandKeyMask) )
|
||||
{
|
||||
[map_i entityConnect: p1 : p2];
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// alt click = set entire brush texture
|
||||
//
|
||||
if (flags == NSAlternateKeyMask)
|
||||
{
|
||||
if (drawmode != dr_texture)
|
||||
{
|
||||
qprintf ("No texture setting except in texture mode!\n");
|
||||
NopSound ();
|
||||
return;
|
||||
}
|
||||
[map_i setTextureRay: p1 : p2 : YES];
|
||||
[quakeed_i updateAll];
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// ctrl-alt click = set single face texture
|
||||
//
|
||||
if (flags == (NSControlKeyMask | NSAlternateKeyMask) )
|
||||
{
|
||||
if (drawmode != dr_texture)
|
||||
{
|
||||
qprintf ("No texture setting except in texture mode!\n");
|
||||
NopSound ();
|
||||
return;
|
||||
}
|
||||
[map_i setTextureRay: p1 : p2 : NO];
|
||||
[quakeed_i updateAll];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
qprintf ("bad flags for click");
|
||||
NopSound ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
rightMouseDown
|
||||
===================
|
||||
*/
|
||||
-(void) rightMouseDown:(NSEvent *)theEvent
|
||||
{
|
||||
NSPoint pt;
|
||||
int flags;
|
||||
|
||||
pt = [theEvent locationInWindow];
|
||||
|
||||
[self convertPoint:pt fromView:NULL];
|
||||
|
||||
flags = [theEvent modifierFlags] & (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask);
|
||||
|
||||
//
|
||||
// click = drag camera
|
||||
//
|
||||
if (flags == 0)
|
||||
{
|
||||
qprintf ("looking");
|
||||
[self viewDrag: &pt];
|
||||
qprintf ("");
|
||||
return;
|
||||
}
|
||||
|
||||
qprintf ("bad flags for click");
|
||||
NopSound ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
keyDown
|
||||
===============
|
||||
*/
|
||||
|
||||
#define KEY_RIGHTARROW 0xae
|
||||
#define KEY_LEFTARROW 0xac
|
||||
#define KEY_UPARROW 0xad
|
||||
#define KEY_DOWNARROW 0xaf
|
||||
|
||||
|
||||
- _keyDown: (NSEvent *)theEvent
|
||||
{
|
||||
int ch;
|
||||
|
||||
ch = tolower([[theEvent characters] characterAtIndex: 0]);
|
||||
|
||||
switch (ch)
|
||||
{
|
||||
case 13:
|
||||
return self;
|
||||
|
||||
case 'a':
|
||||
case 'A':
|
||||
xa += M_PI/8;
|
||||
[self matrixFromAngles];
|
||||
[quakeed_i updateCamera];
|
||||
return self;
|
||||
|
||||
case 'z':
|
||||
case 'Z':
|
||||
xa -= M_PI/8;
|
||||
[self matrixFromAngles];
|
||||
[quakeed_i updateCamera];
|
||||
return self;
|
||||
|
||||
case KEY_RIGHTARROW:
|
||||
ya -= M_PI*move/(64*2);
|
||||
[self matrixFromAngles];
|
||||
[quakeed_i updateCamera];
|
||||
break;
|
||||
|
||||
case KEY_LEFTARROW:
|
||||
ya += M_PI*move/(64*2);
|
||||
[self matrixFromAngles];
|
||||
[quakeed_i updateCamera];
|
||||
break;
|
||||
|
||||
case KEY_UPARROW:
|
||||
origin[0] += move*cos(ya);
|
||||
origin[1] += move*sin(ya);
|
||||
[quakeed_i updateCamera];
|
||||
break;
|
||||
|
||||
case KEY_DOWNARROW:
|
||||
origin[0] -= move*cos(ya);
|
||||
origin[1] -= move*sin(ya);
|
||||
[quakeed_i updateCamera];
|
||||
break;
|
||||
|
||||
case '.':
|
||||
origin[0] += move*cos(ya-M_PI_2);
|
||||
origin[1] += move*sin(ya-M_PI_2);
|
||||
[quakeed_i updateCamera];
|
||||
break;
|
||||
|
||||
case ',':
|
||||
origin[0] -= move*cos(ya-M_PI_2);
|
||||
origin[1] -= move*sin(ya-M_PI_2);
|
||||
[quakeed_i updateCamera];
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
case 'D':
|
||||
origin[2] += move;
|
||||
[quakeed_i updateCamera];
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
case 'C':
|
||||
origin[2] -= move;
|
||||
[quakeed_i updateCamera];
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
24
tools/Forge/Bundles/MapEdit/Clipper.h
Normal file
24
tools/Forge/Bundles/MapEdit/Clipper.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
|
||||
extern id clipper_i;
|
||||
|
||||
@interface Clipper : Object
|
||||
{
|
||||
int num;
|
||||
vec3_t pos[3];
|
||||
plane_t plane;
|
||||
}
|
||||
|
||||
- (BOOL)hide;
|
||||
- XYClick: (NSPoint)pt;
|
||||
- (BOOL)XYDrag: (NSPoint *)pt;
|
||||
- ZClick: (NSPoint)pt;
|
||||
- carve;
|
||||
- flipNormal;
|
||||
- (BOOL)getFace: (face_t *)pl;
|
||||
|
||||
- cameraDrawSelf;
|
||||
- XYDrawSelf;
|
||||
- ZDrawSelf;
|
||||
|
||||
@end
|
||||
|
230
tools/Forge/Bundles/MapEdit/Clipper.m
Normal file
230
tools/Forge/Bundles/MapEdit/Clipper.m
Normal file
|
@ -0,0 +1,230 @@
|
|||
|
||||
#include "qedefs.h"
|
||||
|
||||
#import <AppKit/NSGraphics.h>
|
||||
#import <AppKit/DPSOperators.h>
|
||||
|
||||
id clipper_i;
|
||||
|
||||
@implementation Clipper
|
||||
|
||||
- init
|
||||
{
|
||||
[super init];
|
||||
clipper_i = self;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)hide
|
||||
{
|
||||
int oldnum;
|
||||
|
||||
oldnum = num;
|
||||
num = 0;
|
||||
return (oldnum > 0);
|
||||
}
|
||||
|
||||
- flipNormal
|
||||
{
|
||||
vec3_t temp;
|
||||
|
||||
if (num == 2)
|
||||
{
|
||||
VectorCopy (pos[0], temp);
|
||||
VectorCopy (pos[1], pos[0]);
|
||||
VectorCopy (temp, pos[1]);
|
||||
}
|
||||
else if (num == 3)
|
||||
{
|
||||
VectorCopy (pos[0], temp);
|
||||
VectorCopy (pos[2], pos[0]);
|
||||
VectorCopy (temp, pos[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
qprintf ("no clipplane");
|
||||
NSBeep ();
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)getFace: (face_t *)f
|
||||
{
|
||||
vec3_t v1, v2, norm;
|
||||
int i;
|
||||
|
||||
VectorCopy (vec3_origin, plane.normal);
|
||||
plane.dist = 0;
|
||||
if (num < 2)
|
||||
return NO;
|
||||
if (num == 2)
|
||||
{
|
||||
VectorCopy (pos[0], pos[2]);
|
||||
pos[2][2] += 16;
|
||||
}
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
VectorCopy (pos[i], f->planepts[i]);
|
||||
|
||||
VectorSubtract (pos[2], pos[0], v1);
|
||||
VectorSubtract (pos[1], pos[0], v2);
|
||||
|
||||
CrossProduct (v1, v2, norm);
|
||||
VectorNormalize (norm);
|
||||
|
||||
if ( !norm[0] && !norm[1] && !norm[2] )
|
||||
return NO;
|
||||
|
||||
[texturepalette_i getTextureDef: &f->texture];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
XYClick
|
||||
================
|
||||
*/
|
||||
- XYClick: (NSPoint)pt
|
||||
{
|
||||
int i;
|
||||
vec3_t new;
|
||||
|
||||
new[0] = [xyview_i snapToGrid: pt.x];
|
||||
new[1] = [xyview_i snapToGrid: pt.y];
|
||||
new[2] = [map_i currentMinZ];
|
||||
|
||||
// see if a point is allready there
|
||||
for (i=0 ; i<num ; i++)
|
||||
{
|
||||
if (new[0] == pos[i][0] && new[1] == pos[i][1])
|
||||
{
|
||||
if (pos[i][2] == [map_i currentMinZ])
|
||||
pos[i][2] = [map_i currentMaxZ];
|
||||
else
|
||||
pos[i][2] = [map_i currentMinZ];
|
||||
[quakeed_i updateAll];
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (num == 3)
|
||||
num = 0;
|
||||
|
||||
VectorCopy (new, pos[num]);
|
||||
num++;
|
||||
|
||||
[quakeed_i updateAll];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
XYDrag
|
||||
================
|
||||
*/
|
||||
- (BOOL)XYDrag: (NSPoint *)pt
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
if (fabs(pt->x - pos[i][0] > 10) || fabs(pt->y - pos[i][1] > 10) )
|
||||
continue;
|
||||
// drag this point
|
||||
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- ZClick: (NSPoint)pt
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
- carve
|
||||
{
|
||||
[map_i makeSelectedPerform: @selector(carveByClipper)];
|
||||
num = 0;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- cameraDrawSelf
|
||||
{
|
||||
vec3_t mid;
|
||||
int i;
|
||||
|
||||
linecolor (1,0.5,0);
|
||||
|
||||
for (i=0 ; i<num ; i++)
|
||||
{
|
||||
VectorCopy (pos[i], mid);
|
||||
mid[0] -= 8;
|
||||
mid[1] -= 8;
|
||||
CameraMoveto (mid);
|
||||
mid[0] += 16;
|
||||
mid[1] += 16;
|
||||
CameraLineto (mid);
|
||||
|
||||
VectorCopy (pos[i], mid);
|
||||
mid[0] -= 8;
|
||||
mid[1] += 8;
|
||||
CameraMoveto (mid);
|
||||
mid[0] += 16;
|
||||
mid[1] -= 16;
|
||||
CameraLineto (mid);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- XYDrawSelf
|
||||
{
|
||||
int i;
|
||||
char text[8];
|
||||
|
||||
PSsetrgbcolor (1,0.5,0);
|
||||
//XXX PSselectfont("Helvetica-Medium",10/[xyview_i currentScale]);
|
||||
PSrotate(0);
|
||||
|
||||
for (i=0 ; i<num ; i++)
|
||||
{
|
||||
PSmoveto (pos[i][0]-4, pos[i][1]-4);
|
||||
sprintf (text, "%i", i);
|
||||
PSshow (text);
|
||||
PSstroke ();
|
||||
PSarc ( pos[i][0], pos[i][1], 10, 0, 360);
|
||||
PSstroke ();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- ZDrawSelf
|
||||
{
|
||||
int i;
|
||||
char text[8];
|
||||
|
||||
PSsetrgbcolor (1,0.5,0);
|
||||
//XXX PSselectfont("Helvetica-Medium",10/[zview_i currentScale]);
|
||||
PSrotate(0);
|
||||
|
||||
for (i=0 ; i<num ; i++)
|
||||
{
|
||||
PSmoveto (-28+i*8 - 4, pos[i][2]-4);
|
||||
sprintf (text, "%i", i);
|
||||
PSshow (text);
|
||||
PSstroke ();
|
||||
PSarc ( -28+i*8, pos[i][2], 10, 0, 360);
|
||||
PSstroke ();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
47
tools/Forge/Bundles/MapEdit/Dict.h
Normal file
47
tools/Forge/Bundles/MapEdit/Dict.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *key;
|
||||
char *value;
|
||||
} dict_t;
|
||||
|
||||
@interface Dict:Storage
|
||||
{
|
||||
}
|
||||
|
||||
- initFromFile:(FILE *)fp;
|
||||
|
||||
- (id) parseMultipleFrom:(char *)value;
|
||||
- (int) getValueUnits:(char *)key;
|
||||
- delString:(char *)string fromValue:(char *)key;
|
||||
- addString:(char *)string toValue:(char *)key;
|
||||
- (char *)convertListToString:(id)list;
|
||||
- (char *)getStringFor:(char *)name;
|
||||
- removeKeyword:(char *)key;
|
||||
- (unsigned int)getValueFor:(char *)name;
|
||||
- changeStringFor:(char *)key to:(char *)value;
|
||||
- (dict_t *) findKeyword:(char *)key;
|
||||
|
||||
- writeBlockTo:(FILE *)fp;
|
||||
- writeFile:(char *)path;
|
||||
|
||||
// INTERNAL
|
||||
- init;
|
||||
- (id) parseBraceBlock:(FILE *)fp;
|
||||
- setupMultiple:(char *)value;
|
||||
- (char *)getNextParameter;
|
||||
|
||||
@end
|
||||
|
||||
int GetNextChar(FILE *fp);
|
||||
void CopyUntilWhitespc(FILE *fp,char *buffer);
|
||||
void CopyUntilQuote(FILE *fp,char *buffer);
|
||||
int FindBrace(FILE *fp);
|
||||
int FindQuote(FILE *fp);
|
||||
int FindWhitespc(FILE *fp);
|
||||
int FindNonwhitespc(FILE *fp);
|
||||
|
||||
char *FindWhitespcInBuffer(char *buffer);
|
||||
char *FindNonwhitespcInBuffer(char *buffer);
|
583
tools/Forge/Bundles/MapEdit/Dict.m
Normal file
583
tools/Forge/Bundles/MapEdit/Dict.m
Normal file
|
@ -0,0 +1,583 @@
|
|||
|
||||
#import "qedefs.h"
|
||||
|
||||
@implementation Dict
|
||||
|
||||
- init
|
||||
{
|
||||
[super initCount:0
|
||||
elementSize:sizeof(dict_t)
|
||||
description:NULL];
|
||||
return self;
|
||||
}
|
||||
|
||||
- print
|
||||
{
|
||||
int i;
|
||||
dict_t *d;
|
||||
|
||||
for (i=0 ; i<numElements ; i++)
|
||||
{
|
||||
d = [self elementAt: i];
|
||||
printf ("%s : %s\n",d->key, d->value);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
copyFromZone
|
||||
|
||||
JDC
|
||||
===========
|
||||
*/
|
||||
- copyFromZone:(NSZone *)zone
|
||||
{
|
||||
id new;
|
||||
int i;
|
||||
dict_t *d;
|
||||
char *old;
|
||||
|
||||
new = [super copyFromZone: zone];
|
||||
for (i=0 ; i<numElements ; i++)
|
||||
{
|
||||
d = [self elementAt: i];
|
||||
old = d->key;
|
||||
d->key = malloc(strlen(old)+1);
|
||||
strcpy (d->key, old);
|
||||
|
||||
old = d->value;
|
||||
d->value = malloc(strlen(old)+1);
|
||||
strcpy (d->value, old);
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
- initFromFile:(FILE *)fp
|
||||
{
|
||||
[self init];
|
||||
return [self parseBraceBlock:fp];
|
||||
}
|
||||
|
||||
//===============================================
|
||||
//
|
||||
// Dictionary pair functions
|
||||
//
|
||||
//===============================================
|
||||
|
||||
//
|
||||
// Write a { } block out to a FILE*
|
||||
//
|
||||
- writeBlockTo:(FILE *)fp
|
||||
{
|
||||
int max;
|
||||
int i;
|
||||
dict_t *d;
|
||||
|
||||
fprintf(fp,"{\n");
|
||||
max = [super count];
|
||||
for (i = 0;i < max;i++)
|
||||
{
|
||||
d = [super elementAt:i];
|
||||
fprintf(fp,"\t{\"%s\"\t\"%s\"}\n",d->key,d->value);
|
||||
}
|
||||
fprintf(fp,"}\n");
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Write a single { } block out
|
||||
//
|
||||
- writeFile:(char *)path
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(path,"w+t");
|
||||
if (fp != NULL)
|
||||
{
|
||||
printf("Writing dictionary file %s.\n",path);
|
||||
fprintf(fp,"// QE_Project file %s\n",path);
|
||||
[self writeBlockTo:fp];
|
||||
fclose(fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error writing %s!\n",path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//===============================================
|
||||
//
|
||||
// Utility methods
|
||||
//
|
||||
//===============================================
|
||||
|
||||
//
|
||||
// Find a keyword in storage
|
||||
// Returns * to dict_t, otherwise NULL
|
||||
//
|
||||
- (dict_t *) findKeyword:(char *)key
|
||||
{
|
||||
int max;
|
||||
int i;
|
||||
dict_t *d;
|
||||
|
||||
max = [super count];
|
||||
for (i = 0;i < max;i++)
|
||||
{
|
||||
d = [super elementAt:i];
|
||||
if (!strcmp(d->key,key))
|
||||
return d;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// Change a keyword's string
|
||||
//
|
||||
- changeStringFor:(char *)key to:(char *)value
|
||||
{
|
||||
dict_t *d;
|
||||
dict_t newd;
|
||||
|
||||
d = [self findKeyword:key];
|
||||
if (d != NULL)
|
||||
{
|
||||
free(d->value);
|
||||
d->value = malloc(strlen(value)+1);
|
||||
strcpy(d->value,value);
|
||||
}
|
||||
else
|
||||
{
|
||||
newd.key = malloc(strlen(key)+1);
|
||||
strcpy(newd.key,key);
|
||||
newd.value = malloc(strlen(value)+1);
|
||||
strcpy(newd.value,value);
|
||||
[self addElement:&newd];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Search for keyword, return the string *
|
||||
//
|
||||
- (char *)getStringFor:(char *)name
|
||||
{
|
||||
dict_t *d;
|
||||
|
||||
d = [self findKeyword:name];
|
||||
if (d != NULL)
|
||||
return d->value;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
//
|
||||
// Search for keyword, return the value
|
||||
//
|
||||
- (unsigned int)getValueFor:(char *)name
|
||||
{
|
||||
dict_t *d;
|
||||
|
||||
d = [self findKeyword:name];
|
||||
if (d != NULL)
|
||||
return atol(d->value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Return # of units in keyword's value
|
||||
//
|
||||
- (int) getValueUnits:(char *)key
|
||||
{
|
||||
id temp;
|
||||
int count;
|
||||
|
||||
temp = [self parseMultipleFrom:key];
|
||||
count = [temp count];
|
||||
[temp free];
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
//
|
||||
// Convert List to string
|
||||
//
|
||||
- (char *)convertListToString:(id)list
|
||||
{
|
||||
int i;
|
||||
int max;
|
||||
char tempstr[4096];
|
||||
char *s;
|
||||
char *newstr;
|
||||
|
||||
max = [list count];
|
||||
tempstr[0] = 0;
|
||||
for (i = 0;i < max;i++)
|
||||
{
|
||||
s = [list elementAt:i];
|
||||
strcat(tempstr,s);
|
||||
strcat(tempstr," ");
|
||||
}
|
||||
newstr = malloc(strlen(tempstr)+1);
|
||||
strcpy(newstr,tempstr);
|
||||
|
||||
return newstr;
|
||||
}
|
||||
|
||||
//
|
||||
// JDC: I wrote this to simplify removing vectors
|
||||
//
|
||||
- removeKeyword:(char *)key
|
||||
{
|
||||
dict_t *d;
|
||||
|
||||
d = [self findKeyword:key];
|
||||
if (d == NULL)
|
||||
return self;
|
||||
[self removeElementAt:d - (dict_t*)dataPtr];
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Delete string from keyword's value
|
||||
//
|
||||
- delString:(char *)string fromValue:(char *)key
|
||||
{
|
||||
id temp;
|
||||
int count;
|
||||
int i;
|
||||
char *s;
|
||||
dict_t *d;
|
||||
|
||||
d = [self findKeyword:key];
|
||||
if (d == NULL)
|
||||
return NULL;
|
||||
temp = [self parseMultipleFrom:key];
|
||||
count = [temp count];
|
||||
for (i = 0;i < count;i++)
|
||||
{
|
||||
s = [temp elementAt:i];
|
||||
if (!strcmp(s,string))
|
||||
{
|
||||
[temp removeElementAt:i];
|
||||
free(d->value);
|
||||
d->value = [self convertListToString:temp];
|
||||
[temp free];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Add string to keyword's value
|
||||
//
|
||||
- addString:(char *)string toValue:(char *)key
|
||||
{
|
||||
char *newstr;
|
||||
char spacing[] = "\t";
|
||||
dict_t *d;
|
||||
|
||||
d = [self findKeyword:key];
|
||||
if (d == NULL)
|
||||
return NULL;
|
||||
newstr = malloc(strlen(string) + strlen(d->value) + strlen(spacing) + 1);
|
||||
strcpy(newstr,d->value);
|
||||
strcat(newstr,spacing);
|
||||
strcat(newstr,string);
|
||||
free(d->value);
|
||||
d->value = newstr;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//===============================================
|
||||
//
|
||||
// Use these for multiple parameters in a keyword value
|
||||
//
|
||||
//===============================================
|
||||
char *searchStr;
|
||||
char item[4096];
|
||||
|
||||
- setupMultiple:(char *)value
|
||||
{
|
||||
searchStr = value;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (char *)getNextParameter
|
||||
{
|
||||
char *s;
|
||||
|
||||
if (!searchStr)
|
||||
return NULL;
|
||||
strcpy(item,searchStr);
|
||||
s = FindWhitespcInBuffer(item);
|
||||
if (!*s)
|
||||
searchStr = NULL;
|
||||
else
|
||||
{
|
||||
*s = 0;
|
||||
searchStr = FindNonwhitespcInBuffer(s+1);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
//
|
||||
// Parses a keyvalue string & returns a Storage full of those items
|
||||
//
|
||||
- (id) parseMultipleFrom:(char *)key
|
||||
{
|
||||
#define ITEMSIZE 128
|
||||
id stuff;
|
||||
char string[ITEMSIZE];
|
||||
char *s;
|
||||
|
||||
s = [self getStringFor:key];
|
||||
if (s == NULL)
|
||||
return NULL;
|
||||
|
||||
stuff = [[Storage alloc]
|
||||
initCount:0
|
||||
elementSize:ITEMSIZE
|
||||
description:NULL];
|
||||
|
||||
[self setupMultiple:s];
|
||||
while((s = [self getNextParameter]))
|
||||
{
|
||||
bzero(string,ITEMSIZE);
|
||||
strcpy(string,s);
|
||||
[stuff addElement:string];
|
||||
}
|
||||
|
||||
return stuff;
|
||||
}
|
||||
|
||||
//===============================================
|
||||
//
|
||||
// Dictionary pair parsing
|
||||
//
|
||||
//===============================================
|
||||
|
||||
//
|
||||
// parse all keyword/value pairs within { } 's
|
||||
//
|
||||
- (id) parseBraceBlock:(FILE *)fp
|
||||
{
|
||||
int c;
|
||||
dict_t pair;
|
||||
char string[1024];
|
||||
|
||||
c = FindBrace(fp);
|
||||
if (c == -1)
|
||||
return NULL;
|
||||
|
||||
while((c = FindBrace(fp)) != '}')
|
||||
{
|
||||
if (c == -1)
|
||||
return NULL;
|
||||
// c = FindNonwhitespc(fp);
|
||||
// if (c == -1)
|
||||
// return NULL;
|
||||
// CopyUntilWhitespc(fp,string);
|
||||
|
||||
// JDC: fixed to allow quoted keys
|
||||
c = FindNonwhitespc(fp);
|
||||
if (c == -1)
|
||||
return NULL;
|
||||
c = fgetc(fp);
|
||||
if ( c == '\"')
|
||||
CopyUntilQuote(fp,string);
|
||||
else
|
||||
{
|
||||
ungetc (c,fp);
|
||||
CopyUntilWhitespc(fp,string);
|
||||
}
|
||||
|
||||
pair.key = malloc(strlen(string)+1);
|
||||
strcpy(pair.key,string);
|
||||
|
||||
c = FindQuote(fp);
|
||||
CopyUntilQuote(fp,string);
|
||||
pair.value = malloc(strlen(string)+1);
|
||||
strcpy(pair.value,string);
|
||||
|
||||
[super addElement:&pair];
|
||||
c = FindBrace(fp);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//===============================================
|
||||
//
|
||||
// C routines for string parsing
|
||||
//
|
||||
//===============================================
|
||||
int GetNextChar(FILE *fp)
|
||||
{
|
||||
int c;
|
||||
int c2;
|
||||
|
||||
c = getc(fp);
|
||||
if (c == EOF)
|
||||
return -1;
|
||||
if (c == '/') // parse comments
|
||||
{
|
||||
c2 = getc(fp);
|
||||
if (c2 == '/')
|
||||
{
|
||||
while((c2 = getc(fp)) != '\n');
|
||||
c = getc(fp);
|
||||
}
|
||||
else
|
||||
ungetc(c2,fp);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void CopyUntilWhitespc(FILE *fp,char *buffer)
|
||||
{
|
||||
int count = 800;
|
||||
int c;
|
||||
|
||||
while(count--)
|
||||
{
|
||||
c = GetNextChar(fp);
|
||||
if (c == EOF)
|
||||
return;
|
||||
if (c <= ' ')
|
||||
{
|
||||
*buffer = 0;
|
||||
return;
|
||||
}
|
||||
*buffer++ = c;
|
||||
}
|
||||
}
|
||||
|
||||
void CopyUntilQuote(FILE *fp,char *buffer)
|
||||
{
|
||||
int count = 800;
|
||||
int c;
|
||||
|
||||
while(count--)
|
||||
{
|
||||
c = GetNextChar(fp);
|
||||
if (c == EOF)
|
||||
return;
|
||||
if (c == '\"')
|
||||
{
|
||||
*buffer = 0;
|
||||
return;
|
||||
}
|
||||
*buffer++ = c;
|
||||
}
|
||||
}
|
||||
|
||||
int FindBrace(FILE *fp)
|
||||
{
|
||||
int count = 800;
|
||||
int c;
|
||||
|
||||
while(count--)
|
||||
{
|
||||
c = GetNextChar(fp);
|
||||
if (c == EOF)
|
||||
return -1;
|
||||
if (c == '{' ||
|
||||
c == '}')
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int FindQuote(FILE *fp)
|
||||
{
|
||||
int count = 800;
|
||||
int c;
|
||||
|
||||
while(count--)
|
||||
{
|
||||
c = GetNextChar(fp);
|
||||
if (c == EOF)
|
||||
return -1;
|
||||
if (c == '\"')
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int FindWhitespc(FILE *fp)
|
||||
{
|
||||
int count = 800;
|
||||
int c;
|
||||
|
||||
while(count--)
|
||||
{
|
||||
c = GetNextChar(fp);
|
||||
if (c == EOF)
|
||||
return -1;
|
||||
if (c <= ' ')
|
||||
{
|
||||
ungetc(c,fp);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int FindNonwhitespc(FILE *fp)
|
||||
{
|
||||
int count = 800;
|
||||
int c;
|
||||
|
||||
while(count--)
|
||||
{
|
||||
c = GetNextChar(fp);
|
||||
if (c == EOF)
|
||||
return -1;
|
||||
if (c > ' ')
|
||||
{
|
||||
ungetc(c,fp);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *FindWhitespcInBuffer(char *buffer)
|
||||
{
|
||||
int count = 1000;
|
||||
char *b = buffer;
|
||||
|
||||
while(count--)
|
||||
if (*b <= ' ')
|
||||
return b;
|
||||
else
|
||||
b++;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *FindNonwhitespcInBuffer(char *buffer)
|
||||
{
|
||||
int count = 1000;
|
||||
char *b = buffer;
|
||||
|
||||
while(count--)
|
||||
if (*b > ' ')
|
||||
return b;
|
||||
else
|
||||
b++;
|
||||
return NULL;
|
||||
}
|
12
tools/Forge/Bundles/MapEdit/DictList.h
Normal file
12
tools/Forge/Bundles/MapEdit/DictList.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@interface DictList:List
|
||||
{
|
||||
}
|
||||
|
||||
- initListFromFile:(FILE *)fp;
|
||||
- writeListFile:(char *)filename;
|
||||
- (id) findDictKeyword:(char *)key;
|
||||
|
||||
@end
|
69
tools/Forge/Bundles/MapEdit/DictList.m
Normal file
69
tools/Forge/Bundles/MapEdit/DictList.m
Normal file
|
@ -0,0 +1,69 @@
|
|||
|
||||
#import "qedefs.h"
|
||||
|
||||
@implementation DictList
|
||||
|
||||
//
|
||||
// Read in variable # of objects from FILE *
|
||||
//
|
||||
- initListFromFile:(FILE *)fp
|
||||
{
|
||||
id d;
|
||||
|
||||
[super init];
|
||||
do
|
||||
{
|
||||
d = [(Dict *)[Dict alloc] initFromFile:fp];
|
||||
if (d != NULL)
|
||||
[self addObject:d];
|
||||
} while(d != NULL);
|
||||
[d free];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Write out list file
|
||||
//
|
||||
- writeListFile:(char *)filename
|
||||
{
|
||||
FILE *fp;
|
||||
int i;
|
||||
id obj;
|
||||
|
||||
fp = fopen(filename,"w+t");
|
||||
if (fp == NULL)
|
||||
return NULL;
|
||||
|
||||
fprintf(fp,"// Object List written by QuakeEd\n");
|
||||
|
||||
for (i = 0;i < maxElements;i++)
|
||||
{
|
||||
obj = [self objectAt:i];
|
||||
[obj writeBlockTo:fp];
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Find the keyword in all the Dict objects
|
||||
//
|
||||
- (id) findDictKeyword:(char *)key
|
||||
{
|
||||
int i;
|
||||
dict_t *d;
|
||||
id dict;
|
||||
|
||||
for (i = 0;i < maxElements;i++)
|
||||
{
|
||||
dict = [self objectAt:i];
|
||||
d = [(Dict *)dict findKeyword:key];
|
||||
if (d != NULL)
|
||||
return dict;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@end
|
40
tools/Forge/Bundles/MapEdit/Entity.h
Normal file
40
tools/Forge/Bundles/MapEdit/Entity.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
|
||||
#define MAX_KEY 64
|
||||
#define MAX_VALUE 128
|
||||
typedef struct epair_s
|
||||
{
|
||||
struct epair_s *next;
|
||||
char key[MAX_KEY];
|
||||
char value[MAX_VALUE];
|
||||
} epair_t;
|
||||
|
||||
// an Entity is a list of brush objects, with additional key / value info
|
||||
|
||||
@interface Entity : NSObject <NSCopying, NSMutableCopying>
|
||||
{
|
||||
epair_t *epairs;
|
||||
BOOL modifiable;
|
||||
}
|
||||
|
||||
- initClass: (char *)classname;
|
||||
- initFromTokens;
|
||||
|
||||
- free;
|
||||
|
||||
- (BOOL)modifiable;
|
||||
- setModifiable: (BOOL)m;
|
||||
|
||||
- (char *)targetname;
|
||||
|
||||
- writeToFILE: (FILE *)f region:(BOOL)reg;
|
||||
|
||||
- (char *)valueForQKey: (char *)k;
|
||||
- getVector: (vec3_t)v forKey: (char *)k;
|
||||
- setKey:(char *)k toValue:(char *)v;
|
||||
- (int)numPairs;
|
||||
- (epair_t *)epairs;
|
||||
- removeKeyPair: (char *)key;
|
||||
|
||||
@end
|
||||
|
||||
|
485
tools/Forge/Bundles/MapEdit/Entity.m
Normal file
485
tools/Forge/Bundles/MapEdit/Entity.m
Normal file
|
@ -0,0 +1,485 @@
|
|||
|
||||
#include "qedefs.h"
|
||||
|
||||
@implementation Entity
|
||||
|
||||
vec3_t bad_mins = {-8, -8, -8};
|
||||
vec3_t bad_maxs = {8, 8, 8};
|
||||
|
||||
- createFixedBrush: (vec3_t)org
|
||||
{
|
||||
vec3_t emins, emaxs;
|
||||
float *v, *v2, *color;
|
||||
id new;
|
||||
texturedef_t td;
|
||||
|
||||
// get class
|
||||
new = [entity_classes_i classForName: [self valueForQKey: "classname"]];
|
||||
if (new)
|
||||
{
|
||||
v = [new mins];
|
||||
v2 = [new maxs];
|
||||
}
|
||||
else
|
||||
{
|
||||
v = bad_mins;
|
||||
v2 = bad_maxs;
|
||||
}
|
||||
|
||||
color = [new drawColor];
|
||||
|
||||
modifiable = NO;
|
||||
memset(&td,0,sizeof(td));
|
||||
strcpy (td.texture,"entity");
|
||||
|
||||
VectorAdd (org, v, emins);
|
||||
VectorAdd (org, v2, emaxs);
|
||||
new = [[SetBrush alloc] initOwner: self mins:emins maxs:emaxs
|
||||
texture: &td];
|
||||
[new setEntityColor: color];
|
||||
|
||||
[self addObject: new];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- copyWithZone:(NSZone *)zone
|
||||
{
|
||||
id new, nb;
|
||||
epair_t *e;
|
||||
int i;
|
||||
|
||||
new = [[Entity alloc] init];
|
||||
[new setModifiable: modifiable];
|
||||
|
||||
for (e=epairs ; e ; e=e->next)
|
||||
{ // don't copy target and targetname fields
|
||||
if (strncmp(e->key,"target",6))
|
||||
[new setKey: e->key toValue: e->value];
|
||||
}
|
||||
|
||||
for (i=0 ; i<numElements ; i++)
|
||||
{
|
||||
nb = [[self objectAt: i] copy];
|
||||
[nb setParent: new];
|
||||
[new addObject: nb];
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
- initClass: (char *)classname
|
||||
{
|
||||
id new;
|
||||
esize_t esize;
|
||||
char value[80];
|
||||
vec3_t min, max;
|
||||
float *v;
|
||||
|
||||
[super init];
|
||||
|
||||
modifiable = YES;
|
||||
|
||||
[self setKey: "classname" toValue:classname];
|
||||
|
||||
// get class
|
||||
new = [entity_classes_i classForName: [self valueForQKey: "classname"]];
|
||||
if (!new)
|
||||
esize = esize_model;
|
||||
else
|
||||
esize = [new esize];
|
||||
|
||||
// create a brush if needed
|
||||
if (esize == esize_fixed)
|
||||
{
|
||||
v = [new mins];
|
||||
[[map_i selectedBrush] getMins: min maxs: max];
|
||||
VectorSubtract (min, v, min);
|
||||
|
||||
sprintf (value, "%i %i %i",(int)min[0], (int)min[1], (int)min[2]);
|
||||
[self setKey:"origin" toValue: value];
|
||||
|
||||
[self createFixedBrush: min];
|
||||
}
|
||||
else
|
||||
modifiable = YES;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- free
|
||||
{
|
||||
epair_t *e, *n;
|
||||
|
||||
for (e=epairs ; e ; e=n)
|
||||
{
|
||||
n = e->next;
|
||||
free (e);
|
||||
}
|
||||
return [super free];
|
||||
}
|
||||
|
||||
- (BOOL)modifiable
|
||||
{
|
||||
return modifiable;
|
||||
}
|
||||
|
||||
- setModifiable: (BOOL)m
|
||||
{
|
||||
modifiable = m;
|
||||
return self;
|
||||
}
|
||||
|
||||
- removeObject: o
|
||||
{
|
||||
o = [super removeObject: o];
|
||||
if (numElements)
|
||||
return o;
|
||||
// the entity is empty, so remove the entire thing
|
||||
if ( self == [map_i objectAt: 0])
|
||||
return o; // never remove the world
|
||||
|
||||
[map_i removeObject: self];
|
||||
[self free];
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
- (char *)valueForQKey: (char *)k
|
||||
{
|
||||
epair_t *e;
|
||||
static char ret[64];
|
||||
|
||||
for (e=epairs ; e ; e=e->next)
|
||||
if (!strcmp(k,e->key))
|
||||
{
|
||||
strcpy (ret, e->value);
|
||||
return ret;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
- getVector: (vec3_t)v forKey: (char *)k
|
||||
{
|
||||
char *c;
|
||||
|
||||
c = [self valueForQKey: k];
|
||||
|
||||
v[0] = v[1] = v[2] = 0;
|
||||
|
||||
sscanf (c, "%f %f %f", &v[0], &v[1], &v[2]);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- print
|
||||
{
|
||||
epair_t *e;
|
||||
|
||||
for (e=epairs ; e ; e=e->next)
|
||||
printf ("%20s : %20s\n",e->key, e->value);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- setKey:(char *)k toValue:(char *)v
|
||||
{
|
||||
epair_t *e;
|
||||
|
||||
if (strlen(k) > MAX_KEY)
|
||||
Error ("setKey: %s > MAX_KEY", k);
|
||||
if (strlen(v) > MAX_VALUE)
|
||||
Error ("setKey: %s > MAX_VALUE", v);
|
||||
|
||||
while (*k && *k <= ' ')
|
||||
k++;
|
||||
if (!*k)
|
||||
return self; // don't set NULL values
|
||||
|
||||
for (e=epairs ; e ; e=e->next)
|
||||
if (!strcmp(k,e->key))
|
||||
{
|
||||
memset (e->value, 0, sizeof(e->value));
|
||||
strcpy (e->value, v);
|
||||
return self;
|
||||
}
|
||||
|
||||
e = malloc (sizeof(epair_t));
|
||||
memset (e, 0, sizeof(epair_t));
|
||||
|
||||
strcpy (e->key, k);
|
||||
strcpy (e->value, v);
|
||||
e->next = epairs;
|
||||
epairs = e;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (int)numPairs
|
||||
{
|
||||
int i;
|
||||
epair_t *e;
|
||||
|
||||
i=0;
|
||||
for (e=epairs ; e ; e=e->next)
|
||||
i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
- (epair_t *)epairs
|
||||
{
|
||||
return epairs;
|
||||
}
|
||||
|
||||
- removeKeyPair: (char *)key
|
||||
{
|
||||
epair_t *e, *e2;
|
||||
|
||||
if (!epairs)
|
||||
return self;
|
||||
e = epairs;
|
||||
if (!strcmp(e->key, key))
|
||||
{
|
||||
epairs = e->next;
|
||||
free (e);
|
||||
return self;
|
||||
}
|
||||
|
||||
for (; e ; e=e->next)
|
||||
{
|
||||
if (e->next && !strcmp(e->next->key, key))
|
||||
{
|
||||
e2 = e->next;
|
||||
e->next = e2->next;
|
||||
free (e2);
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
printf ("WARNING: removeKeyPair: %s not found\n", key);
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
targetname
|
||||
|
||||
If the entity does not have a "targetname" key, a unique one is generated
|
||||
=============
|
||||
*/
|
||||
- (char *)targetname
|
||||
{
|
||||
char *t;
|
||||
int i, count;
|
||||
id ent;
|
||||
int tval, maxt;
|
||||
char name[20];
|
||||
|
||||
t = [self valueForQKey: "targetname"];
|
||||
if (t && t[0])
|
||||
return t;
|
||||
|
||||
// make a unique name of the form t<number>
|
||||
count = [map_i count];
|
||||
maxt = 0;
|
||||
for (i=1 ; i<count ; i++)
|
||||
{
|
||||
ent = [map_i objectAt: i];
|
||||
t = [ent valueForQKey: "targetname"];
|
||||
if (!t || t[0] != 't')
|
||||
continue;
|
||||
tval = atoi (t+1);
|
||||
if (tval > maxt)
|
||||
maxt = tval;
|
||||
}
|
||||
|
||||
sprintf (name,"t%i",maxt+1);
|
||||
|
||||
[self setKey: "targetname" toValue: name];
|
||||
|
||||
return [self valueForQKey: "targetname"]; // so it's not on the stack
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
FILE METHODS
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
int nument;
|
||||
|
||||
- initFromTokens
|
||||
{
|
||||
char key[MAXTOKEN];
|
||||
id eclass, brush;
|
||||
char *spawn;
|
||||
vec3_t emins, emaxs;
|
||||
vec3_t org;
|
||||
texturedef_t td;
|
||||
esize_t esize;
|
||||
int i, c;
|
||||
float *color;
|
||||
|
||||
[self init];
|
||||
|
||||
if (!GetToken (true))
|
||||
{
|
||||
[self free];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (strcmp (token, "{") )
|
||||
Error ("initFromFileP: { not found");
|
||||
|
||||
do
|
||||
{
|
||||
if (!GetToken (true))
|
||||
break;
|
||||
if (!strcmp (token, "}") )
|
||||
break;
|
||||
if (!strcmp (token, "{") )
|
||||
{ // read a brush
|
||||
brush = [[SetBrush alloc] initFromTokens: self];
|
||||
[self addObject: brush];
|
||||
}
|
||||
else
|
||||
{ // read a key / value pair
|
||||
strcpy (key, token);
|
||||
GetToken (false);
|
||||
[self setKey: key toValue:token];
|
||||
}
|
||||
} while (1);
|
||||
|
||||
nument++;
|
||||
|
||||
// get class
|
||||
spawn = [self valueForQKey: "classname"];
|
||||
eclass = [entity_classes_i classForName: spawn];
|
||||
|
||||
esize = [eclass esize];
|
||||
|
||||
[self getVector: org forKey: "origin"];
|
||||
|
||||
if ([self count] && esize != esize_model)
|
||||
{
|
||||
printf ("WARNING:Entity with brushes and wrong model type\n");
|
||||
[self empty];
|
||||
}
|
||||
|
||||
if (![self count] && esize == esize_model)
|
||||
{
|
||||
printf ("WARNING:Entity with no brushes and esize_model\n");
|
||||
[texturepalette_i getTextureDef: &td];
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
emins[i] = org[i] - 8;
|
||||
emaxs[i] = org[i] + 8;
|
||||
}
|
||||
brush = [[SetBrush alloc] initOwner: self mins:emins maxs:emaxs
|
||||
texture: &td];
|
||||
[self addObject: brush];
|
||||
}
|
||||
|
||||
// create a brush if needed
|
||||
if (esize == esize_fixed)
|
||||
[self createFixedBrush: org];
|
||||
else
|
||||
modifiable = YES;
|
||||
|
||||
// set all the brush colors
|
||||
color = [eclass drawColor];
|
||||
|
||||
c = [self count];
|
||||
for (i=0 ; i<c ; i++)
|
||||
{
|
||||
brush = [self objectAt: i];
|
||||
[brush setEntityColor: color];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- writeToFILE: (FILE *)f region:(BOOL)reg;
|
||||
{
|
||||
epair_t *e;
|
||||
int i;
|
||||
id new;
|
||||
char value[80];
|
||||
vec3_t mins, maxs, org;
|
||||
float *v;
|
||||
BOOL temporg;
|
||||
char oldang[80];
|
||||
|
||||
temporg = NO;
|
||||
if (reg)
|
||||
{
|
||||
if ( !strcmp ([self valueForQKey: "classname"], "info_player_start") )
|
||||
{ // move the playerstart temporarily to the camera position
|
||||
temporg = YES;
|
||||
strcpy (oldang, [self valueForQKey: "angle"]);
|
||||
sprintf (value, "%i", (int)([cameraview_i yawAngle]*180/M_PI));
|
||||
[self setKey: "angle" toValue: value];
|
||||
}
|
||||
else if ( self != [map_i objectAt: 0]
|
||||
&& [[self objectAt: 0] regioned] )
|
||||
return self; // skip the entire entity definition
|
||||
}
|
||||
|
||||
fprintf (f,"{\n");
|
||||
|
||||
// set an origin epair
|
||||
if (!modifiable)
|
||||
{
|
||||
[[self objectAt: 0] getMins: mins maxs: maxs];
|
||||
if (temporg)
|
||||
{
|
||||
[cameraview_i getOrigin: mins];
|
||||
mins[0] -= 16;
|
||||
mins[1] -= 16;
|
||||
mins[2] -= 48;
|
||||
}
|
||||
new = [entity_classes_i classForName:
|
||||
[self valueForQKey: "classname"]];
|
||||
if (new)
|
||||
v = [new mins];
|
||||
else
|
||||
v = vec3_origin;
|
||||
|
||||
VectorSubtract (mins, v, org);
|
||||
sprintf (value, "%i %i %i",(int)org[0], (int)org[1], (int)org[2]);
|
||||
[self setKey:"origin" toValue: value];
|
||||
}
|
||||
|
||||
for (e=epairs ; e ; e=e->next)
|
||||
fprintf (f,"\"%s\"\t\"%s\"\n", e->key, e->value);
|
||||
|
||||
// fixed size entities don't save out brushes
|
||||
if ( modifiable )
|
||||
{
|
||||
for (i=0 ; i<numElements ; i++)
|
||||
[[self objectAt: i] writeToFILE: f region: reg];
|
||||
}
|
||||
|
||||
fprintf (f,"}\n");
|
||||
|
||||
if (temporg)
|
||||
[self setKey: "angle" toValue: oldang];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
INTERACTION
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
@end
|
42
tools/Forge/Bundles/MapEdit/EntityClass.h
Normal file
42
tools/Forge/Bundles/MapEdit/EntityClass.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#import <AppKit/AppKit.h>
|
||||
#import "mathlib.h"
|
||||
|
||||
typedef enum {esize_model, esize_fixed} esize_t;
|
||||
|
||||
#define MAX_FLAGS 8
|
||||
|
||||
@interface EntityClass : Object
|
||||
{
|
||||
char *name;
|
||||
esize_t esize;
|
||||
vec3_t mins, maxs;
|
||||
vec3_t color;
|
||||
char *comments;
|
||||
char flagnames[MAX_FLAGS][32];
|
||||
}
|
||||
|
||||
- initFromText: (char *)text;
|
||||
- (char *)classname;
|
||||
- (esize_t)esize;
|
||||
- (float *)mins; // only for esize_fixed
|
||||
- (float *)maxs; // only for esize_fixed
|
||||
- (float *)drawColor;
|
||||
- (char *)comments;
|
||||
- (char *)flagName: (unsigned)flagnum;
|
||||
|
||||
@end
|
||||
|
||||
extern id entity_classes_i;
|
||||
|
||||
@interface EntityClassList : NSMutableArray
|
||||
{
|
||||
id nullclass;
|
||||
char *source_path;
|
||||
}
|
||||
|
||||
- initForSourceDirectory: (char *)path;
|
||||
- (id)classForName: (char *)name;
|
||||
- (void)scanDirectory;
|
||||
|
||||
@end
|
||||
|
265
tools/Forge/Bundles/MapEdit/EntityClass.m
Normal file
265
tools/Forge/Bundles/MapEdit/EntityClass.m
Normal file
|
@ -0,0 +1,265 @@
|
|||
|
||||
#import "qedefs.h"
|
||||
|
||||
@implementation EntityClass
|
||||
|
||||
// the classname, color triple, and bounding box are parsed out of comments
|
||||
// A ? size means take the exact brush size.
|
||||
//
|
||||
// /*QUAKED <classname> (0 0 0) ?
|
||||
// /*QUAKED <classname> (0 0 0) (-8 -8 -8) (8 8 8)
|
||||
//
|
||||
// Flag names can follow the size description:
|
||||
//
|
||||
// /*QUAKED func_door (0 .5 .8) ? START_OPEN STONE_SOUND DOOR_DONT_LINK GOLD_KEY SILVER_KEY
|
||||
|
||||
char *debugname;
|
||||
- initFromText: (char *)text
|
||||
{
|
||||
char *t;
|
||||
int len;
|
||||
int r, i;
|
||||
char parms[256], *p;
|
||||
|
||||
[super init];
|
||||
|
||||
text += strlen("/*QUAKED ");
|
||||
|
||||
// grab the name
|
||||
text = COM_Parse (text);
|
||||
name = malloc (strlen(com_token)+1);
|
||||
strcpy (name, com_token);
|
||||
debugname = name;
|
||||
|
||||
// grab the color
|
||||
r = sscanf (text," (%f %f %f)", &color[0], &color[1], &color[2]);
|
||||
if (r != 3)
|
||||
return NULL;
|
||||
|
||||
while (*text != ')')
|
||||
{
|
||||
if (!*text)
|
||||
return NULL;
|
||||
text++;
|
||||
}
|
||||
text++;
|
||||
|
||||
// get the size
|
||||
text = COM_Parse (text);
|
||||
if (com_token[0] == '(')
|
||||
{ // parse the size as two vectors
|
||||
esize = esize_fixed;
|
||||
r = sscanf (text,"%f %f %f) (%f %f %f)", &mins[0], &mins[1], &mins[2], &maxs[0], &maxs[1], &maxs[2]);
|
||||
if (r != 6)
|
||||
return NULL;
|
||||
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
while (*text != ')')
|
||||
{
|
||||
if (!*text)
|
||||
return NULL;
|
||||
text++;
|
||||
}
|
||||
text++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // use the brushes
|
||||
esize = esize_model;
|
||||
}
|
||||
|
||||
// get the flags
|
||||
|
||||
|
||||
// copy to the first /n
|
||||
p = parms;
|
||||
while (*text && *text != '\n')
|
||||
*p++ = *text++;
|
||||
*p = 0;
|
||||
text++;
|
||||
|
||||
// any remaining words are parm flags
|
||||
p = parms;
|
||||
for (i=0 ; i<8 ; i++)
|
||||
{
|
||||
p = COM_Parse (p);
|
||||
if (!p)
|
||||
break;
|
||||
strcpy (flagnames[i], com_token);
|
||||
}
|
||||
|
||||
// find the length until close comment
|
||||
for (t=text ; t[0] && !(t[0]=='*' && t[1]=='/') ; t++)
|
||||
;
|
||||
|
||||
// copy the comment block out
|
||||
len = t-text;
|
||||
comments = malloc (len+1);
|
||||
memcpy (comments, text, len);
|
||||
comments[len] = 0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (esize_t)esize
|
||||
{
|
||||
return esize;
|
||||
}
|
||||
|
||||
- (char *)classname
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
- (float *)mins
|
||||
{
|
||||
return mins;
|
||||
}
|
||||
|
||||
- (float *)maxs
|
||||
{
|
||||
return maxs;
|
||||
}
|
||||
|
||||
- (float *)drawColor
|
||||
{
|
||||
return color;
|
||||
}
|
||||
|
||||
- (char *)comments
|
||||
{
|
||||
return comments;
|
||||
}
|
||||
|
||||
|
||||
- (char *)flagName: (unsigned)flagnum
|
||||
{
|
||||
if (flagnum >= MAX_FLAGS)
|
||||
Error ("EntityClass flagName: bad number");
|
||||
return flagnames[flagnum];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//===========================================================================
|
||||
|
||||
@implementation EntityClassList
|
||||
|
||||
/*
|
||||
=================
|
||||
insertEC:
|
||||
=================
|
||||
*/
|
||||
- (void)insertEC: ec
|
||||
{
|
||||
char *name;
|
||||
int i;
|
||||
|
||||
name = [ec classname];
|
||||
for (i=0 ; i<[self count] ; i++)
|
||||
{
|
||||
if (strcasecmp (name, [[self objectAtIndex: i] classname]) < 0)
|
||||
{
|
||||
[self insertObject: ec atIndex:i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
[self addObject: ec];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
scanFile
|
||||
=================
|
||||
*/
|
||||
- (void)scanFile: (char *)filename
|
||||
{
|
||||
int size;
|
||||
char *data;
|
||||
id cl;
|
||||
int i;
|
||||
char path[1024];
|
||||
|
||||
sprintf (path,"%s/%s", source_path, filename);
|
||||
|
||||
size = LoadFile (path, (void *)&data);
|
||||
|
||||
for (i=0 ; i<size ; i++)
|
||||
if (!strncmp(data+i, "/*QUAKED",8))
|
||||
{
|
||||
cl = [[EntityClass alloc] initFromText: data+i];
|
||||
if (cl)
|
||||
[self insertEC: cl];
|
||||
else
|
||||
printf ("Error parsing: %s in %s\n",debugname, filename);
|
||||
}
|
||||
|
||||
free (data);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
scanDirectory
|
||||
=================
|
||||
*/
|
||||
- (void)scanDirectory
|
||||
{
|
||||
int count, i;
|
||||
struct direct **namelist, *ent;
|
||||
|
||||
[self removeAllObjects];
|
||||
|
||||
count = scandir(source_path, &namelist, NULL, NULL);
|
||||
|
||||
for (i=0 ; i<count ; i++)
|
||||
{
|
||||
int len;
|
||||
ent = namelist[i];
|
||||
len = strlen (ent->d_name);
|
||||
if (len <= 3)
|
||||
continue;
|
||||
if (!strcmp (ent->d_name+len-3,".qc"))
|
||||
[self scanFile: ent->d_name];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
id entity_classes_i;
|
||||
|
||||
|
||||
- initForSourceDirectory: (char *)path
|
||||
{
|
||||
[super init];
|
||||
|
||||
source_path = path;
|
||||
[self scanDirectory];
|
||||
|
||||
entity_classes_i = self;
|
||||
|
||||
nullclass = [[EntityClass alloc] initFromText:
|
||||
"/*QUAKED UNKNOWN_CLASS (0 0.5 0) ?"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)classForName: (char *)name
|
||||
{
|
||||
int i;
|
||||
id o;
|
||||
|
||||
for (i=0 ; i<[self count] ; i++)
|
||||
{
|
||||
o = [self objectAtIndex: i];
|
||||
if (!strcmp (name,[o classname]) )
|
||||
return o;
|
||||
}
|
||||
|
||||
return nullclass;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
27
tools/Forge/Bundles/MapEdit/GNUmakefile
Normal file
27
tools/Forge/Bundles/MapEdit/GNUmakefile
Normal file
|
@ -0,0 +1,27 @@
|
|||
include $(GNUSTEP_MAKEFILES)/common.make
|
||||
|
||||
BUNDLE_NAME= MapEdit
|
||||
BUNDLE_EXTENSION= .forgeb
|
||||
#
|
||||
# We don't install this bundle, it goes inside the app.
|
||||
#
|
||||
BUNDLE_INSTALL_DIR= none
|
||||
MapEdit_STANDARD_INSTALL= no
|
||||
|
||||
MapEdit_RESOURCE_FILES= \
|
||||
MapEdit.gorm
|
||||
|
||||
MapEdit_OBJC_FILES= \
|
||||
CameraView.m Clipper.m EntityClass.m KeypairView.m PopScrollView.m ZView.m misc.m render.m
|
||||
|
||||
MapEdit_HEADERS= \
|
||||
EntityClass.h
|
||||
|
||||
MapEdit_PRINCIPAL_CLASS= \
|
||||
MapEdit
|
||||
|
||||
-include GNUmakefile.preamble
|
||||
|
||||
include $(GNUSTEP_MAKEFILES)/bundle.make
|
||||
|
||||
-include GNUmakefile.postamble
|
39
tools/Forge/Bundles/MapEdit/GNUmakefile.preamble
Normal file
39
tools/Forge/Bundles/MapEdit/GNUmakefile.preamble
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Additional flags to pass to the preprocessor
|
||||
ADDITIONAL_CPPFLAGS +=
|
||||
|
||||
# Additional flags to pass to the Objective-C compiler
|
||||
ADDITIONAL_OBJCFLAGS += -DUSING_NIBS -Wall -Werror
|
||||
|
||||
# Additional flags to pass to the C compiler
|
||||
ADDITIONAL_CFLAGS += -Wall -Werror
|
||||
|
||||
# Additional include directories the compiler should search
|
||||
ADDITIONAL_INCLUDE_DIRS += -I ../..
|
||||
|
||||
# Additional LDFLAGS to pass to the linker
|
||||
ADDITIONAL_LDFLAGS +=
|
||||
|
||||
# Additional library directories the linker should search
|
||||
ADDITIONAL_LIB_DIRS +=
|
||||
|
||||
# Additional libraries
|
||||
|
||||
# GNUstepWeb
|
||||
ADDITIONAL_GSW_LIBS +=
|
||||
# GUI apps
|
||||
ADDITIONAL_GUI_LIBS +=
|
||||
# Libraries
|
||||
ADDITIONAL_LIBRARY_LIBS +=
|
||||
# ObjC stuff
|
||||
ADDITIONAL_OBJC_LIBS +=
|
||||
# Tools
|
||||
ADDITIONAL_TOOL_LIBS +=
|
||||
# WebObjects
|
||||
ADDITIONAL_WO_LIBS +=
|
||||
|
||||
#
|
||||
# Flags dealing with installing and uninstalling
|
||||
#
|
||||
|
||||
# Additional directories to be created during installation
|
||||
ADDITIONAL_INSTALL_DIRS +=
|
71
tools/Forge/Bundles/MapEdit/InspectorControl.h
Normal file
71
tools/Forge/Bundles/MapEdit/InspectorControl.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#define MINIWINICON "DoomEdIcon"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
i_project,
|
||||
i_textures,
|
||||
i_things,
|
||||
i_prefs,
|
||||
i_settings,
|
||||
i_output,
|
||||
i_help,
|
||||
i_end
|
||||
} insp_e;
|
||||
|
||||
extern id inspcontrol_i;
|
||||
|
||||
@interface InspectorControl:Object
|
||||
{
|
||||
id inspectorView_i; // inspector view
|
||||
id inspectorSubview_i; // inspector view's current subview (gets replaced)
|
||||
|
||||
id contentList; // List of contentviews (corresponds to
|
||||
// insp_e enum order)
|
||||
id windowList; // List of Windows (corresponds to
|
||||
// insp_e enum order)
|
||||
|
||||
id obj_textures_i; // TexturePalette object (for delegating)
|
||||
id obj_genkeypair_i; // GenKeyPair object
|
||||
|
||||
id popUpButton_i; // PopUpList title button
|
||||
id popUpMatrix_i; // PopUpList matrix
|
||||
id itemList; // List of popUp buttons
|
||||
|
||||
insp_e currentInspectorType; // keep track of current inspector
|
||||
//
|
||||
// Add id's here for new inspectors
|
||||
// **NOTE: Make sure PopUpList has correct TAG value that
|
||||
// corresponds to the enums above!
|
||||
|
||||
// Windows
|
||||
id win_project_i; // project
|
||||
id win_textures_i; // textures
|
||||
id win_things_i; // things
|
||||
id win_prefs_i; // preferences
|
||||
id win_settings_i; // project settings
|
||||
id win_output_i; // bsp output
|
||||
id win_help_i; // documentation
|
||||
|
||||
// PopUpList objs
|
||||
id itemProject_i; // project
|
||||
id itemTextures_i; // textures
|
||||
id itemThings_i; // things
|
||||
id itemPrefs_i; // preferences
|
||||
id itemSettings_i; // project settings
|
||||
id itemOutput_i; // bsp output
|
||||
id itemHelp_i; // docs
|
||||
}
|
||||
|
||||
- awakeFromNib;
|
||||
- changeInspector:sender;
|
||||
- changeInspectorTo:(insp_e)which;
|
||||
- (insp_e)getCurrentInspector;
|
||||
|
||||
@end
|
||||
|
||||
@protocol InspectorControl
|
||||
- windowResized;
|
||||
@end
|
128
tools/Forge/Bundles/MapEdit/InspectorControl.m
Normal file
128
tools/Forge/Bundles/MapEdit/InspectorControl.m
Normal file
|
@ -0,0 +1,128 @@
|
|||
|
||||
#import "qedefs.h"
|
||||
|
||||
// Add .h-files here for new inspectors
|
||||
#import "Things.h"
|
||||
#import "TexturePalette.h"
|
||||
#import "Preferences.h"
|
||||
|
||||
id inspcontrol_i;
|
||||
|
||||
@implementation InspectorControl
|
||||
|
||||
- awakeFromNib
|
||||
{
|
||||
inspcontrol_i = self;
|
||||
|
||||
currentInspectorType = -1;
|
||||
|
||||
contentList = [[List alloc] init];
|
||||
windowList = [[List alloc] init];
|
||||
itemList = [[List alloc] init];
|
||||
|
||||
// ADD NEW INSPECTORS HERE...
|
||||
|
||||
[windowList addObject:win_project_i];
|
||||
[contentList addObject:[win_project_i contentView]];
|
||||
[itemProject_i setKeyEquivalent:'1'];
|
||||
[itemList addObject:itemProject_i];
|
||||
|
||||
[windowList addObject:win_textures_i];
|
||||
[contentList addObject:[win_textures_i contentView]];
|
||||
[itemTextures_i setKeyEquivalent:'2'];
|
||||
[itemList addObject:itemTextures_i];
|
||||
|
||||
[windowList addObject:win_things_i];
|
||||
[contentList addObject:[win_things_i contentView]];
|
||||
[itemThings_i setKeyEquivalent:'3'];
|
||||
[itemList addObject:itemThings_i];
|
||||
|
||||
[windowList addObject:win_prefs_i];
|
||||
[contentList addObject:[win_prefs_i contentView]];
|
||||
[itemPrefs_i setKeyEquivalent:'4'];
|
||||
[itemList addObject:itemPrefs_i];
|
||||
|
||||
[windowList addObject:win_settings_i];
|
||||
[contentList addObject:[win_settings_i contentView]];
|
||||
[itemSettings_i setKeyEquivalent:'5'];
|
||||
[itemList addObject:itemSettings_i];
|
||||
|
||||
[windowList addObject:win_output_i];
|
||||
[contentList addObject:[win_output_i contentView]];
|
||||
[itemOutput_i setKeyEquivalent:'6'];
|
||||
[itemList addObject:itemOutput_i];
|
||||
|
||||
[windowList addObject:win_help_i];
|
||||
[contentList addObject:[win_help_i contentView]];
|
||||
[itemHelp_i setKeyEquivalent:'7'];
|
||||
[itemList addObject:itemHelp_i];
|
||||
|
||||
// Setup inspector window with project subview first
|
||||
|
||||
[inspectorView_i setAutoresizeSubviews:YES];
|
||||
|
||||
inspectorSubview_i = [contentList objectAt:i_project];
|
||||
[inspectorView_i addSubview:inspectorSubview_i];
|
||||
|
||||
currentInspectorType = -1;
|
||||
[self changeInspectorTo:i_project];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Sent by the PopUpList in the Inspector
|
||||
// Each cell in the PopUpList must have the correct tag
|
||||
//
|
||||
- changeInspector:sender
|
||||
{
|
||||
id cell;
|
||||
|
||||
cell = [sender selectedCell];
|
||||
[self changeInspectorTo:[cell tag]];
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Change to specific Inspector
|
||||
//
|
||||
- changeInspectorTo:(insp_e)which
|
||||
{
|
||||
id newView;
|
||||
NSRect r;
|
||||
id cell;
|
||||
NSRect f;
|
||||
|
||||
if (which == currentInspectorType)
|
||||
return self;
|
||||
|
||||
currentInspectorType = which;
|
||||
newView = [contentList objectAt:which];
|
||||
|
||||
cell = [itemList objectAt:which]; // set PopUpButton title
|
||||
[popUpButton_i setTitle:[cell title]];
|
||||
|
||||
[inspectorView_i replaceSubview:inspectorSubview_i with:newView];
|
||||
[inspectorView_i getFrame:&r];
|
||||
inspectorSubview_i = newView;
|
||||
[inspectorSubview_i setAutosizing:NS_WIDTHSIZABLE | NS_HEIGHTSIZABLE];
|
||||
[inspectorSubview_i sizeTo:r.size.width - 4 :r.size.height - 4];
|
||||
|
||||
[inspectorSubview_i lockFocus];
|
||||
[inspectorSubview_i getBounds:&f];
|
||||
PSsetgray(NS_LTGRAY);
|
||||
NSRectFill(&f);
|
||||
[inspectorSubview_i unlockFocus];
|
||||
[inspectorView_i display];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (insp_e)getCurrentInspector
|
||||
{
|
||||
return currentInspectorType;
|
||||
}
|
||||
|
||||
|
||||
@end
|
16
tools/Forge/Bundles/MapEdit/KeypairView.h
Normal file
16
tools/Forge/Bundles/MapEdit/KeypairView.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
|
||||
extern id keypairview_i;
|
||||
|
||||
@interface KeypairView:NSView
|
||||
{
|
||||
}
|
||||
|
||||
- calcViewSize;
|
||||
|
||||
#define SPACING 4
|
||||
#define FONTSIZE 12
|
||||
#define EXTRASPC 2
|
||||
|
||||
#define LINEHEIGHT 16
|
||||
|
||||
@end
|
93
tools/Forge/Bundles/MapEdit/KeypairView.m
Normal file
93
tools/Forge/Bundles/MapEdit/KeypairView.m
Normal file
|
@ -0,0 +1,93 @@
|
|||
|
||||
#import "qedefs.h"
|
||||
|
||||
id keypairview_i;
|
||||
|
||||
@implementation KeypairView
|
||||
|
||||
/*
|
||||
==================
|
||||
initWithFrame:
|
||||
==================
|
||||
*/
|
||||
- initWithFrame:(NSRect)frameRect
|
||||
{
|
||||
[super initWithFrame:frameRect];
|
||||
keypairview_i = self;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- calcViewSize
|
||||
{
|
||||
NSRect b;
|
||||
NSPoint pt;
|
||||
int count;
|
||||
id ent;
|
||||
|
||||
ent = [map_i currentEntity];
|
||||
count = [ent numPairs];
|
||||
|
||||
//XXX[_super_view setFlipped: YES];
|
||||
|
||||
b = [_super_view bounds];
|
||||
b.size.height = LINEHEIGHT*count + SPACING;
|
||||
[self setBounds: b];
|
||||
pt.x = pt.y = 0;
|
||||
[self scrollPoint: pt];
|
||||
return self;
|
||||
}
|
||||
|
||||
- drawSelf:(const NSRect *)rects :(int)rectCount
|
||||
{
|
||||
epair_t *pair;
|
||||
int y;
|
||||
|
||||
//XXX PSsetgray(NSGrayComponent(NS_COLORLTGRAY));
|
||||
PSrectfill(0,0,_bounds.size.width,_bounds.size.height);
|
||||
|
||||
//XXX PSselectfont("Helvetica-Bold",FONTSIZE);
|
||||
PSrotate(0);
|
||||
PSsetgray(0);
|
||||
|
||||
pair = [[map_i currentEntity] epairs];
|
||||
y = _bounds.size.height - LINEHEIGHT;
|
||||
for ( ; pair ; pair=pair->next)
|
||||
{
|
||||
PSmoveto(SPACING, y);
|
||||
PSshow(pair->key);
|
||||
PSmoveto(100, y);
|
||||
PSshow(pair->value);
|
||||
y -= LINEHEIGHT;
|
||||
}
|
||||
PSstroke();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent *)theEvent
|
||||
{
|
||||
NSPoint loc;
|
||||
int i;
|
||||
epair_t *p;
|
||||
|
||||
loc = [theEvent locationInWindow];
|
||||
loc = [self convertPoint:loc fromView:NULL];
|
||||
|
||||
i = (_bounds.size.height - loc.y - 4) / LINEHEIGHT;
|
||||
|
||||
p = [[map_i currentEntity] epairs];
|
||||
while ( i )
|
||||
{
|
||||
p=p->next;
|
||||
if (!p)
|
||||
return;
|
||||
i--;
|
||||
}
|
||||
if (p)
|
||||
[things_i setSelectedKey: p];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@end
|
68
tools/Forge/Bundles/MapEdit/Map.h
Normal file
68
tools/Forge/Bundles/MapEdit/Map.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
|
||||
// Map is a list of Entity objects
|
||||
|
||||
extern id map_i;
|
||||
|
||||
@interface Map : NSMutableArray
|
||||
{
|
||||
id currentEntity;
|
||||
id oldselection; // temp when loading a new map
|
||||
float minz, maxz;
|
||||
}
|
||||
|
||||
- newMap;
|
||||
|
||||
- writeStats;
|
||||
|
||||
- readMapFile: (char *)fname;
|
||||
- writeMapFile: (char *)fname useRegion: (BOOL)reg;
|
||||
|
||||
- entityConnect: (vec3_t)p1 : (vec3_t)p2;
|
||||
|
||||
- selectRay: (vec3_t)p1 : (vec3_t)p2 : (BOOL)ef;
|
||||
- grabRay: (vec3_t)p1 : (vec3_t)p2;
|
||||
- setTextureRay: (vec3_t)p1 : (vec3_t)p2 : (BOOL)allsides;
|
||||
- getTextureRay: (vec3_t)p1 : (vec3_t)p2;
|
||||
|
||||
- currentEntity;
|
||||
- setCurrentEntity: ent;
|
||||
|
||||
- (float)currentMinZ;
|
||||
- setCurrentMinZ: (float)m;
|
||||
- (float)currentMaxZ;
|
||||
- setCurrentMaxZ: (float)m;
|
||||
|
||||
- (int)numSelected;
|
||||
- selectedBrush; // returns the first selected brush
|
||||
|
||||
//
|
||||
// operations on current selection
|
||||
//
|
||||
- makeSelectedPerform: (SEL)sel;
|
||||
- makeUnselectedPerform: (SEL)sel;
|
||||
- makeAllPerform: (SEL)sel;
|
||||
- makeGlobalPerform: (SEL)sel; // in and out of region
|
||||
|
||||
- cloneSelection: sender;
|
||||
|
||||
- makeEntity: sender;
|
||||
|
||||
- subtractSelection: sender;
|
||||
|
||||
- selectCompletelyInside: sender;
|
||||
- selectPartiallyInside: sender;
|
||||
|
||||
- tallBrush: sender;
|
||||
- shortBrush: sender;
|
||||
|
||||
- rotate_x: sender;
|
||||
- rotate_y: sender;
|
||||
- rotate_z: sender;
|
||||
|
||||
- flip_x: sender;
|
||||
- flip_y: sender;
|
||||
- flip_z: sender;
|
||||
|
||||
- selectCompleteEntity: sender;
|
||||
|
||||
@end
|
1125
tools/Forge/Bundles/MapEdit/Map.m
Normal file
1125
tools/Forge/Bundles/MapEdit/Map.m
Normal file
File diff suppressed because it is too large
Load diff
11
tools/Forge/Bundles/MapEdit/PopScrollView.h
Normal file
11
tools/Forge/Bundles/MapEdit/PopScrollView.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#import <AppKit/AppKit.h>
|
||||
|
||||
@interface PopScrollView : NSScrollView
|
||||
{
|
||||
id button1, button2;
|
||||
}
|
||||
|
||||
- initWithFrame:(NSRect)frameRect button1: b1 button2: b2;
|
||||
- tile;
|
||||
|
||||
@end
|
87
tools/Forge/Bundles/MapEdit/PopScrollView.m
Normal file
87
tools/Forge/Bundles/MapEdit/PopScrollView.m
Normal file
|
@ -0,0 +1,87 @@
|
|||
|
||||
#import "qedefs.h"
|
||||
|
||||
@implementation PopScrollView
|
||||
|
||||
/*
|
||||
====================
|
||||
initWithFrame: button:
|
||||
|
||||
Initizes a scroll view with a button at it's lower right corner
|
||||
====================
|
||||
*/
|
||||
|
||||
- initWithFrame:(NSRect)frameRect button1:b1 button2:b2
|
||||
{
|
||||
[super initWithFrame: frameRect];
|
||||
|
||||
[self addSubview: b1];
|
||||
[self addSubview: b2];
|
||||
|
||||
button1 = b1;
|
||||
button2 = b2;
|
||||
|
||||
[self setHasHorizontalScroller: YES];
|
||||
[self setHasVerticalScroller: YES];
|
||||
|
||||
[self setBorderType: NSBezelBorder];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
tile
|
||||
|
||||
Adjust the size for the pop up scale menu
|
||||
=================
|
||||
*/
|
||||
|
||||
- tile
|
||||
{
|
||||
NSRect scrollerframe;
|
||||
NSRect buttonframe, buttonframe2;
|
||||
NSRect newframe;
|
||||
|
||||
[super tile];
|
||||
buttonframe = [button1 frame];
|
||||
buttonframe2 = [button2 frame];
|
||||
scrollerframe = [_horizScroller frame];
|
||||
|
||||
newframe.origin.y = scrollerframe.origin.y;
|
||||
newframe.origin.x = scrollerframe.size.width - buttonframe.size.width;
|
||||
newframe.size.width = buttonframe.size.width;
|
||||
newframe.size.height = scrollerframe.size.height;
|
||||
scrollerframe.size.width -= newframe.size.width;
|
||||
[button1 setFrame: newframe];
|
||||
newframe.size.width = buttonframe2.size.width;
|
||||
newframe.origin.x -= newframe.size.width;
|
||||
[button2 setFrame: newframe];
|
||||
scrollerframe.size.width -= newframe.size.width;
|
||||
|
||||
[_horizScroller setFrame: scrollerframe];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
- superviewSizeChanged:(const NSSize *)oldSize
|
||||
{
|
||||
[super superviewSizeChanged: oldSize];
|
||||
|
||||
[[self docView] newSuperBounds];
|
||||
|
||||
return self;
|
||||
}
|
||||
*/
|
||||
|
||||
-(BOOL) acceptsFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
78
tools/Forge/Bundles/MapEdit/Preferences.h
Normal file
78
tools/Forge/Bundles/MapEdit/Preferences.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
|
||||
extern id preferences_i;
|
||||
|
||||
extern float lightaxis[3];
|
||||
|
||||
// these are personal preferences saved in NeXT defaults, not project
|
||||
// parameters saved in the quake.qe_project file
|
||||
|
||||
@interface Preferences:Object
|
||||
{
|
||||
id bspSound_i; // actual sound object
|
||||
|
||||
// internal state
|
||||
char projectpath[1024];
|
||||
char bspSound[1024];
|
||||
|
||||
BOOL brushOffset;
|
||||
BOOL showBSP;
|
||||
|
||||
float xlight;
|
||||
float ylight;
|
||||
float zlight; // 0.0 - 1.0
|
||||
|
||||
int startwad; // 0 - 2
|
||||
|
||||
// UI targets
|
||||
id startproject_i; // TextField
|
||||
|
||||
id bspSoundField_i; // TextField of bspSound
|
||||
|
||||
id brushOffset_i; // Brush Offset checkbox
|
||||
id showBSP_i; // Show BSP Output checkbox
|
||||
|
||||
id startwad_i; // which wad to load at startup
|
||||
|
||||
id xlight_i; // X-side lighting
|
||||
id ylight_i; // Y-side lighting
|
||||
id zlight_i; // Z-side lighting
|
||||
}
|
||||
|
||||
- readDefaults;
|
||||
|
||||
//
|
||||
// validate and set methods called by UI or defaults
|
||||
//
|
||||
- setProjectPath:(char *)path;
|
||||
- setBspSoundPath:(char *)path; // set the path of the soundfile externally
|
||||
- setShowBSP:(int)state; // set the state of ShowBSP
|
||||
- setBrushOffset:(int)state; // set the state of BrushOffset
|
||||
- setStartWad:(int)value; // set start wad (0-2)
|
||||
- setXlight:(float)value; // set Xlight value for CameraView
|
||||
- setYlight:(float)value; // set Ylight value for CameraView
|
||||
- setZlight:(float)value; // set Zlight value for CameraView
|
||||
|
||||
//
|
||||
// UI targets
|
||||
//
|
||||
- setBspSound:sender; // use OpenPanel to select sound
|
||||
- setCurrentProject:sender; // make current roject the default
|
||||
- UIChanged: sender; // target for all checks and fields
|
||||
|
||||
//
|
||||
// methods used by other objects to retreive defaults
|
||||
//
|
||||
- playBspSound;
|
||||
|
||||
- (char *)getProjectPath;
|
||||
- (int)getBrushOffset; // get the state
|
||||
- (int)getShowBSP; // get the state
|
||||
|
||||
- (float)getXlight; // get Xlight value
|
||||
- (float)getYlight; // get Ylight value
|
||||
- (float)getZlight; // get Zlight value
|
||||
|
||||
- (int)getStartWad;
|
||||
|
||||
|
||||
@end
|
330
tools/Forge/Bundles/MapEdit/Preferences.m
Normal file
330
tools/Forge/Bundles/MapEdit/Preferences.m
Normal file
|
@ -0,0 +1,330 @@
|
|||
|
||||
#import "qedefs.h"
|
||||
|
||||
id preferences_i;
|
||||
|
||||
#define DEFOWNER "QuakeEd2"
|
||||
|
||||
float lightaxis[3] = {1, 0.6, 0.75};
|
||||
|
||||
@implementation Preferences
|
||||
|
||||
- init
|
||||
{
|
||||
[super init];
|
||||
preferences_i = self;
|
||||
return self;
|
||||
}
|
||||
|
||||
int _atoi (char *c)
|
||||
{
|
||||
if (!c)
|
||||
return 0;
|
||||
return atoi(c);
|
||||
}
|
||||
|
||||
int _atof (char *c)
|
||||
{
|
||||
if (!c)
|
||||
return 0;
|
||||
return atof(c);
|
||||
}
|
||||
|
||||
void WriteNumericDefault (char *name, float value)
|
||||
{
|
||||
char str[128];
|
||||
|
||||
sprintf (str,"%f", value);
|
||||
NSWriteDefault (DEFOWNER, name, str);
|
||||
}
|
||||
void WriteStringDefault (char *name, char *value)
|
||||
{
|
||||
NSWriteDefault (DEFOWNER, name, value);
|
||||
}
|
||||
|
||||
//
|
||||
// Read in at start of program
|
||||
//
|
||||
- readDefaults
|
||||
{
|
||||
char *string;
|
||||
float value;
|
||||
|
||||
string = (char *)NSGetDefaultValue(DEFOWNER,"ProjectPath");
|
||||
[self setProjectPath: string];
|
||||
|
||||
string = (char *)NSGetDefaultValue(DEFOWNER,"BspSoundPath");
|
||||
[self setBspSoundPath:string];
|
||||
|
||||
value = _atoi((char *)NSGetDefaultValue(DEFOWNER,"ShowBSPOutput"));
|
||||
[self setShowBSP:value];
|
||||
|
||||
value = _atoi((char *)NSGetDefaultValue(DEFOWNER,"OffsetBrushCopy"));
|
||||
[self setBrushOffset:value];
|
||||
|
||||
value = _atoi((char *)NSGetDefaultValue(DEFOWNER,"StartWad"));
|
||||
[self setStartWad:value];
|
||||
|
||||
value = _atof((char *)NSGetDefaultValue(DEFOWNER,"Xlight"));
|
||||
[self setXlight:value];
|
||||
|
||||
value = _atof((char *)NSGetDefaultValue(DEFOWNER,"Ylight"));
|
||||
[self setYlight:value];
|
||||
|
||||
value = _atof((char *)NSGetDefaultValue(DEFOWNER,"Zlight"));
|
||||
[self setZlight:value];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- setProjectPath:(char *)path
|
||||
{
|
||||
if (!path)
|
||||
path = "";
|
||||
strcpy (projectpath, path);
|
||||
[startproject_i setStringValue: path];
|
||||
WriteStringDefault ("ProjectPath", path);
|
||||
return self;
|
||||
}
|
||||
|
||||
- setCurrentProject:sender
|
||||
{
|
||||
[startproject_i setStringValue: [project_i currentProjectFile]];
|
||||
[self UIChanged: self];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (char *)getProjectPath
|
||||
{
|
||||
return projectpath;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//===============================================
|
||||
// BSP sound stuff
|
||||
//===============================================
|
||||
//
|
||||
// Set the BSP sound using an OpenPanel
|
||||
//
|
||||
- setBspSound:sender
|
||||
{
|
||||
id panel;
|
||||
char *types[]={"snd",NULL};
|
||||
int rtn;
|
||||
char **filename;
|
||||
char path[1024], file[64];
|
||||
|
||||
panel = [OpenPanel new];
|
||||
|
||||
ExtractFilePath (bspSound, path);
|
||||
ExtractFileBase (bspSound, file);
|
||||
|
||||
rtn = [panel
|
||||
runModalForDirectory:path
|
||||
file: file
|
||||
types: types];
|
||||
|
||||
if (rtn)
|
||||
{
|
||||
filename = (char **)[panel filenames];
|
||||
strcpy(bspSound,[panel directory]);
|
||||
strcat(bspSound,"/");
|
||||
strcat(bspSound,filename[0]);
|
||||
[self setBspSoundPath:bspSound];
|
||||
[self playBspSound];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Play the BSP sound
|
||||
//
|
||||
- playBspSound
|
||||
{
|
||||
[bspSound_i play];
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Set the bspSound path
|
||||
//
|
||||
- setBspSoundPath:(char *)path
|
||||
{
|
||||
if (!path)
|
||||
path = "";
|
||||
strcpy(bspSound,path);
|
||||
|
||||
if (bspSound_i)
|
||||
[bspSound_i free];
|
||||
bspSound_i = [[Sound alloc] initFromSoundfile:bspSound];
|
||||
if (!bspSound_i)
|
||||
{
|
||||
strcpy (bspSound, "/NextLibrary/Sounds/Funk.snd");
|
||||
bspSound_i = [[Sound alloc] initFromSoundfile:bspSound];
|
||||
}
|
||||
|
||||
[bspSoundField_i setStringValue:bspSound];
|
||||
|
||||
WriteStringDefault ("BspSoundPath", bspSound);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//===============================================
|
||||
// Show BSP Output management
|
||||
//===============================================
|
||||
|
||||
//
|
||||
// Set the state
|
||||
//
|
||||
- setShowBSP:(int)state
|
||||
{
|
||||
showBSP = state;
|
||||
[showBSP_i setIntValue:state];
|
||||
WriteNumericDefault ("ShowBSPOutput", showBSP);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Get the state
|
||||
//
|
||||
- (int)getShowBSP
|
||||
{
|
||||
return showBSP;
|
||||
}
|
||||
|
||||
|
||||
//===============================================
|
||||
// "Offset Brush ..." management
|
||||
//===============================================
|
||||
|
||||
//
|
||||
// Set the state
|
||||
//
|
||||
- setBrushOffset:(int)state
|
||||
{
|
||||
brushOffset = state;
|
||||
[brushOffset_i setIntValue:state];
|
||||
WriteNumericDefault ("OffsetBrushCopy", state);
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Get the state
|
||||
//
|
||||
- (int)getBrushOffset
|
||||
{
|
||||
return brushOffset;
|
||||
}
|
||||
|
||||
//===============================================
|
||||
// StartWad
|
||||
//===============================================
|
||||
|
||||
- setStartWad:(int)value // set start wad (0-2)
|
||||
{
|
||||
startwad = value;
|
||||
if (startwad<0 || startwad>2)
|
||||
startwad = 0;
|
||||
|
||||
[startwad_i selectCellAt:startwad : 0];
|
||||
|
||||
WriteNumericDefault ("StartWad", value);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (int)getStartWad
|
||||
{
|
||||
return startwad;
|
||||
}
|
||||
|
||||
|
||||
//===============================================
|
||||
// X,Y,Z light values
|
||||
//===============================================
|
||||
//
|
||||
// Set the state
|
||||
//
|
||||
- setXlight:(float)value
|
||||
{
|
||||
xlight = value;
|
||||
if (xlight < 0.25 || xlight > 1)
|
||||
xlight = 0.6;
|
||||
lightaxis[1] = xlight;
|
||||
[xlight_i setFloatValue:xlight];
|
||||
WriteNumericDefault ("Xlight", xlight);
|
||||
return self;
|
||||
}
|
||||
- setYlight:(float)value
|
||||
{
|
||||
ylight = value;
|
||||
if (ylight < 0.25 || ylight > 1)
|
||||
ylight = 0.75;
|
||||
lightaxis[2] = ylight;
|
||||
[ylight_i setFloatValue:ylight];
|
||||
WriteNumericDefault ("Ylight", ylight);
|
||||
return self;
|
||||
}
|
||||
- setZlight:(float)value
|
||||
{
|
||||
zlight = value;
|
||||
if (zlight < 0.25 || zlight > 1)
|
||||
zlight = 1;
|
||||
lightaxis[0] = zlight;
|
||||
[zlight_i setFloatValue:zlight];
|
||||
WriteNumericDefault ("Zlight", zlight);
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Get the state
|
||||
//
|
||||
- (float)getXlight
|
||||
{
|
||||
return [xlight_i floatValue];
|
||||
}
|
||||
- (float)getYlight
|
||||
{
|
||||
return [ylight_i floatValue];
|
||||
}
|
||||
- (float)getZlight
|
||||
{
|
||||
return [zlight_i floatValue];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
UIChanged
|
||||
|
||||
Grab all the current UI state
|
||||
============
|
||||
*/
|
||||
-UIChanged: sender
|
||||
{
|
||||
qprintf ("defaults updated");
|
||||
|
||||
[self setProjectPath: (char *)[startproject_i stringValue]];
|
||||
[self setBspSoundPath: (char *)[bspSoundField_i stringValue]];
|
||||
[self setShowBSP: [showBSP_i intValue]];
|
||||
[self setBrushOffset: [brushOffset_i intValue]];
|
||||
[self setStartWad: [startwad_i selectedRow]];
|
||||
[self setXlight: [xlight_i floatValue]];
|
||||
[self setYlight: [ylight_i floatValue]];
|
||||
[self setZlight: [zlight_i floatValue]];
|
||||
|
||||
[map_i makeGlobalPerform: @selector(flushTextures)];
|
||||
[quakeed_i updateAll];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@end
|
108
tools/Forge/Bundles/MapEdit/Project.h
Normal file
108
tools/Forge/Bundles/MapEdit/Project.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
|
||||
#import <AppKit/AppKit.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define BASEPATHKEY "basepath"
|
||||
#define MAPNAMESKEY "maps"
|
||||
#define DESCKEY "desc"
|
||||
#define WADSKEY "wads"
|
||||
#define BSPFULLVIS "bspfullvis"
|
||||
#define BSPFASTVIS "bspfastvis"
|
||||
#define BSPNOVIS "bspnovis"
|
||||
#define BSPRELIGHT "bsprelight"
|
||||
#define BSPLEAKTEST "bspleaktest"
|
||||
#define BSPENTITIES "bspentities"
|
||||
|
||||
#define SUBDIR_ENT "progs" // subdir names in heirarchy
|
||||
#define SUBDIR_MAPS "maps"
|
||||
#define SUBDIR_GFX "gfx"
|
||||
|
||||
extern id project_i;
|
||||
|
||||
@interface Project:Object
|
||||
{
|
||||
id projectInfo; // dictionary storage of project info
|
||||
|
||||
id basepathinfo_i; // outlet to base path info textfield
|
||||
id mapbrowse_i; // outlet to QuakeEd Maps browser
|
||||
id currentmap_i; // outlet to current map textfield
|
||||
id mapList; // list of map names (Storage)
|
||||
id descList; // list of map descriptions (Storage)
|
||||
id wadList; // list of wad names (Storage)
|
||||
|
||||
id pis_panel_i; // outlet to Project Info Settings (PIS) panel
|
||||
|
||||
id pis_basepath_i; // outlet to PIS->base path
|
||||
id pis_wads_i; // outlet to PIS->wad browser
|
||||
id pis_fullvis_i; // outlet to PIS->full vis command
|
||||
id pis_fastvis_i; // outlet to PIS->fast vis command
|
||||
id pis_novis_i; // outlet to PIS->no vis command
|
||||
id pis_relight_i; // outlet to PIS->relight command
|
||||
id pis_leaktest_i; // outlet to PIS->leak test command
|
||||
|
||||
id BSPoutput_i; // outlet to Text
|
||||
|
||||
char path_projectinfo[128]; // path of QE_Project file
|
||||
|
||||
char path_basepath[128]; // base path of heirarchy
|
||||
|
||||
char path_progdir[128]; // derived from basepath
|
||||
char path_mapdirectory[128]; // derived from basepath
|
||||
char path_finalmapdir[128]; // derived from basepath
|
||||
|
||||
char path_wad8[128]; // path of texture WAD for cmd-8 key
|
||||
char path_wad9[128]; // path of texture WAD for cmd-9 key
|
||||
char path_wad0[128]; // path of texture WAD for cmd-0 key
|
||||
|
||||
char string_fullvis[1024]; // cmd-line parm
|
||||
char string_fastvis[1024]; // cmd-line parm
|
||||
char string_novis[1024]; // cmd-line parm
|
||||
char string_relight[1024]; // cmd-line parm
|
||||
char string_leaktest[1024]; // cmd-line parm
|
||||
char string_entities[1024]; // cmd-line parm
|
||||
|
||||
int showDescriptions; // 1 = show map descs in browser
|
||||
|
||||
time_t lastModified; // last time project file was modified
|
||||
}
|
||||
|
||||
- initProject;
|
||||
- initVars;
|
||||
|
||||
- (char *)currentProjectFile;
|
||||
|
||||
- setTextureWad: (char *)wf;
|
||||
|
||||
- addToOutput:(char *)string;
|
||||
- clearBspOutput:sender;
|
||||
- initProjSettings;
|
||||
- changeChar:(char)f to:(char)t in:(id)obj;
|
||||
- (int)searchForString:(char *)str in:(id)obj;
|
||||
|
||||
- parseProjectFile; // read defaultsdatabase for project path
|
||||
- openProjectFile:(char *)path; // called by openProject and newProject
|
||||
- openProject;
|
||||
- clickedOnMap:sender; // called if clicked on map in browser
|
||||
- clickedOnWad:sender; // called if clicked on wad in browser
|
||||
|
||||
// methods to querie the project file
|
||||
|
||||
- (char *)getMapDirectory;
|
||||
- (char *)getFinalMapDirectory;
|
||||
- (char *)getProgDirectory;
|
||||
|
||||
- (char *)getWAD8;
|
||||
- (char *)getWAD9;
|
||||
- (char *)getWAD0;
|
||||
|
||||
- (char *)getFullVisCmd;
|
||||
- (char *)getFastVisCmd;
|
||||
- (char *)getNoVisCmd;
|
||||
- (char *)getRelightCmd;
|
||||
- (char *)getLeaktestCmd;
|
||||
- (char *)getEntitiesCmd;
|
||||
|
||||
@end
|
||||
|
||||
void changeString(char cf,char ct,char *string);
|
||||
|
526
tools/Forge/Bundles/MapEdit/Project.m
Normal file
526
tools/Forge/Bundles/MapEdit/Project.m
Normal file
|
@ -0,0 +1,526 @@
|
|||
//======================================
|
||||
//
|
||||
// QuakeEd Project Management
|
||||
//
|
||||
//======================================
|
||||
|
||||
#import "qedefs.h"
|
||||
|
||||
|
||||
id project_i;
|
||||
|
||||
@implementation Project
|
||||
|
||||
- init
|
||||
{
|
||||
project_i = self;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
//
|
||||
// Project code
|
||||
//
|
||||
//===========================================================
|
||||
- initVars
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = [preferences_i getProjectPath];
|
||||
StripFilename(s);
|
||||
strcpy(path_basepath,s);
|
||||
|
||||
strcpy(path_progdir,s);
|
||||
strcat(path_progdir,"/"SUBDIR_ENT);
|
||||
|
||||
strcpy(path_mapdirectory,s);
|
||||
strcat(path_mapdirectory,"/"SUBDIR_MAPS); // source dir
|
||||
|
||||
strcpy(path_finalmapdir,s);
|
||||
strcat(path_finalmapdir,"/"SUBDIR_MAPS); // dest dir
|
||||
|
||||
[basepathinfo_i setStringValue:s]; // in Project Inspector
|
||||
|
||||
#if 0
|
||||
if ((s = [projectInfo getStringFor:BASEPATHKEY]))
|
||||
{
|
||||
strcpy(path_basepath,s);
|
||||
|
||||
strcpy(path_progdir,s);
|
||||
strcat(path_progdir,"/"SUBDIR_ENT);
|
||||
|
||||
strcpy(path_mapdirectory,s);
|
||||
strcat(path_mapdirectory,"/"SUBDIR_MAPS); // source dir
|
||||
|
||||
strcpy(path_finalmapdir,s);
|
||||
strcat(path_finalmapdir,"/"SUBDIR_MAPS); // dest dir
|
||||
|
||||
[basepathinfo_i setStringValue:s]; // in Project Inspector
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((s = [projectInfo getStringFor:BSPFULLVIS]))
|
||||
{
|
||||
strcpy(string_fullvis,s);
|
||||
changeString('@','\"',string_fullvis);
|
||||
}
|
||||
|
||||
if ((s = [projectInfo getStringFor:BSPFASTVIS]))
|
||||
{
|
||||
strcpy(string_fastvis,s);
|
||||
changeString('@','\"',string_fastvis);
|
||||
}
|
||||
|
||||
if ((s = [projectInfo getStringFor:BSPNOVIS]))
|
||||
{
|
||||
strcpy(string_novis,s);
|
||||
changeString('@','\"',string_novis);
|
||||
}
|
||||
|
||||
if ((s = [projectInfo getStringFor:BSPRELIGHT]))
|
||||
{
|
||||
strcpy(string_relight,s);
|
||||
changeString('@','\"',string_relight);
|
||||
}
|
||||
|
||||
if ((s = [projectInfo getStringFor:BSPLEAKTEST]))
|
||||
{
|
||||
strcpy(string_leaktest,s);
|
||||
changeString('@','\"',string_leaktest);
|
||||
}
|
||||
|
||||
if ((s = [projectInfo getStringFor:BSPENTITIES]))
|
||||
{
|
||||
strcpy(string_entities,s);
|
||||
changeString('@','\"', string_entities);
|
||||
}
|
||||
|
||||
// Build list of wads
|
||||
wadList = [projectInfo parseMultipleFrom:WADSKEY];
|
||||
|
||||
// Build list of maps & descriptions
|
||||
mapList = [projectInfo parseMultipleFrom:MAPNAMESKEY];
|
||||
descList = [projectInfo parseMultipleFrom:DESCKEY];
|
||||
[self changeChar:'_' to:' ' in:descList];
|
||||
|
||||
[self initProjSettings];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Init Project Settings fields
|
||||
//
|
||||
- initProjSettings
|
||||
{
|
||||
[pis_basepath_i setStringValue:path_basepath];
|
||||
[pis_fullvis_i setStringValue:string_fullvis];
|
||||
[pis_fastvis_i setStringValue:string_fastvis];
|
||||
[pis_novis_i setStringValue:string_novis];
|
||||
[pis_relight_i setStringValue:string_relight];
|
||||
[pis_leaktest_i setStringValue:string_leaktest];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Add text to the BSP Output window
|
||||
//
|
||||
- addToOutput:(char *)string
|
||||
{
|
||||
int end;
|
||||
|
||||
end = [BSPoutput_i textLength];
|
||||
[BSPoutput_i setSel:end :end];
|
||||
[BSPoutput_i replaceSel:string];
|
||||
|
||||
end = [BSPoutput_i textLength];
|
||||
[BSPoutput_i setSel:end :end];
|
||||
[BSPoutput_i scrollSelToVisible];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- clearBspOutput:sender
|
||||
{
|
||||
[BSPoutput_i selectAll:self];
|
||||
[BSPoutput_i replaceSel:"\0"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- print
|
||||
{
|
||||
[BSPoutput_i printPSCode:self];
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- initProject
|
||||
{
|
||||
[self parseProjectFile];
|
||||
if (projectInfo == NULL)
|
||||
return self;
|
||||
[self initVars];
|
||||
[mapbrowse_i reuseColumns:YES];
|
||||
[mapbrowse_i loadColumnZero];
|
||||
[pis_wads_i reuseColumns:YES];
|
||||
[pis_wads_i loadColumnZero];
|
||||
|
||||
[things_i initEntities];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Change a character to another in a Storage list of strings
|
||||
//
|
||||
- changeChar:(char)f to:(char)t in:(id)obj
|
||||
{
|
||||
int i;
|
||||
int max;
|
||||
char *string;
|
||||
|
||||
max = [obj count];
|
||||
for (i = 0;i < max;i++)
|
||||
{
|
||||
string = [obj elementAt:i];
|
||||
changeString(f,t,string);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Fill the QuakeEd Maps or wads browser
|
||||
// (Delegate method - delegated in Interface Builder)
|
||||
//
|
||||
- (int)browser:sender fillMatrix:matrix inColumn:(int)column
|
||||
{
|
||||
id cell, list;
|
||||
int max;
|
||||
char *name;
|
||||
int i;
|
||||
|
||||
if (sender == mapbrowse_i)
|
||||
list = mapList;
|
||||
else if (sender == pis_wads_i)
|
||||
list = wadList;
|
||||
else
|
||||
{
|
||||
list = nil;
|
||||
Error ("Project: unknown browser to fill");
|
||||
}
|
||||
|
||||
max = [list count];
|
||||
for (i = 0 ; i<max ; i++)
|
||||
{
|
||||
name = [list elementAt:i];
|
||||
[matrix addRow];
|
||||
cell = [matrix cellAt:i :0];
|
||||
[cell setStringValue:name];
|
||||
[cell setLeaf:YES];
|
||||
[cell setLoaded:YES];
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
//
|
||||
// Clicked on a map name or description!
|
||||
//
|
||||
- clickedOnMap:sender
|
||||
{
|
||||
id matrix;
|
||||
int row;
|
||||
char fname[1024];
|
||||
id panel;
|
||||
|
||||
matrix = [sender matrixInColumn:0];
|
||||
row = [matrix selectedRow];
|
||||
sprintf(fname,"%s/%s.map",path_mapdirectory,
|
||||
(char *)[mapList elementAt:row]);
|
||||
|
||||
panel = NSGetAlertPanel("Loading...",
|
||||
"Loading map. Please wait.",NULL,NULL,NULL);
|
||||
[panel orderFront:NULL];
|
||||
|
||||
[quakeed_i doOpen:fname];
|
||||
|
||||
[panel performClose:NULL];
|
||||
NSFreeAlertPanel(panel);
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- setTextureWad: (char *)wf
|
||||
{
|
||||
int i, c;
|
||||
char *name;
|
||||
|
||||
qprintf ("loading %s", wf);
|
||||
|
||||
// set the row in the settings inspector wad browser
|
||||
c = [wadList count];
|
||||
for (i=0 ; i<c ; i++)
|
||||
{
|
||||
name = (char *)[wadList elementAt:i];
|
||||
if (!strcmp(name, wf))
|
||||
{
|
||||
[[pis_wads_i matrixInColumn:0] selectCellAt: i : 0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// update the texture inspector
|
||||
[texturepalette_i initPaletteFromWadfile:wf ];
|
||||
[[map_i objectAt: 0] setKey:"wad" toValue: wf];
|
||||
// [inspcontrol_i changeInspectorTo:i_textures];
|
||||
|
||||
[quakeed_i updateAll];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Clicked on a wad name
|
||||
//
|
||||
- clickedOnWad:sender
|
||||
{
|
||||
id matrix;
|
||||
int row;
|
||||
char *name;
|
||||
|
||||
matrix = [sender matrixInColumn:0];
|
||||
row = [matrix selectedRow];
|
||||
|
||||
name = (char *)[wadList elementAt:row];
|
||||
[self setTextureWad: name];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Read in the <name>.QE_Project file
|
||||
//
|
||||
- parseProjectFile
|
||||
{
|
||||
char *path;
|
||||
int rtn;
|
||||
|
||||
path = [preferences_i getProjectPath];
|
||||
if (!path || !path[0] || access(path,0))
|
||||
{
|
||||
rtn = NSRunAlertPanel("Project Error!",
|
||||
"A default project has not been found.\n"
|
||||
, "Open Project", NULL, NULL);
|
||||
if ([self openProject] == nil)
|
||||
while (1) // can't run without a project
|
||||
[NSApp terminate: self];
|
||||
return self;
|
||||
}
|
||||
|
||||
[self openProjectFile:path];
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Loads and parses a project file
|
||||
//
|
||||
- openProjectFile:(char *)path
|
||||
{
|
||||
FILE *fp;
|
||||
struct stat s;
|
||||
|
||||
strcpy(path_projectinfo,path);
|
||||
|
||||
projectInfo = NULL;
|
||||
fp = fopen(path,"r+t");
|
||||
if (fp == NULL)
|
||||
return self;
|
||||
|
||||
stat(path,&s);
|
||||
lastModified = s.st_mtime;
|
||||
|
||||
projectInfo = [(Dict *)[Dict alloc] initFromFile:fp];
|
||||
fclose(fp);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (char *)currentProjectFile
|
||||
{
|
||||
return path_projectinfo;
|
||||
}
|
||||
|
||||
//
|
||||
// Open a project file
|
||||
//
|
||||
- openProject
|
||||
{
|
||||
char path[128];
|
||||
id openpanel;
|
||||
int rtn;
|
||||
char *projtypes[2] = {"qpr",NULL};
|
||||
char **filenames;
|
||||
char *dir;
|
||||
|
||||
openpanel = [OpenPanel new];
|
||||
[openpanel allowMultipleFiles:NO];
|
||||
[openpanel chooseDirectories:NO];
|
||||
rtn = [openpanel runModalForTypes:projtypes];
|
||||
if (rtn == NS_OKTAG)
|
||||
{
|
||||
(const char *const *)filenames = [openpanel filenames];
|
||||
dir = (char *)[openpanel directory];
|
||||
sprintf(path,"%s/%s",dir,filenames[0]);
|
||||
strcpy(path_projectinfo,path);
|
||||
[self openProjectFile:path];
|
||||
return self;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Search for a string in a List of strings
|
||||
//
|
||||
- (int)searchForString:(char *)str in:(id)obj
|
||||
{
|
||||
int i;
|
||||
int max;
|
||||
char *s;
|
||||
|
||||
max = [obj count];
|
||||
for (i = 0;i < max; i++)
|
||||
{
|
||||
s = (char *)[obj elementAt:i];
|
||||
if (!strcmp(s,str))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (char *)getMapDirectory
|
||||
{
|
||||
return path_mapdirectory;
|
||||
}
|
||||
|
||||
- (char *)getFinalMapDirectory
|
||||
{
|
||||
return path_finalmapdir;
|
||||
}
|
||||
|
||||
- (char *)getProgDirectory
|
||||
{
|
||||
return path_progdir;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return the WAD name for cmd-8
|
||||
//
|
||||
- (char *)getWAD8
|
||||
{
|
||||
if (!path_wad8[0])
|
||||
return NULL;
|
||||
return path_wad8;
|
||||
}
|
||||
|
||||
//
|
||||
// Return the WAD name for cmd-9
|
||||
//
|
||||
- (char *)getWAD9
|
||||
{
|
||||
if (!path_wad9[0])
|
||||
return NULL;
|
||||
return path_wad9;
|
||||
}
|
||||
|
||||
//
|
||||
// Return the WAD name for cmd-0
|
||||
//
|
||||
- (char *)getWAD0
|
||||
{
|
||||
if (!path_wad0[0])
|
||||
return NULL;
|
||||
return path_wad0;
|
||||
}
|
||||
|
||||
//
|
||||
// Return the FULLVIS cmd string
|
||||
//
|
||||
- (char *)getFullVisCmd
|
||||
{
|
||||
if (!string_fullvis[0])
|
||||
return NULL;
|
||||
return string_fullvis;
|
||||
}
|
||||
|
||||
//
|
||||
// Return the FASTVIS cmd string
|
||||
//
|
||||
- (char *)getFastVisCmd
|
||||
{
|
||||
if (!string_fastvis[0])
|
||||
return NULL;
|
||||
return string_fastvis;
|
||||
}
|
||||
|
||||
//
|
||||
// Return the NOVIS cmd string
|
||||
//
|
||||
- (char *)getNoVisCmd
|
||||
{
|
||||
if (!string_novis[0])
|
||||
return NULL;
|
||||
return string_novis;
|
||||
}
|
||||
|
||||
//
|
||||
// Return the RELIGHT cmd string
|
||||
//
|
||||
- (char *)getRelightCmd
|
||||
{
|
||||
if (!string_relight[0])
|
||||
return NULL;
|
||||
return string_relight;
|
||||
}
|
||||
|
||||
//
|
||||
// Return the LEAKTEST cmd string
|
||||
//
|
||||
- (char *)getLeaktestCmd
|
||||
{
|
||||
if (!string_leaktest[0])
|
||||
return NULL;
|
||||
return string_leaktest;
|
||||
}
|
||||
|
||||
- (char *)getEntitiesCmd
|
||||
{
|
||||
if (!string_entities[0])
|
||||
return NULL;
|
||||
return string_entities;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//====================================================
|
||||
// C Functions
|
||||
//====================================================
|
||||
|
||||
//
|
||||
// Change a character to a different char in a string
|
||||
//
|
||||
void changeString(char cf,char ct,char *string)
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0;j < strlen(string);j++)
|
||||
if (string[j] == cf)
|
||||
string[j] = ct;
|
||||
}
|
||||
|
||||
|
98
tools/Forge/Bundles/MapEdit/QuakeEd.h
Normal file
98
tools/Forge/Bundles/MapEdit/QuakeEd.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
|
||||
extern id quakeed_i;
|
||||
|
||||
extern BOOL filter_light, filter_path, filter_entities;
|
||||
extern BOOL filter_clip_brushes, filter_water_brushes, filter_world;
|
||||
|
||||
extern UserPath *upath;
|
||||
|
||||
extern id g_cmd_out_i;
|
||||
|
||||
double I_FloatTime (void);
|
||||
|
||||
void NopSound (void);
|
||||
|
||||
void qprintf (char *fmt, ...); // prints text to cmd_out_i
|
||||
|
||||
@interface QuakeEd : NSWindow
|
||||
{
|
||||
BOOL dirty;
|
||||
char filename[1024]; // full path with .map extension
|
||||
|
||||
// UI objects
|
||||
id brushcount_i;
|
||||
id entitycount_i;
|
||||
id regionbutton_i;
|
||||
|
||||
id show_coordinates_i;
|
||||
id show_names_i;
|
||||
|
||||
id filter_light_i;
|
||||
id filter_path_i;
|
||||
id filter_entities_i;
|
||||
id filter_clip_i;
|
||||
id filter_water_i;
|
||||
id filter_world_i;
|
||||
|
||||
id cmd_in_i; // text fields
|
||||
id cmd_out_i;
|
||||
|
||||
id xy_drawmode_i; // passed over to xyview after init
|
||||
}
|
||||
|
||||
- setDefaultFilename;
|
||||
- (char *)currentFilename;
|
||||
|
||||
- updateAll; // when a model has been changed
|
||||
- updateCamera; // when the camera has moved
|
||||
- updateXY;
|
||||
- updateZ;
|
||||
|
||||
- updateAll:sender;
|
||||
|
||||
- newinstance; // force next flushwindow to clear all instance drawing
|
||||
- redrawInstance; // erase and redraw all instance now
|
||||
|
||||
- appDidInit:sender;
|
||||
- appWillTerminate:sender;
|
||||
|
||||
- openProject:sender;
|
||||
|
||||
- textCommand: sender;
|
||||
|
||||
- applyRegion: sender;
|
||||
|
||||
- (BOOL)dirty;
|
||||
|
||||
- clear: sender;
|
||||
- centerCamera: sender;
|
||||
- centerZChecker: sender;
|
||||
|
||||
- changeXYLookUp: sender;
|
||||
|
||||
- setBrushRegion: sender;
|
||||
- setXYRegion: sender;
|
||||
|
||||
- open: sender;
|
||||
- save: sender;
|
||||
- saveAs: sender;
|
||||
|
||||
- doOpen: (char *)fname;
|
||||
|
||||
- saveBSP:(char *)cmdline dialog:(BOOL)wt;
|
||||
|
||||
- BSP_Full: sender;
|
||||
- BSP_FastVis: sender;
|
||||
- BSP_NoVis: sender;
|
||||
- BSP_relight: sender;
|
||||
- BSP_stop: sender;
|
||||
- BSP_entities: sender;
|
||||
|
||||
//
|
||||
// UI querie for other objects
|
||||
//
|
||||
- (BOOL)showCoordinates;
|
||||
- (BOOL)showNames;
|
||||
|
||||
@end
|
||||
|
1026
tools/Forge/Bundles/MapEdit/QuakeEd.m
Normal file
1026
tools/Forge/Bundles/MapEdit/QuakeEd.m
Normal file
File diff suppressed because it is too large
Load diff
15
tools/Forge/Bundles/MapEdit/QuakeEd_main.m
Normal file
15
tools/Forge/Bundles/MapEdit/QuakeEd_main.m
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* Generated by the NeXT Project Builder
|
||||
NOTE: Do NOT change this file -- Project Builder maintains it.
|
||||
*/
|
||||
|
||||
#import <appkit/appkit.h>
|
||||
|
||||
void main(int argc, char *argv[]) {
|
||||
|
||||
[Application new];
|
||||
if ([NSApp loadNibSection:"QuakeEd.nib" owner:NSApp withNames:NO])
|
||||
[NSApp run];
|
||||
|
||||
[NSApp free];
|
||||
exit(0);
|
||||
}
|
158
tools/Forge/Bundles/MapEdit/SetBrush.h
Normal file
158
tools/Forge/Bundles/MapEdit/SetBrush.h
Normal file
|
@ -0,0 +1,158 @@
|
|||
|
||||
|
||||
#define MAX_FACES 16
|
||||
|
||||
typedef float vec5_t[5];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int numpoints;
|
||||
vec5_t points[8]; // variable sized
|
||||
} winding_t;
|
||||
|
||||
#define MAX_POINTS_ON_WINDING 64
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec3_t normal;
|
||||
float dist;
|
||||
} plane_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// implicit rep
|
||||
vec3_t planepts[3];
|
||||
texturedef_t texture;
|
||||
|
||||
// cached rep
|
||||
plane_t plane;
|
||||
qtexture_t *qtexture;
|
||||
float light; // 0 - 1.0
|
||||
winding_t *w;
|
||||
} face_t;
|
||||
|
||||
#define ON_EPSILON 0.1
|
||||
#define FP_EPSILON 0.01
|
||||
#define VECTOR_EPSILON 0.0001
|
||||
|
||||
#define SIDE_FRONT 0
|
||||
#define SIDE_BACK 1
|
||||
#define SIDE_ON 2
|
||||
|
||||
|
||||
winding_t *ClipWinding (winding_t *in, plane_t *split);
|
||||
winding_t *CopyWinding (winding_t *w);
|
||||
winding_t *NewWinding (int points);
|
||||
|
||||
|
||||
@interface SetBrush : Object
|
||||
{
|
||||
BOOL regioned; // not active
|
||||
BOOL selected;
|
||||
|
||||
BOOL invalid; // not a proper polyhedron
|
||||
|
||||
id parent; // the entity this brush is in
|
||||
vec3_t bmins, bmaxs;
|
||||
vec3_t entitycolor;
|
||||
int numfaces;
|
||||
face_t faces[MAX_FACES];
|
||||
}
|
||||
|
||||
- initOwner: own mins:(float *)mins maxs:(float *)maxs texture:(texturedef_t *)tex;
|
||||
- initFromTokens: own;
|
||||
- setMins:(float *)mins maxs:(float *)maxs;
|
||||
|
||||
- parent;
|
||||
- setParent: (id)p;
|
||||
|
||||
- setEntityColor: (vec3_t)color;
|
||||
|
||||
- calcWindings;
|
||||
|
||||
- writeToFILE: (FILE *)f region: (BOOL)reg;
|
||||
|
||||
- (BOOL)selected;
|
||||
- (BOOL)regioned;
|
||||
- setSelected: (BOOL)s;
|
||||
- setRegioned: (BOOL)s;
|
||||
|
||||
- getMins: (vec3_t)mins maxs: (vec3_t)maxs;
|
||||
|
||||
- (BOOL)containsPoint: (vec3_t)pt;
|
||||
|
||||
- freeWindings;
|
||||
- removeIfInvalid;
|
||||
|
||||
extern vec3_t region_min, region_max;
|
||||
- newRegion;
|
||||
|
||||
- (texturedef_t *)texturedef;
|
||||
- (texturedef_t *)texturedefForFace: (int)f;
|
||||
- setTexturedef: (texturedef_t *)tex;
|
||||
- setTexturedef: (texturedef_t *)tex forFace:(int)f;
|
||||
|
||||
- XYDrawSelf;
|
||||
- ZDrawSelf;
|
||||
- CameraDrawSelf;
|
||||
- XYRenderSelf;
|
||||
- CameraRenderSelf;
|
||||
|
||||
- hitByRay: (vec3_t)p1 : (vec3_t) p2 : (float *)time : (int *)face;
|
||||
|
||||
//
|
||||
// single brush actions
|
||||
//
|
||||
extern int numcontrolpoints;
|
||||
extern float *controlpoints[MAX_FACES*3];
|
||||
- getZdragface: (vec3_t)dragpoint;
|
||||
- getXYdragface: (vec3_t)dragpoint;
|
||||
- getXYShearPoints: (vec3_t)dragpoint;
|
||||
|
||||
- addFace: (face_t *)f;
|
||||
|
||||
//
|
||||
// multiple brush actions
|
||||
//
|
||||
- carveByClipper;
|
||||
|
||||
extern vec3_t sb_translate;
|
||||
- translate;
|
||||
|
||||
extern id carve_in, carve_out;
|
||||
- select;
|
||||
- deselect;
|
||||
- remove;
|
||||
- flushTextures;
|
||||
|
||||
extern vec3_t sb_mins, sb_maxs;
|
||||
- addToBBox;
|
||||
|
||||
extern vec3_t sel_x, sel_y, sel_z;
|
||||
extern vec3_t sel_org;
|
||||
- transform;
|
||||
|
||||
- flipNormals;
|
||||
|
||||
- carve;
|
||||
- setCarveVars;
|
||||
|
||||
extern id sb_newowner;
|
||||
- moveToEntity;
|
||||
|
||||
- takeCurrentTexture;
|
||||
|
||||
extern vec3_t select_min, select_max;
|
||||
- selectPartial;
|
||||
- selectComplete;
|
||||
- regionPartial;
|
||||
- regionComplete;
|
||||
|
||||
extern float sb_floor_dir, sb_floor_dist;
|
||||
- feetToFloor;
|
||||
|
||||
- (int) getNumBrushFaces;
|
||||
- (face_t *)getBrushFace: (int)which;
|
||||
|
||||
@end
|
||||
|
2034
tools/Forge/Bundles/MapEdit/SetBrush.m
Normal file
2034
tools/Forge/Bundles/MapEdit/SetBrush.m
Normal file
File diff suppressed because it is too large
Load diff
113
tools/Forge/Bundles/MapEdit/TexturePalette.h
Normal file
113
tools/Forge/Bundles/MapEdit/TexturePalette.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
|
||||
typedef union
|
||||
{
|
||||
byte chan[4];
|
||||
unsigned p;
|
||||
} pixel32_t;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char texture[16];
|
||||
float rotate;
|
||||
float shift[2];
|
||||
float scale[2];
|
||||
} texturedef_t;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[16];
|
||||
|
||||
int width;
|
||||
int height;
|
||||
NSBitmapImageRep *rep;
|
||||
void *data;
|
||||
pixel32_t flatcolor;
|
||||
} qtexture_t;
|
||||
|
||||
#define MAX_TEXTURES 1024
|
||||
|
||||
extern int tex_count;
|
||||
extern qtexture_t qtextures[MAX_TEXTURES];
|
||||
|
||||
void TEX_InitFromWad (char *path);
|
||||
qtexture_t *TEX_ForName (char *name);
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
id image; // NSImage
|
||||
NSRect r;
|
||||
char *name;
|
||||
int index;
|
||||
int display; // flag (on/off)
|
||||
} texpal_t;
|
||||
|
||||
#define TEX_INDENT 10
|
||||
#define TEX_SPACING 16
|
||||
|
||||
extern id texturepalette_i;
|
||||
|
||||
@interface TexturePalette:Object
|
||||
{
|
||||
char currentwad[1024];
|
||||
id textureList_i;
|
||||
id textureView_i;
|
||||
id searchField_i;
|
||||
id sizeField_i;
|
||||
|
||||
id field_Xshift_i;
|
||||
id field_Yshift_i;
|
||||
id field_Xscale_i;
|
||||
id field_Yscale_i;
|
||||
id field_Rotate_i;
|
||||
|
||||
int viewWidth;
|
||||
int viewHeight;
|
||||
int selectedTexture;
|
||||
}
|
||||
|
||||
- (char*)currentWad;
|
||||
- initPaletteFromWadfile:(char *)wf;
|
||||
- computeTextureViewSize;
|
||||
- alphabetize;
|
||||
- getList;
|
||||
- (int)getSelectedTexture;
|
||||
- setSelectedTexture:(int)which;
|
||||
- (int)getSelectedTexIndex;
|
||||
|
||||
// Called externally
|
||||
- (char *)getSelTextureName;
|
||||
- setTextureByName:(char *)name;
|
||||
|
||||
// New methods to replace the 2 above ones
|
||||
- setTextureDef:(texturedef_t *)td;
|
||||
- getTextureDef:(texturedef_t *)td;
|
||||
|
||||
// Action methods
|
||||
- searchForTexture:sender;
|
||||
|
||||
- clearTexinfo: sender;
|
||||
|
||||
- incXShift:sender;
|
||||
- decXShift:sender;
|
||||
|
||||
- incYShift:sender;
|
||||
- decYShift:sender;
|
||||
|
||||
- incRotate: sender;
|
||||
- decRotate: sender;
|
||||
|
||||
- incXScale:sender;
|
||||
- decXScale:sender;
|
||||
|
||||
- incYScale:sender;
|
||||
- decYScale:sender;
|
||||
|
||||
- texturedefChanged: sender;
|
||||
- onlyShowMapTextures:sender;
|
||||
- (int) searchForTextureInPalette:(char *)texture;
|
||||
- setDisplayFlag:(int)index to:(int)value;
|
||||
|
||||
@end
|
818
tools/Forge/Bundles/MapEdit/TexturePalette.m
Normal file
818
tools/Forge/Bundles/MapEdit/TexturePalette.m
Normal file
|
@ -0,0 +1,818 @@
|
|||
|
||||
#import "qedefs.h"
|
||||
|
||||
id texturepalette_i;
|
||||
|
||||
|
||||
#define TYP_MIPTEX 67
|
||||
|
||||
int tex_count;
|
||||
qtexture_t qtextures[MAX_TEXTURES];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[16];
|
||||
unsigned width, height;
|
||||
unsigned offsets[4]; // four mip maps stored
|
||||
} miptex_t;
|
||||
|
||||
unsigned tex_palette[256];
|
||||
|
||||
unsigned badtex_d[] =
|
||||
{
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff
|
||||
};
|
||||
|
||||
qtexture_t badtex = {"notexture",16,16,NULL, badtex_d, {0,0,255,255}};
|
||||
|
||||
/*
|
||||
==============
|
||||
TEX_InitPalette
|
||||
==============
|
||||
*/
|
||||
void TEX_InitPalette (byte *pal)
|
||||
{
|
||||
int r,g,b,v;
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<256 ; i++)
|
||||
{
|
||||
r = pal[0];
|
||||
g = pal[1];
|
||||
b = pal[2];
|
||||
pal += 3;
|
||||
|
||||
v = (r<<24) + (g<<16) + (b<<8) + 255;
|
||||
v = BigLong (v);
|
||||
|
||||
tex_palette[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
TEX_ImageFromMiptex
|
||||
=================
|
||||
*/
|
||||
void TEX_ImageFromMiptex (miptex_t *qtex)
|
||||
{
|
||||
NSBitmapImageRep *bm;
|
||||
byte *source;
|
||||
unsigned *dest;
|
||||
int width, height, i, count;
|
||||
qtexture_t *q;
|
||||
int tr, tg, tb;
|
||||
|
||||
width = LittleLong(qtex->width);
|
||||
height = LittleLong(qtex->height);
|
||||
|
||||
bm = [[NSBitmapImageRep alloc]
|
||||
initData: NULL
|
||||
pixelsWide: width
|
||||
pixelsHigh: height
|
||||
bitsPerSample: 8
|
||||
samplesPerPixel:3
|
||||
hasAlpha: NO
|
||||
isPlanar: NO
|
||||
colorSpace: NS_RGBColorSpace
|
||||
bytesPerRow: width*4
|
||||
bitsPerPixel: 32];
|
||||
|
||||
dest = (unsigned *)[bm data];
|
||||
count = width*height;
|
||||
source = (byte *)qtex + LittleLong(qtex->offsets[0]);
|
||||
|
||||
q = &qtextures[tex_count];
|
||||
tex_count++;
|
||||
|
||||
q->width = width;
|
||||
q->height = height;
|
||||
q->rep = bm;
|
||||
q->data = dest;
|
||||
|
||||
tr = tg = tb = 0;
|
||||
|
||||
for (i=0 ; i<count ; i++)
|
||||
{
|
||||
dest[i] = tex_palette[source[i]];
|
||||
tr += ((pixel32_t *)&dest[i])->chan[0];
|
||||
tg += ((pixel32_t *)&dest[i])->chan[1];
|
||||
tb += ((pixel32_t *)&dest[i])->chan[2];
|
||||
}
|
||||
|
||||
q->flatcolor.chan[0] = tr / count;
|
||||
q->flatcolor.chan[1] = tg / count;
|
||||
q->flatcolor.chan[2] = tb / count;
|
||||
q->flatcolor.chan[3] = 0xff;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char identification[4]; // should be WAD2 or 2DAW
|
||||
int numlumps;
|
||||
int infotableofs;
|
||||
} wadinfo_t;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int filepos;
|
||||
int disksize;
|
||||
int size; // uncompressed
|
||||
char type;
|
||||
char compression;
|
||||
char pad1, pad2;
|
||||
char name[16]; // must be null terminated
|
||||
} lumpinfo_t;
|
||||
|
||||
/*
|
||||
=================
|
||||
TEX_InitFromWad
|
||||
=================
|
||||
*/
|
||||
void TEX_InitFromWad (char *path)
|
||||
{
|
||||
int i;
|
||||
char local[1024];
|
||||
char newpath[1024];
|
||||
byte *wadfile;
|
||||
wadinfo_t *wadinfo;
|
||||
lumpinfo_t *lumpinfo;
|
||||
int numlumps;
|
||||
float start, stop;
|
||||
|
||||
start = I_FloatTime ();
|
||||
|
||||
strcpy(newpath, [preferences_i getProjectPath]);
|
||||
strcat(newpath,"/");
|
||||
strcat(newpath, path);
|
||||
|
||||
// free any textures
|
||||
for (i=0 ; i<tex_count ; i++)
|
||||
[qtextures[i].rep free];
|
||||
tex_count = 0;
|
||||
|
||||
// try and use the cached wadfile
|
||||
sprintf (local, "/qcache%s", newpath);
|
||||
|
||||
Sys_UpdateFile (local, newpath);
|
||||
|
||||
LoadFile (local, (void **)&wadfile);
|
||||
wadinfo = (wadinfo_t *)wadfile;
|
||||
|
||||
if (strncmp (wadfile, "WAD2", 4))
|
||||
{
|
||||
unlink (local);
|
||||
Error ("TEX_InitFromWad: %s isn't a wadfile", newpath);
|
||||
}
|
||||
|
||||
numlumps = LittleLong (wadinfo->numlumps);
|
||||
lumpinfo = (lumpinfo_t *)(wadfile + LittleLong (wadinfo->infotableofs));
|
||||
|
||||
if (strcmp (lumpinfo->name, "PALETTE"))
|
||||
{
|
||||
unlink (local);
|
||||
Error ("TEX_InitFromWad: %s doesn't have palette as 0",path);
|
||||
}
|
||||
|
||||
TEX_InitPalette (wadfile + LittleLong(lumpinfo->filepos));
|
||||
|
||||
lumpinfo++;
|
||||
for (i=1 ; i<numlumps ; i++, lumpinfo++)
|
||||
{
|
||||
if (lumpinfo->type != TYP_MIPTEX)
|
||||
Error ("TEX_InitFromWad: %s is not a miptex!",lumpinfo->name);
|
||||
CleanupName (lumpinfo->name,qtextures[tex_count].name);
|
||||
TEX_ImageFromMiptex ( (miptex_t *)(wadfile +
|
||||
LittleLong(lumpinfo->filepos) ));
|
||||
}
|
||||
|
||||
free (wadfile);
|
||||
|
||||
stop = I_FloatTime ();
|
||||
|
||||
qprintf ("loaded %s (%5.1f)", local, stop - start);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
TEX_NumForName
|
||||
=================
|
||||
*/
|
||||
qtexture_t *TEX_ForName (char *name)
|
||||
{
|
||||
char newname[16];
|
||||
int i;
|
||||
qtexture_t *q;
|
||||
|
||||
CleanupName (name, newname);
|
||||
|
||||
for (i=0,q = qtextures ; i< tex_count ; i++, q++)
|
||||
{
|
||||
if (!strcmp(name, q->name))
|
||||
return q;
|
||||
}
|
||||
|
||||
return &badtex;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========================================================================
|
||||
|
||||
@implementation TexturePalette
|
||||
|
||||
- init
|
||||
{
|
||||
[super init];
|
||||
texturepalette_i = self;
|
||||
selectedTexture = -1;
|
||||
return self;
|
||||
}
|
||||
|
||||
- display
|
||||
{
|
||||
[[textureView_i superview] display];
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (char *)currentWad
|
||||
{
|
||||
return currentwad;
|
||||
}
|
||||
|
||||
- initPaletteFromWadfile:(char *)wf
|
||||
{
|
||||
int i;
|
||||
texpal_t t;
|
||||
qtexture_t *q;
|
||||
|
||||
strcpy (currentwad, wf);
|
||||
[map_i makeGlobalPerform: @selector(flushTextures)];
|
||||
selectedTexture = -1;
|
||||
|
||||
// Init textures WAD
|
||||
TEX_InitFromWad(wf);
|
||||
|
||||
// Create STORAGE
|
||||
if (textureList_i)
|
||||
[textureList_i empty];
|
||||
else
|
||||
textureList_i = [[Storage alloc]
|
||||
initCount:0
|
||||
elementSize:sizeof(texpal_t)
|
||||
description:NULL];
|
||||
|
||||
// Init STORAGE
|
||||
|
||||
for (i = 0,q=qtextures;i < tex_count; i++,q++)
|
||||
{
|
||||
t.image = q->rep;
|
||||
t.r.size.width = [t.image pixelsWide];
|
||||
if (t.r.size.width < 64)
|
||||
t.r.size.width = 64;
|
||||
t.r.size.height = [t.image pixelsHigh] + TEX_SPACING;
|
||||
t.name = q->name;
|
||||
t.index = i;
|
||||
t.display = 1;
|
||||
[textureList_i addElement:&t];
|
||||
}
|
||||
|
||||
// Calculate size of TextureView
|
||||
[self alphabetize];
|
||||
[self computeTextureViewSize];
|
||||
[textureView_i setParent:self];
|
||||
[self setSelectedTexture:0];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Return texture STORAGE list
|
||||
- getList
|
||||
{
|
||||
return textureList_i;
|
||||
}
|
||||
|
||||
// Alphabetize texture list - reverse order!
|
||||
- alphabetize
|
||||
{
|
||||
int i;
|
||||
int max;
|
||||
texpal_t *t1p;
|
||||
texpal_t *t2p;
|
||||
texpal_t t1;
|
||||
texpal_t t2;
|
||||
int found;
|
||||
|
||||
max = [textureList_i count];
|
||||
found = 1;
|
||||
while(found)
|
||||
{
|
||||
found = 0;
|
||||
for (i = 0;i < max-1;i++)
|
||||
{
|
||||
t1p = [textureList_i elementAt:i];
|
||||
t2p = [textureList_i elementAt:i+1];
|
||||
if (strcmp(t1p->name,t2p->name) < 0)
|
||||
{
|
||||
t1 = *t1p;
|
||||
t2 = *t2p;
|
||||
[textureList_i replaceElementAt:i with:&t2];
|
||||
[textureList_i replaceElementAt:i+1 with:&t1];
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- computeTextureViewSize
|
||||
{
|
||||
int i;
|
||||
int max;
|
||||
int x;
|
||||
texpal_t *t;
|
||||
int y;
|
||||
id view;
|
||||
NSRect b;
|
||||
int maxwidth;
|
||||
int maxheight;
|
||||
NSPoint pt;
|
||||
|
||||
max = [textureList_i count];
|
||||
y = 0;
|
||||
maxheight = 0;
|
||||
x = TEX_INDENT;
|
||||
|
||||
view = [textureView_i superview];
|
||||
[view getBounds:&b];
|
||||
maxwidth = b.size.width;
|
||||
|
||||
for (i = 0;i < max; i++)
|
||||
{
|
||||
t = [textureList_i elementAt:i];
|
||||
if (x + t->r.size.width + TEX_INDENT > maxwidth)
|
||||
{
|
||||
x = TEX_INDENT;
|
||||
y += maxheight;
|
||||
maxheight = 0;
|
||||
}
|
||||
if (t->r.size.height > maxheight)
|
||||
maxheight = t->r.size.height;
|
||||
t->r.origin.x = x;
|
||||
t->r.origin.y = y;
|
||||
x += t->r.size.width + TEX_INDENT;
|
||||
if (i == max - 1)
|
||||
y += t->r.size.height;
|
||||
}
|
||||
|
||||
viewWidth = maxwidth;
|
||||
viewHeight = y + TEX_SPACING;
|
||||
[textureView_i sizeTo:viewWidth :viewHeight];
|
||||
pt.x = pt.y = 0;
|
||||
[textureView_i scrollPoint:&pt];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- windowResized
|
||||
{
|
||||
[self computeTextureViewSize];
|
||||
return self;
|
||||
}
|
||||
|
||||
- texturedefChanged: sender
|
||||
{
|
||||
if ([map_i numSelected])
|
||||
{
|
||||
if ( [[map_i currentEntity] modifiable] )
|
||||
{
|
||||
[map_i makeSelectedPerform: @selector(takeCurrentTexture)];
|
||||
[quakeed_i updateAll];
|
||||
}
|
||||
else
|
||||
qprintf ("can't modify spawned entities");
|
||||
}
|
||||
[quakeed_i makeFirstResponder: quakeed_i];
|
||||
return self;
|
||||
}
|
||||
|
||||
- clearTexinfo: sender
|
||||
{
|
||||
[field_Xshift_i setFloatValue:0];
|
||||
[field_Yshift_i setFloatValue:0];
|
||||
[field_Xscale_i setFloatValue:1];
|
||||
[field_Yscale_i setFloatValue:1];
|
||||
[field_Rotate_i setFloatValue:0];
|
||||
|
||||
[self texturedefChanged: self];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Set the selected texture
|
||||
//
|
||||
- setSelectedTexture:(int)which
|
||||
{
|
||||
texpal_t *t;
|
||||
NSRect r;
|
||||
char string[16];
|
||||
|
||||
// wipe the fields
|
||||
[self clearTexinfo: self];
|
||||
|
||||
if (which != selectedTexture)
|
||||
{
|
||||
[textureView_i deselect];
|
||||
selectedTexture = which;
|
||||
t = [textureList_i elementAt:which];
|
||||
r = t->r;
|
||||
r.size.width += TEX_INDENT*2;
|
||||
r.size.height += TEX_INDENT*2;
|
||||
r.origin.x -= TEX_INDENT;
|
||||
r.origin.y -= TEX_INDENT;
|
||||
[textureView_i scrollRectToVisible:&r];
|
||||
[textureView_i display];
|
||||
sprintf(string,"%d x %d",(int)t->r.size.width,
|
||||
(int)t->r.size.height - TEX_SPACING);
|
||||
[sizeField_i setStringValue:string];
|
||||
}
|
||||
|
||||
[self texturedefChanged:self];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Return the selected texture index
|
||||
//
|
||||
- (int)getSelectedTexture
|
||||
{
|
||||
return selectedTexture;
|
||||
}
|
||||
|
||||
//
|
||||
// Return the original tex_ index of the selected texture
|
||||
// so the texture info can be indexed from tex_images, etc.
|
||||
//
|
||||
- (int)getSelectedTexIndex
|
||||
{
|
||||
texpal_t *t;
|
||||
|
||||
if (selectedTexture == -1)
|
||||
return -1;
|
||||
t = [textureList_i elementAt:selectedTexture];
|
||||
return t->index;
|
||||
}
|
||||
|
||||
//
|
||||
// Return the name of the selected texture
|
||||
//
|
||||
- (char *)getSelTextureName
|
||||
{
|
||||
texpal_t *t;
|
||||
|
||||
if (selectedTexture == -1)
|
||||
return NULL;
|
||||
t = [textureList_i elementAt:selectedTexture];
|
||||
return t->name;
|
||||
}
|
||||
|
||||
//
|
||||
// Set selected texture by texture name
|
||||
//
|
||||
- setTextureByName:(char *)name
|
||||
{
|
||||
texpal_t *t;
|
||||
int i;
|
||||
int max;
|
||||
|
||||
max = [textureList_i count];
|
||||
CleanupName(name,name);
|
||||
for (i = 0;i < max;i++)
|
||||
{
|
||||
t = [textureList_i elementAt:i];
|
||||
if (!strcmp(t->name,name))
|
||||
{
|
||||
[self setSelectedTexture: i];
|
||||
return self;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//===================================================
|
||||
//
|
||||
// Action methods
|
||||
//
|
||||
//===================================================
|
||||
|
||||
|
||||
//
|
||||
// Search for texture named in searchField
|
||||
//
|
||||
- searchForTexture:sender
|
||||
{
|
||||
int i;
|
||||
int max;
|
||||
int len;
|
||||
char name[32];
|
||||
texpal_t *t;
|
||||
|
||||
if (selectedTexture == -1)
|
||||
return self;
|
||||
|
||||
max = [textureList_i count];
|
||||
strcpy(name,(const char *)[sender stringValue]);
|
||||
[sender setStringValue:strupr(name)];
|
||||
len = strlen(name);
|
||||
|
||||
for (i = selectedTexture-1;i >= 0; i--)
|
||||
{
|
||||
t = [textureList_i elementAt:i];
|
||||
if (!strncmp(t->name,name,len))
|
||||
{
|
||||
[self setTextureByName:t->name];
|
||||
[sender selectText:sender];
|
||||
[self texturedefChanged:self];
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = max-1;i >= selectedTexture; i--)
|
||||
{
|
||||
t = [textureList_i elementAt:i];
|
||||
if (!strncmp(t->name,name,len))
|
||||
{
|
||||
[self setTextureByName:t->name];
|
||||
[sender selectText:sender];
|
||||
[self texturedefChanged:self];
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
[self texturedefChanged:self];
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Set texture def from outside TexturePalette
|
||||
//
|
||||
- setTextureDef:(texturedef_t *)td
|
||||
{
|
||||
[self setTextureByName:td->texture];
|
||||
|
||||
[field_Xshift_i setFloatValue:td->shift[0]];
|
||||
[field_Yshift_i setFloatValue:td->shift[1]];
|
||||
[field_Xscale_i setFloatValue:td->scale[0]];
|
||||
[field_Yscale_i setFloatValue:td->scale[1]];
|
||||
[field_Rotate_i setFloatValue:td->rotate];
|
||||
|
||||
[self texturedefChanged:self];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Return the current texture def to passed *
|
||||
//
|
||||
- getTextureDef:(texturedef_t *)td
|
||||
{
|
||||
if (selectedTexture == -1)
|
||||
{
|
||||
memset (td, 0, sizeof(*td));
|
||||
strcpy (td->texture, "notexture");
|
||||
return self;
|
||||
}
|
||||
|
||||
strncpy(td->texture,[self getSelTextureName],16);
|
||||
|
||||
td->shift[0] = [field_Xshift_i floatValue];
|
||||
td->shift[1] = [field_Yshift_i floatValue];
|
||||
td->scale[0] = [field_Xscale_i floatValue];
|
||||
td->scale[1] = [field_Yscale_i floatValue];
|
||||
td->rotate = [field_Rotate_i floatValue];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
//
|
||||
// Change value in a field
|
||||
//
|
||||
- changeField:(id)field by:(int)amount
|
||||
{
|
||||
int val;
|
||||
|
||||
val = [field intValue];
|
||||
val += amount;
|
||||
[field setIntValue:val];
|
||||
|
||||
[self texturedefChanged:self];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Inc/Dec the XShift field
|
||||
//
|
||||
- incXShift:sender
|
||||
{
|
||||
[self changeField:field_Xshift_i by:8];
|
||||
return self;
|
||||
}
|
||||
- decXShift:sender
|
||||
{
|
||||
[self changeField:field_Xshift_i by:-8];
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Inc/Dec the YShift field
|
||||
//
|
||||
- incYShift:sender
|
||||
{
|
||||
[self changeField:field_Yshift_i by:8];
|
||||
return self;
|
||||
}
|
||||
- decYShift:sender
|
||||
{
|
||||
[self changeField:field_Yshift_i by:-8];
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Inc/Dec the Rotate field
|
||||
//
|
||||
- incRotate:sender
|
||||
{
|
||||
[self changeField:field_Rotate_i by:90];
|
||||
return self;
|
||||
}
|
||||
- decRotate:sender
|
||||
{
|
||||
[self changeField:field_Rotate_i by:-90];
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Inc/Dec the Xscale field
|
||||
//
|
||||
- incXScale:sender
|
||||
{
|
||||
[field_Xscale_i setIntValue: 1];
|
||||
[self texturedefChanged:self];
|
||||
return self;
|
||||
}
|
||||
- decXScale:sender
|
||||
{
|
||||
[field_Xscale_i setIntValue: -1];
|
||||
[self texturedefChanged:self];
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Inc/Dec the Yscale field
|
||||
//
|
||||
- incYScale:sender
|
||||
{
|
||||
[field_Yscale_i setIntValue: 1];
|
||||
[self texturedefChanged:self];
|
||||
return self;
|
||||
}
|
||||
- decYScale:sender
|
||||
{
|
||||
[field_Yscale_i setIntValue: -1];
|
||||
[self texturedefChanged:self];
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
||||
//
|
||||
// Search for texture in entire palette
|
||||
// Return index of texturedef, or -1 if unsuccessful
|
||||
//
|
||||
- (int) searchForTextureInPalette:(char *)texture
|
||||
{
|
||||
int i;
|
||||
int max;
|
||||
char name[32];
|
||||
texpal_t *t;
|
||||
|
||||
if (selectedTexture == -1)
|
||||
return -1;
|
||||
|
||||
max = [textureList_i count];
|
||||
strcpy(name,texture);
|
||||
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
t = [textureList_i elementAt:i];
|
||||
if (!strcmp(t->name,name))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
//
|
||||
// Scan thru map & only display textures that are in map
|
||||
//
|
||||
- onlyShowMapTextures:sender
|
||||
{
|
||||
int max;
|
||||
int i;
|
||||
int j;
|
||||
id brushes;
|
||||
SetBrush *b;
|
||||
int numfaces;
|
||||
face_t *f;
|
||||
int index;
|
||||
|
||||
// Turn 'em off
|
||||
if ([sender intValue])
|
||||
{
|
||||
max = [textureList_i count];
|
||||
for (i = 0;i < max; i++)
|
||||
[self setDisplayFlag:i to:0];
|
||||
|
||||
brushes = [map_i objectAt:0];
|
||||
max = [brushes count];
|
||||
for (i = 0;i < max; i++)
|
||||
{
|
||||
b = (SetBrush *)[brushes objectAt:i];
|
||||
numfaces = [b getNumBrushFaces];
|
||||
for (j = 0; j < numfaces; j++)
|
||||
{
|
||||
f = [b getBrushFace:j];
|
||||
index = [self searchForTextureInPalette:f->texture.texture];
|
||||
if (index >= 0)
|
||||
[self setDisplayFlag:index to:1];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Turn 'em on
|
||||
else
|
||||
{
|
||||
max = [textureList_i count];
|
||||
for (i = 0;i < max; i++)
|
||||
[self setDisplayFlag:i to:1];
|
||||
}
|
||||
|
||||
[textureView_i display];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- setDisplayFlag:(int)index to:(int)value
|
||||
{
|
||||
texpal_t *tp;
|
||||
|
||||
tp = [textureList_i elementAt:index];
|
||||
tp->display = value;
|
||||
return self;
|
||||
};
|
||||
|
||||
@end
|
12
tools/Forge/Bundles/MapEdit/TextureView.h
Normal file
12
tools/Forge/Bundles/MapEdit/TextureView.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
|
||||
@interface TextureView:NSView
|
||||
{
|
||||
id parent_i;
|
||||
int deselectIndex;
|
||||
}
|
||||
|
||||
- setParent:(id)from;
|
||||
- deselect;
|
||||
|
||||
@end
|
152
tools/Forge/Bundles/MapEdit/TextureView.m
Normal file
152
tools/Forge/Bundles/MapEdit/TextureView.m
Normal file
|
@ -0,0 +1,152 @@
|
|||
|
||||
#import "qedefs.h"
|
||||
|
||||
/*
|
||||
|
||||
NOTE: I am specifically not using cached image reps, because the data is also needed for texturing the views, and a cached rep would waste tons of space.
|
||||
|
||||
*/
|
||||
|
||||
@implementation TextureView
|
||||
|
||||
- init
|
||||
{
|
||||
deselectIndex = -1;
|
||||
return self;
|
||||
}
|
||||
|
||||
- setParent:(id)from
|
||||
{
|
||||
parent_i = from;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstMouse
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- drawSelf:(const NSRect *)rects :(int)rectCount
|
||||
{
|
||||
int i;
|
||||
int max;
|
||||
id list_i;
|
||||
texpal_t *t;
|
||||
int x;
|
||||
int y;
|
||||
NSPoint p;
|
||||
NSRect r;
|
||||
int selected;
|
||||
|
||||
selected = [parent_i getSelectedTexture];
|
||||
list_i = [parent_i getList];
|
||||
PSselectfont("Helvetica-Medium",FONTSIZE);
|
||||
PSrotate(0);
|
||||
|
||||
PSsetgray(NS_LTGRAY);
|
||||
PSrectfill(rects->origin.x, rects->origin.y,
|
||||
rects->size.width, rects->size.height);
|
||||
|
||||
if (!list_i) // WADfile didn't init
|
||||
return self;
|
||||
|
||||
if (deselectIndex != -1)
|
||||
{
|
||||
t = [list_i elementAt:deselectIndex];
|
||||
r = t->r;
|
||||
r.origin.x -= TEX_INDENT;
|
||||
r.origin.y -= TEX_INDENT;
|
||||
r.size.width += TEX_INDENT*2;
|
||||
r.size.height += TEX_INDENT*2;
|
||||
|
||||
PSsetgray(NSGrayComponent(NS_COLORLTGRAY));
|
||||
PSrectfill(r.origin.x, r.origin.y,
|
||||
r.size.width, r.size.height);
|
||||
p = t->r.origin;
|
||||
p.y += TEX_SPACING;
|
||||
[t->image drawAt:&p];
|
||||
PSsetgray(0);
|
||||
x = t->r.origin.x;
|
||||
y = t->r.origin.y + 7;
|
||||
PSmoveto(x,y);
|
||||
PSshow(t->name);
|
||||
PSstroke();
|
||||
deselectIndex = -1;
|
||||
}
|
||||
|
||||
max = [list_i count];
|
||||
PSsetgray(0);
|
||||
|
||||
for (i = 0;i < max; i++)
|
||||
{
|
||||
t = [list_i elementAt:i];
|
||||
r = t->r;
|
||||
r.origin.x -= TEX_INDENT/2;
|
||||
r.size.width += TEX_INDENT;
|
||||
r.origin.y += 4;
|
||||
if (NSIntersectsRect(&rects[0],&r) == YES &&
|
||||
t->display)
|
||||
{
|
||||
if (selected == i)
|
||||
{
|
||||
PSsetgray(1);
|
||||
PSrectfill(r.origin.x,r.origin.y,
|
||||
r.size.width,r.size.height);
|
||||
PSsetrgbcolor(1,0,0);
|
||||
PSrectstroke(r.origin.x, r.origin.y,
|
||||
r.size.width, r.size.height);
|
||||
PSsetgray(0);
|
||||
}
|
||||
|
||||
p = t->r.origin;
|
||||
p.y += TEX_SPACING;
|
||||
[t->image drawAt:&p];
|
||||
x = t->r.origin.x;
|
||||
y = t->r.origin.y + 7;
|
||||
PSmoveto(x,y);
|
||||
PSshow(t->name);
|
||||
}
|
||||
}
|
||||
PSstroke();
|
||||
return self;
|
||||
}
|
||||
|
||||
- deselect
|
||||
{
|
||||
deselectIndex = [parent_i getSelectedTexture];
|
||||
return self;
|
||||
}
|
||||
|
||||
- mouseDown:(NSEvent *)theEvent
|
||||
{
|
||||
NSPoint loc;
|
||||
int i;
|
||||
int max;
|
||||
int oldwindowmask;
|
||||
texpal_t *t;
|
||||
id list;
|
||||
NSRect r;
|
||||
|
||||
oldwindowmask = [window addToEventMask:NS_LMOUSEDRAGGEDMASK];
|
||||
loc = theEvent->location;
|
||||
[self convertPoint:&loc fromView:NULL];
|
||||
|
||||
list = [parent_i getList];
|
||||
max = [list count];
|
||||
for (i = 0;i < max; i++)
|
||||
{
|
||||
t = [list elementAt:i];
|
||||
r = t->r;
|
||||
if (NSPointInRect(&loc,&r) == YES)
|
||||
{
|
||||
[self deselect];
|
||||
[parent_i setSelectedTexture:i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[window setEventMask:oldwindowmask];
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
42
tools/Forge/Bundles/MapEdit/Things.h
Normal file
42
tools/Forge/Bundles/MapEdit/Things.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
extern id things_i;
|
||||
|
||||
#define ENTITYNAMEKEY "spawn"
|
||||
|
||||
@interface Things:Object
|
||||
{
|
||||
id entity_browser_i; // browser
|
||||
id entity_comment_i; // scrolling text window
|
||||
|
||||
id prog_path_i;
|
||||
|
||||
int lastSelected; // last row selected in browser
|
||||
|
||||
id keyInput_i;
|
||||
id valueInput_i;
|
||||
id flags_i;
|
||||
}
|
||||
|
||||
- initEntities;
|
||||
|
||||
- newCurrentEntity;
|
||||
- setSelectedKey:(epair_t *)ep;
|
||||
|
||||
- clearInputs;
|
||||
- (char *)spawnName;
|
||||
|
||||
// UI targets
|
||||
- reloadEntityClasses: sender;
|
||||
- selectEntity: sender;
|
||||
- doubleClickEntity: sender;
|
||||
|
||||
// Action methods
|
||||
- addPair:sender;
|
||||
- delPair:sender;
|
||||
- setAngle:sender;
|
||||
- setFlags:sender;
|
||||
|
||||
|
||||
@end
|
317
tools/Forge/Bundles/MapEdit/Things.m
Normal file
317
tools/Forge/Bundles/MapEdit/Things.m
Normal file
|
@ -0,0 +1,317 @@
|
|||
|
||||
#import "qedefs.h"
|
||||
|
||||
id things_i;
|
||||
|
||||
@implementation Things
|
||||
|
||||
- init
|
||||
{
|
||||
[super init];
|
||||
|
||||
things_i = self;
|
||||
lastSelected = 0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Load the TEXT object with the entity comment
|
||||
//
|
||||
- loadEntityComment:(id)obj
|
||||
{
|
||||
[entity_comment_i selectAll:self];
|
||||
[entity_comment_i replaceSel:[obj comments]];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- initEntities
|
||||
{
|
||||
char *path;
|
||||
|
||||
path = [project_i getProgDirectory];
|
||||
|
||||
[prog_path_i setStringValue: path];
|
||||
|
||||
[[EntityClassList alloc] initForSourceDirectory: path];
|
||||
|
||||
[self loadEntityComment:[entity_classes_i objectAt:lastSelected]];
|
||||
[entity_browser_i loadColumnZero];
|
||||
[[entity_browser_i matrixInColumn:0] selectCellAt:lastSelected :0];
|
||||
|
||||
[entity_browser_i setDoubleAction: @selector(doubleClickEntity:)];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- selectEntity: sender
|
||||
{
|
||||
id matr;
|
||||
|
||||
matr = [sender matrixInColumn: 0];
|
||||
lastSelected = [matr selectedRow];
|
||||
[self loadEntityComment:[entity_classes_i objectAt:lastSelected]];
|
||||
[quakeed_i makeFirstResponder: quakeed_i];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- doubleClickEntity: sender
|
||||
{
|
||||
[map_i makeEntity: sender];
|
||||
[quakeed_i makeFirstResponder: quakeed_i];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (char *)spawnName
|
||||
{
|
||||
return [[entity_classes_i objectAt:lastSelected] classname];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Flush entity classes & reload them!
|
||||
//
|
||||
- reloadEntityClasses: sender
|
||||
{
|
||||
EntityClass *ent;
|
||||
char *path;
|
||||
|
||||
path = (char *)[prog_path_i stringValue];
|
||||
if (!path || !path[0])
|
||||
{
|
||||
path = [project_i getProgDirectory];
|
||||
[prog_path_i setStringValue: path];
|
||||
}
|
||||
|
||||
// Free all entity info in memory...
|
||||
[entity_classes_i freeObjects];
|
||||
[entity_classes_i free];
|
||||
|
||||
// Now, RELOAD!
|
||||
[[EntityClassList alloc] initForSourceDirectory: path];
|
||||
|
||||
lastSelected = 0;
|
||||
ent = [entity_classes_i objectAt:lastSelected];
|
||||
[self loadEntityComment:[entity_classes_i objectAt:lastSelected]];
|
||||
|
||||
[entity_browser_i loadColumnZero];
|
||||
[[entity_browser_i matrixInColumn:0] selectCellAt:lastSelected :0];
|
||||
|
||||
[self newCurrentEntity]; // in case flags changed
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- selectClass: (char *)class
|
||||
{
|
||||
id classent;
|
||||
|
||||
classent = [entity_classes_i classForName:class];
|
||||
if (!classent)
|
||||
return self;
|
||||
lastSelected = [entity_classes_i indexOf: classent];
|
||||
|
||||
if (lastSelected < 0)
|
||||
lastSelected = 0;
|
||||
|
||||
[self loadEntityComment:classent];
|
||||
[[entity_browser_i matrixInColumn:0] selectCellAt:lastSelected :0];
|
||||
[[entity_browser_i matrixInColumn:0] scrollCellToVisible:lastSelected :0];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- newCurrentEntity
|
||||
{
|
||||
id ent, classent, cell;
|
||||
char *classname;
|
||||
int r, c;
|
||||
char *flagname;
|
||||
int flags;
|
||||
|
||||
ent = [map_i currentEntity];
|
||||
classname = [ent valueForQKey: "classname"];
|
||||
if (ent != [map_i objectAt: 0])
|
||||
[self selectClass: classname]; // don't reset for world
|
||||
classent = [entity_classes_i classForName:classname];
|
||||
flagname = [ent valueForQKey: "spawnflags"];
|
||||
if (!flagname)
|
||||
flags = 0;
|
||||
else
|
||||
flags = atoi(flagname);
|
||||
|
||||
[flags_i setAutodisplay: NO];
|
||||
for (r=0 ; r<4 ; r++)
|
||||
for (c=0 ; c<3 ; c++)
|
||||
{
|
||||
cell = [flags_i cellAt: r : c];
|
||||
if (c < 2)
|
||||
{
|
||||
flagname = [classent flagName: c*4 + r];
|
||||
[cell setTitle: flagname];
|
||||
}
|
||||
[cell setIntValue: (flags & (1<< ((c*4)+r)) ) > 0];
|
||||
}
|
||||
[flags_i setAutodisplay: YES];
|
||||
[flags_i display];
|
||||
|
||||
// [keyInput_i setStringValue: ""];
|
||||
// [valueInput_i setStringValue: ""];
|
||||
|
||||
[keypairview_i calcViewSize];
|
||||
[keypairview_i display];
|
||||
|
||||
[quakeed_i makeFirstResponder: quakeed_i];
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Clicked in the Keypair view - set as selected
|
||||
//
|
||||
- setSelectedKey:(epair_t *)ep;
|
||||
{
|
||||
[keyInput_i setStringValue:ep->key];
|
||||
[valueInput_i setStringValue:ep->value];
|
||||
[valueInput_i selectText:self];
|
||||
return self;
|
||||
}
|
||||
|
||||
- clearInputs
|
||||
{
|
||||
// [keyInput_i setStringValue: ""];
|
||||
// [valueInput_i setStringValue: ""];
|
||||
|
||||
[quakeed_i makeFirstResponder: quakeed_i];
|
||||
return self;
|
||||
}
|
||||
|
||||
//
|
||||
// Action methods
|
||||
//
|
||||
|
||||
-addPair:sender
|
||||
{
|
||||
char *key, *value;
|
||||
|
||||
key = (char *)[keyInput_i stringValue];
|
||||
value = (char *)[valueInput_i stringValue];
|
||||
|
||||
[ [map_i currentEntity] setKey: key toValue: value ];
|
||||
|
||||
[keypairview_i calcViewSize];
|
||||
[keypairview_i display];
|
||||
|
||||
[self clearInputs];
|
||||
[quakeed_i updateXY];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-delPair:sender
|
||||
{
|
||||
[quakeed_i makeFirstResponder: quakeed_i];
|
||||
|
||||
[ [map_i currentEntity] removeKeyPair: (char *)[keyInput_i stringValue] ];
|
||||
|
||||
[keypairview_i calcViewSize];
|
||||
[keypairview_i display];
|
||||
|
||||
[self clearInputs];
|
||||
|
||||
[quakeed_i updateXY];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Set the key/value fields to "angle <button value>"
|
||||
//
|
||||
- setAngle:sender
|
||||
{
|
||||
const char *title;
|
||||
char value[10];
|
||||
|
||||
title = [[sender selectedCell] title];
|
||||
if (!strcmp(title,"Up"))
|
||||
strcpy (value, "-1");
|
||||
else if (!strcmp(title,"Dn"))
|
||||
strcpy (value, "-2");
|
||||
else
|
||||
strcpy (value, title);
|
||||
|
||||
[keyInput_i setStringValue:"angle"];
|
||||
[valueInput_i setStringValue:value];
|
||||
[self addPair:NULL];
|
||||
|
||||
[self clearInputs];
|
||||
|
||||
[quakeed_i updateXY];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- setFlags:sender
|
||||
{
|
||||
int flags;
|
||||
int r, c, i;
|
||||
id cell;
|
||||
char str[20];
|
||||
|
||||
[self clearInputs];
|
||||
flags = 0;
|
||||
|
||||
for (r=0 ; r<4 ; r++)
|
||||
for (c=0 ; c<3 ; c++)
|
||||
{
|
||||
cell = [flags_i cellAt: r : c];
|
||||
i = ([cell intValue] > 0);
|
||||
flags |= (i<< ((c*4)+r));
|
||||
}
|
||||
|
||||
if (!flags)
|
||||
[[map_i currentEntity] removeKeyPair: "spawnflags"];
|
||||
else
|
||||
{
|
||||
sprintf (str, "%i", flags);
|
||||
[[map_i currentEntity] setKey: "spawnflags" toValue: str];
|
||||
}
|
||||
|
||||
[keypairview_i calcViewSize];
|
||||
[keypairview_i display];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Fill the Entity browser
|
||||
// (Delegate method - delegated in Interface Builder)
|
||||
//
|
||||
- (int)browser:sender fillMatrix:matrix inColumn:(int)column
|
||||
{
|
||||
id cell;
|
||||
int max;
|
||||
int i;
|
||||
id object;
|
||||
|
||||
max = [entity_classes_i count];
|
||||
i = 0;
|
||||
while(max--)
|
||||
{
|
||||
object = [entity_classes_i objectAt:i];
|
||||
[matrix addRow];
|
||||
cell = [matrix cellAt:i++ :0];
|
||||
[cell setStringValue:[object classname]];
|
||||
[cell setLeaf:YES];
|
||||
[cell setLoaded:YES];
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
@end
|
72
tools/Forge/Bundles/MapEdit/UserPath.h
Normal file
72
tools/Forge/Bundles/MapEdit/UserPath.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* UserPath.h by Bruce Blumberg, NeXT Computer, Inc.
|
||||
*
|
||||
* You may freely copy,distribute and re-use the code in this example. NeXT
|
||||
* disclaims any warranty of any kind, expressed or implied, as to its fitness
|
||||
* for any particular purpose
|
||||
*
|
||||
* This file and its associated .m file define a data structure and set of
|
||||
* functions aimed at facilitating the use of user paths. Here is a simple
|
||||
* example:
|
||||
*
|
||||
* UserPath *arect;
|
||||
* arect = newUserPath(); // creates an empty user path
|
||||
* beginUserPath(arect,YES); // initialize user path and cache
|
||||
* UPmoveto(arect,0.0,0.0); // add moveto to userpath; update bounding box
|
||||
* UPrlineto(arect,0.0,100.0); // add rlineto to path; update bounding box
|
||||
* UPrlineto(arect,100.0,0.0); // add rlineto to path; update bounding box
|
||||
* UPrlineto(arect,0.0,-100.0); // add rlineto to path; update bounding box
|
||||
* closePath(arect); // close path
|
||||
* endUserPath(arect,dps_stroke); // close user path and specify operator
|
||||
* sendUserPath(arect);
|
||||
*
|
||||
* As you will note, the set of routines manage the allocation and growth of
|
||||
* the operator and operand arrays, as well as the calculation of the bounding
|
||||
* box. A user path created via these functions may be optionally cached down
|
||||
* at the window server, or repeatedly sent down. The user paths created by
|
||||
* this set of functions are all allocated in a unique zone.
|
||||
*
|
||||
* Note: the associated file is a .m file because it pulls in some .h files
|
||||
* which reference objective C methods.
|
||||
*/
|
||||
|
||||
#import <objc/objc.h>
|
||||
#import <AppKit/NSGraphicsContext.h>
|
||||
|
||||
typedef struct _UP {
|
||||
float *points;
|
||||
int numberOfPoints;
|
||||
char *ops;
|
||||
NSPoint cp;
|
||||
int numberOfOps;
|
||||
int max;
|
||||
float bbox[4];
|
||||
int opForUserPath;
|
||||
BOOL ping;
|
||||
} UserPath;
|
||||
|
||||
/* UserPath functions */
|
||||
NSZone *userPathZone();
|
||||
UserPath *newUserPath();
|
||||
void freeUserPath(UserPath *up);
|
||||
void debugUserPath(UserPath *up, BOOL shouldPing);
|
||||
void growUserPath(UserPath *up);
|
||||
void beginUserPath(UserPath *up, BOOL cache);
|
||||
void endUserPath(UserPath *up, int op);
|
||||
int sendUserPath(UserPath *up);
|
||||
void UPmoveto(UserPath *up, float x, float y);
|
||||
void UPrmoveto(UserPath *up, float x, float y);
|
||||
void UPlineto(UserPath *up, float x, float y);
|
||||
void UPrlineto(UserPath *up, float x, float y);
|
||||
void UPcurveto(UserPath *up, float x1, float y1, float x2, float y2, float x3,
|
||||
float y3);
|
||||
void UPrcurveto(UserPath *up, float dx1, float dy1, float dx2, float dy2,
|
||||
float dx3, float dy3);
|
||||
void UParc(UserPath *up, float x, float y, float r, float ang1, float ang2);
|
||||
void UParcn(UserPath *up, float x, float y, float r, float ang1, float ang2);
|
||||
void UParct(UserPath *up, float x1, float y1, float x2, float y2, float r);
|
||||
void closePath(UserPath *up);
|
||||
void addPts(UserPath *up, float x, float y);
|
||||
void addOp(UserPath *up, int op);
|
||||
void add(UserPath *up, int op, float x, float y);
|
||||
void checkBBox(UserPath *up, float x, float y);
|
209
tools/Forge/Bundles/MapEdit/UserPath.m
Normal file
209
tools/Forge/Bundles/MapEdit/UserPath.m
Normal file
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* UserPath.m by Bruce Blumberg, NeXT Computer, Inc.
|
||||
*
|
||||
* You may freely copy,distribute and re-use the code in this example. NeXT
|
||||
* disclaims any warranty of any kind, expressed or implied, as to its fitness
|
||||
* for any particular purpose
|
||||
*
|
||||
*/
|
||||
|
||||
#import "UserPath.h"
|
||||
#import <mach/mach_init.h>
|
||||
#import <appkit/graphics.h>
|
||||
#import <appkit/errors.h>
|
||||
#import <math.h>
|
||||
#import <libc.h>
|
||||
|
||||
static NSZone *upZone = NULL;
|
||||
|
||||
NSZone *userPathZone()
|
||||
/* Creates a unique zone for use by all user paths */
|
||||
{
|
||||
if (!upZone) {
|
||||
upZone = NSCreateZone(vm_page_size, vm_page_size, 1);
|
||||
}
|
||||
|
||||
return upZone;
|
||||
}
|
||||
|
||||
UserPath *newUserPath()
|
||||
/* Creates a new User Path in the zone returned by userPathZone */
|
||||
{
|
||||
UserPath *up;
|
||||
|
||||
up = (UserPath *)NSZoneMalloc(userPathZone(), sizeof(UserPath));
|
||||
up->max = 8192; // JDC
|
||||
up->points = (float *)NSZoneMalloc(userPathZone(),
|
||||
sizeof(float) * up->max);
|
||||
up->ops = (char *)NSZoneMalloc(userPathZone(),
|
||||
(2 + (up->max / 2)) * sizeof(char));
|
||||
up->ping = NO;
|
||||
|
||||
return up;
|
||||
}
|
||||
|
||||
void freeUserPath(UserPath *up)
|
||||
/* Frees User Path and its associated buffers */
|
||||
{
|
||||
free(up->points);
|
||||
free(up->ops);
|
||||
free(up);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void growUserPath(UserPath *up)
|
||||
/*
|
||||
* grows the associated buffers as necessary. buffer size doubles on each
|
||||
* call. You never need to call grow directly as it is called as needed by the
|
||||
* methods and functions which add elements into the buffer
|
||||
*/
|
||||
{
|
||||
/* double the size of the internal buffers */
|
||||
printf ("growUserPath\n");
|
||||
up->max *= 2;
|
||||
up->points = (float *)NSZoneRealloc(userPathZone(), up->points,
|
||||
sizeof(float) * up->max);
|
||||
up->ops = (char *)NSZoneRealloc(userPathZone(), up->ops,
|
||||
(2 + (up->max / 2)) * sizeof(char));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void beginUserPath(UserPath *up, BOOL cache)
|
||||
/*
|
||||
* Call this to start generating a user path. The cache argument specifies if
|
||||
* you want the user path cached at the server (i.e. dps_ucache). In either
|
||||
* case, the UserPath object will automatically calculate the bounding box for
|
||||
* the path and add the dps_setbbox operator.
|
||||
*/
|
||||
{
|
||||
up->numberOfPoints = up->numberOfOps = 0;
|
||||
up->cp.x = up->cp.y = 0;
|
||||
up->bbox[0] = up->bbox[1] = 1.0e6;
|
||||
up->bbox[2] = up->bbox[3] = -1.0e6;
|
||||
if (cache) {
|
||||
up->ops[up->numberOfOps++] = dps_ucache;
|
||||
}
|
||||
up->ops[up->numberOfOps++] = dps_setbbox;
|
||||
up->opForUserPath = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void endUserPath(UserPath *up, int op)
|
||||
/*
|
||||
* Call this to stop filling the path. Note this does not send the userpath to
|
||||
* the server -- use sendUserPath. The op argument should be one of the
|
||||
* following:
|
||||
* dps_uappend, dps_ufill ,dps_ueofill, dps_ustroke, dps_ustrokepath,
|
||||
* dps_inufill, dps_inueofill, dps_inustroke, dps_def, dps_put.
|
||||
* These are defined in <dpsclient/dpsNext.h.
|
||||
*/
|
||||
{
|
||||
up->opForUserPath = op;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void UPdebug(UserPath *up, BOOL shouldPing)
|
||||
/*
|
||||
* Sets ping to YES so that after each time a user path is sent down to the
|
||||
* window server, an NSPing() is sent after. The purpose is to catch PostScript
|
||||
* errors that may be generated by the user path. sendUserPath brackets the
|
||||
* download and the NSPing() in an NS_DURING... NS_HANDLER construct. Normally
|
||||
* ping is NO.
|
||||
*/
|
||||
{
|
||||
up->ping = shouldPing;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int sendUserPath(UserPath *up)
|
||||
/*
|
||||
* Call this to send the path down to the server. If ping==YES (set via
|
||||
* debug:), the function will send an NSPing() after the Path. In any event,
|
||||
* code is bracketed by a NS_DURING ... NS_HANDLER construct which will try to
|
||||
* catch postscript errors. If ping==NO (the default) it is unlikely to catch
|
||||
* errors, with ping==YES it will. Whether you can recover or not is another
|
||||
* matter. sendUserPath returns 0 on success and -1 on failure. If no previous
|
||||
* endUserPath: has been sent, will return -2 and will not send the path to the
|
||||
* server.
|
||||
*/
|
||||
{
|
||||
NSHandler exception;
|
||||
|
||||
exception.code = 0;
|
||||
if (up->opForUserPath != 0) {
|
||||
NS_DURING
|
||||
DPSDoUserPath(up->points, up->numberOfPoints, dps_float, up->ops,
|
||||
up->numberOfOps, up->bbox, up->opForUserPath);
|
||||
if (up->ping) {
|
||||
NSPing();
|
||||
}
|
||||
|
||||
NS_HANDLER
|
||||
exception = NSLocalHandler;
|
||||
NS_ENDHANDLER
|
||||
if (exception.code) {
|
||||
NSReportError(&exception);
|
||||
if (exception.code == dps_err_ps) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void UPmoveto(UserPath *up, float x, float y)
|
||||
/* adds <x y moveto> to user path and updates bounding box */
|
||||
{
|
||||
up->ops[up->numberOfOps++] = dps_moveto;
|
||||
up->points[up->numberOfPoints++] = x;
|
||||
up->points[up->numberOfPoints++] = y;
|
||||
|
||||
if (x < up->bbox[0]) {
|
||||
up->bbox[0] = x;
|
||||
}
|
||||
if (y < up->bbox[1]) {
|
||||
up->bbox[1] = y;
|
||||
}
|
||||
if (x > up->bbox[2]) {
|
||||
up->bbox[2] = x;
|
||||
}
|
||||
if (y > up->bbox[3]) {
|
||||
up->bbox[3] = y;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void UPlineto(UserPath *up, float x, float y)
|
||||
/* adds <x y lineto> to user path and updates bounding box */
|
||||
{
|
||||
up->ops[up->numberOfOps++] = dps_lineto;
|
||||
up->points[up->numberOfPoints++] = x;
|
||||
up->points[up->numberOfPoints++] = y;
|
||||
|
||||
if (x < up->bbox[0]) {
|
||||
up->bbox[0] = x;
|
||||
}
|
||||
if (y < up->bbox[1]) {
|
||||
up->bbox[1] = y;
|
||||
}
|
||||
if (x > up->bbox[2]) {
|
||||
up->bbox[2] = x;
|
||||
}
|
||||
if (y > up->bbox[3]) {
|
||||
up->bbox[3] = y;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
66
tools/Forge/Bundles/MapEdit/XYView.h
Normal file
66
tools/Forge/Bundles/MapEdit/XYView.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import "mathlib.h"
|
||||
#import "SetBrush.h"
|
||||
|
||||
extern id xyview_i;
|
||||
|
||||
#define MINSCALE 0.125
|
||||
#define MAXSCALE 2.0
|
||||
|
||||
|
||||
extern vec3_t xy_viewnormal; // v_forward for xy view
|
||||
extern float xy_viewdist; // clip behind this plane
|
||||
|
||||
extern NSRect xy_draw_rect;
|
||||
|
||||
void linestart (float r, float g, float b);
|
||||
void lineflush (void);
|
||||
void linecolor (float r, float g, float b);
|
||||
|
||||
void XYmoveto (vec3_t pt);
|
||||
void XYlineto (vec3_t pt);
|
||||
|
||||
typedef enum {dr_wire, dr_flat, dr_texture} drawmode_t;
|
||||
|
||||
|
||||
@interface XYView : NSView
|
||||
{
|
||||
NSRect realbounds, newrect, combinedrect;
|
||||
NSPoint midpoint;
|
||||
int gridsize;
|
||||
float scale;
|
||||
|
||||
// for textured view
|
||||
int xywidth, xyheight;
|
||||
float *xyzbuffer;
|
||||
unsigned *xypicbuffer;
|
||||
|
||||
drawmode_t drawmode;
|
||||
|
||||
// UI links
|
||||
id mode_radio_i;
|
||||
}
|
||||
|
||||
- (float)currentScale;
|
||||
|
||||
- setModeRadio: m;
|
||||
|
||||
- drawMode: sender;
|
||||
- setDrawMode: (drawmode_t)mode;
|
||||
|
||||
- newSuperBounds;
|
||||
- newRealBounds: (NSRect *)nb;
|
||||
|
||||
- addToScrollRange: (float)x :(float)y;
|
||||
- setOrigin: (NSPoint *)pt scale: (float)sc;
|
||||
- centerOn: (vec3_t)org;
|
||||
|
||||
- drawMode: sender;
|
||||
|
||||
- superviewChanged;
|
||||
|
||||
- (int)gridsize;
|
||||
- (float)snapToGrid: (float)f;
|
||||
|
||||
@end
|
1384
tools/Forge/Bundles/MapEdit/XYView.m
Normal file
1384
tools/Forge/Bundles/MapEdit/XYView.m
Normal file
File diff suppressed because it is too large
Load diff
11
tools/Forge/Bundles/MapEdit/ZScrollView.h
Normal file
11
tools/Forge/Bundles/MapEdit/ZScrollView.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#import <AppKit/AppKit.h>
|
||||
|
||||
@interface ZScrollView : NSScrollView
|
||||
{
|
||||
id button1;
|
||||
}
|
||||
|
||||
- initFrame:(NSRect)frameRect button1: b1;
|
||||
- tile;
|
||||
|
||||
@end
|
71
tools/Forge/Bundles/MapEdit/ZScrollView.m
Normal file
71
tools/Forge/Bundles/MapEdit/ZScrollView.m
Normal file
|
@ -0,0 +1,71 @@
|
|||
#import "qedefs.h"
|
||||
|
||||
@implementation ZScrollView
|
||||
|
||||
/*
|
||||
====================
|
||||
initWithFrame: button:
|
||||
|
||||
Initizes a scroll view with a button at it's lower right corner
|
||||
====================
|
||||
*/
|
||||
|
||||
- initWithFrame:(const NSRect *)frameRect button1:b1
|
||||
{
|
||||
[super initWithFrame: frameRect];
|
||||
|
||||
[self addSubview: b1];
|
||||
|
||||
button1 = b1;
|
||||
|
||||
[self setHorizScrollerRequired: YES];
|
||||
[self setVertScrollerRequired: YES];
|
||||
|
||||
[self setBorderType: NS_BEZEL];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
tile
|
||||
|
||||
Adjust the size for the pop up scale menu
|
||||
=================
|
||||
*/
|
||||
|
||||
- tile
|
||||
{
|
||||
NSRect scrollerframe;
|
||||
|
||||
[super tile];
|
||||
[_horizScroller getFrame: &scrollerframe];
|
||||
[button1 setFrame: &scrollerframe];
|
||||
|
||||
scrollerframe.size.width = 0;
|
||||
[_horizScroller setFrame: &scrollerframe];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
-(BOOL) acceptsFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- superviewSizeChanged:(const NSSize *)oldSize
|
||||
{
|
||||
[super superviewSizeChanged: oldSize];
|
||||
|
||||
[[self docView] newSuperBounds];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
42
tools/Forge/Bundles/MapEdit/ZView.h
Normal file
42
tools/Forge/Bundles/MapEdit/ZView.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import "mathlib.h"
|
||||
|
||||
extern id zview_i;
|
||||
|
||||
// zplane controls the objects displayed in the xyview
|
||||
extern float zplane;
|
||||
extern float zplanedir;
|
||||
|
||||
@interface ZView : NSView
|
||||
{
|
||||
float minheight, maxheight;
|
||||
float oldminheight, oldmaxheight;
|
||||
float topbound, bottombound; // for floor clipping
|
||||
|
||||
float scale;
|
||||
|
||||
vec3_t origin;
|
||||
}
|
||||
|
||||
- clearBounds;
|
||||
- getBounds: (float *)top :(float *)bottom;
|
||||
|
||||
- getPoint: (NSPoint *)pt;
|
||||
- setPoint: (NSPoint *)pt;
|
||||
|
||||
- addToHeightRange: (float)height;
|
||||
|
||||
- newRealBounds;
|
||||
- newSuperBounds;
|
||||
|
||||
- XYDrawSelf;
|
||||
|
||||
- (BOOL)XYmouseDown: (NSPoint *)pt;
|
||||
|
||||
- setXYOrigin: (NSPoint *)pt;
|
||||
|
||||
- setOrigin: (NSPoint *)pt scale: (float)sc;
|
||||
|
||||
@end
|
||||
|
869
tools/Forge/Bundles/MapEdit/ZView.m
Normal file
869
tools/Forge/Bundles/MapEdit/ZView.m
Normal file
|
@ -0,0 +1,869 @@
|
|||
|
||||
#import "qedefs.h"
|
||||
|
||||
id zview_i;
|
||||
|
||||
id zscrollview_i, zscalemenu_i, zscalebutton_i;
|
||||
|
||||
float zplane;
|
||||
float zplanedir;
|
||||
|
||||
@implementation ZView
|
||||
|
||||
/*
|
||||
==================
|
||||
initWithFrame:
|
||||
==================
|
||||
*/
|
||||
- (id)initWithFrame:(NSRect)frameRect
|
||||
{
|
||||
NSPoint pt;
|
||||
|
||||
origin[0] = 0.333;
|
||||
origin[1] = 0.333;
|
||||
|
||||
[super initWithFrame:frameRect];
|
||||
[self allocateGState];
|
||||
[self clearBounds];
|
||||
|
||||
zview_i = self;
|
||||
scale = 1;
|
||||
|
||||
//
|
||||
// initialize the pop up menus
|
||||
//
|
||||
zscalebutton_i = [[NSPopUpButton alloc] init];
|
||||
[zscalebutton_i setTarget: self];
|
||||
[zscalebutton_i setAction: @selector(scaleMenuTarget:)];
|
||||
|
||||
[zscalebutton_i addItemWithTitle: @"12.5%"];
|
||||
[zscalebutton_i addItemWithTitle: @"25%"];
|
||||
[zscalebutton_i addItemWithTitle: @"50%"];
|
||||
[zscalebutton_i addItemWithTitle: @"75%"];
|
||||
[zscalebutton_i addItemWithTitle: @"100%"];
|
||||
[zscalebutton_i addItemWithTitle: @"200%"];
|
||||
[zscalebutton_i addItemWithTitle: @"300%"];
|
||||
[zscalebutton_i selectItemAtIndex: 4];
|
||||
|
||||
|
||||
// initialize the scroll view
|
||||
zscrollview_i = [[ZScrollView alloc]
|
||||
initFrame: frameRect
|
||||
button1: zscalebutton_i
|
||||
];
|
||||
[zscrollview_i setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
|
||||
|
||||
[zscrollview_i setDocumentView: self];
|
||||
RELEASE (zscrollview_i);
|
||||
|
||||
// [_super_view setDrawOrigin: 0 : 0];
|
||||
|
||||
minheight = 0;
|
||||
maxheight = 64;
|
||||
|
||||
pt.x = -_bounds.size.width;
|
||||
pt.y = -128;
|
||||
|
||||
[self newRealBounds];
|
||||
|
||||
[self setOrigin: &pt scale: 1];
|
||||
|
||||
return zscrollview_i;
|
||||
}
|
||||
|
||||
- setXYOrigin: (NSPoint *)pt
|
||||
{
|
||||
origin[0] = pt->x + 0.333;
|
||||
origin[1] = pt->y + 0.333;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (float)currentScale
|
||||
{
|
||||
return scale;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
setOrigin:scale:
|
||||
===================
|
||||
*/
|
||||
- setOrigin: (NSPoint *)pt scale: (float)sc
|
||||
{
|
||||
NSRect sframe;
|
||||
NSRect newbounds;
|
||||
|
||||
//
|
||||
// calculate the area visible in the cliprect
|
||||
//
|
||||
scale = sc;
|
||||
|
||||
sframe = [_super_view frame];
|
||||
newbounds = [_super_view frame];
|
||||
newbounds.origin = *pt;
|
||||
newbounds.size.width /= scale;
|
||||
newbounds.size.height /= scale;
|
||||
|
||||
//
|
||||
// union with the realbounds
|
||||
//
|
||||
if (newbounds.origin.y > oldminheight)
|
||||
{
|
||||
newbounds.size.height += newbounds.origin.y - oldminheight;
|
||||
newbounds.origin.y = oldminheight;
|
||||
}
|
||||
if (newbounds.origin.y+newbounds.size.height < oldmaxheight)
|
||||
{
|
||||
newbounds.size.height += oldmaxheight
|
||||
- (newbounds.origin.y + newbounds.size.height);
|
||||
}
|
||||
|
||||
//
|
||||
// redisplay everything
|
||||
//
|
||||
//XXX[quakeed_i disableDisplay];
|
||||
|
||||
//
|
||||
// size this view
|
||||
//
|
||||
[self setBoundsSize: newbounds.size];
|
||||
//XXX[self setDrawOrigin: -newbounds.size.width/2 : newbounds.origin.y];
|
||||
//XXX[self moveTo: -newbounds.size.width/2 : newbounds.origin.y];
|
||||
|
||||
//
|
||||
// scroll and scale the clip view
|
||||
//
|
||||
//XXX[_super_view setDrawSize
|
||||
//XXX : sframe.size.width/scale
|
||||
//XXX : sframe.size.height/scale];
|
||||
//XXX[_super_view setDrawOrigin: pt->x : pt->y];
|
||||
|
||||
//XXX[quakeed_i reenableDisplay];
|
||||
[zscrollview_i display];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
scaleMenuTarget:
|
||||
|
||||
Called when the scaler popup on the window is used
|
||||
====================
|
||||
*/
|
||||
- scaleMenuTarget: sender
|
||||
{
|
||||
char const *item;
|
||||
NSRect visrect, sframe;
|
||||
float nscale;
|
||||
|
||||
item = [[sender titleOfSelectedItem] cString];
|
||||
sscanf (item,"%f",&nscale);
|
||||
nscale /= 100;
|
||||
|
||||
if (nscale == scale)
|
||||
return NULL;
|
||||
|
||||
// keep the center of the view constant
|
||||
visrect = [_super_view bounds];
|
||||
sframe = [_super_view frame];
|
||||
visrect.origin.x += visrect.size.width/2;
|
||||
visrect.origin.y += visrect.size.height/2;
|
||||
|
||||
visrect.origin.x -= sframe.size.width/2/nscale;
|
||||
visrect.origin.y -= sframe.size.height/2/nscale;
|
||||
|
||||
[self setOrigin: &visrect.origin scale: nscale];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- clearBounds
|
||||
{
|
||||
topbound = 999999;
|
||||
bottombound = -999999;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- getBounds: (float *)top :(float *)bottom;
|
||||
{
|
||||
*top = topbound;
|
||||
*bottom = bottombound;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
addToHeightRange:
|
||||
==================
|
||||
*/
|
||||
- addToHeightRange: (float)height
|
||||
{
|
||||
if (height < minheight)
|
||||
minheight = height;
|
||||
if (height > maxheight)
|
||||
maxheight = height;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
newSuperBounds
|
||||
|
||||
When _super_view is resized
|
||||
==================
|
||||
*/
|
||||
- newSuperBounds
|
||||
{
|
||||
oldminheight++;
|
||||
[self newRealBounds];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
newRealBounds
|
||||
|
||||
Should only change the scroll bars, not cause any redraws.
|
||||
If realbounds has shrunk, nothing will change.
|
||||
===================
|
||||
*/
|
||||
- newRealBounds
|
||||
{
|
||||
NSRect sbounds;
|
||||
float vistop, visbottom;
|
||||
|
||||
if (minheight == oldminheight && maxheight == oldmaxheight)
|
||||
return self;
|
||||
|
||||
oldminheight = minheight;
|
||||
oldmaxheight = maxheight;
|
||||
|
||||
minheight -= 16;
|
||||
maxheight += 16;
|
||||
|
||||
//
|
||||
// calculate the area visible in the cliprect
|
||||
//
|
||||
sbounds = [_super_view bounds];
|
||||
visbottom = sbounds.origin.y;
|
||||
vistop = visbottom + sbounds.size.height;
|
||||
|
||||
if (vistop > maxheight)
|
||||
maxheight = vistop;
|
||||
if (visbottom < minheight)
|
||||
minheight = visbottom;
|
||||
if (minheight == _bounds.origin.y && maxheight-minheight == _bounds.size.height)
|
||||
return self;
|
||||
|
||||
sbounds.origin.y = minheight;
|
||||
sbounds.size.height = maxheight - minheight;
|
||||
|
||||
//
|
||||
// size this view
|
||||
//
|
||||
//XXX[quakeed_i disableDisplay];
|
||||
|
||||
//XXX[self suspendNotifyAncestorWhenFrameChanged:YES];
|
||||
[self setBoundsSize: sbounds.size];
|
||||
//XXX[self setDrawOrigin: -sbounds.size.width/2 : sbounds.origin.y];
|
||||
//XXX[self moveTo: -sbounds.size.width/2 : sbounds.origin.y];
|
||||
//XXX[self suspendNotifyAncestorWhenFrameChanged:NO];
|
||||
//XXX[[_super_view _super_view] reflectScroll: _super_view];
|
||||
|
||||
//XXX[quakeed_i reenableDisplay];
|
||||
|
||||
//XXX[[[[self _super_view] _super_view] vertScroller] display];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
drawGrid
|
||||
|
||||
Draws tile markings every 64 units, and grid markings at the grid scale if
|
||||
the grid lines are >= 4 pixels apart
|
||||
|
||||
Rect is in global world (unscaled) coordinates
|
||||
============
|
||||
*/
|
||||
|
||||
- drawGrid: (const NSRect *)rect
|
||||
{
|
||||
int y, stopy;
|
||||
float top,bottom;
|
||||
int left, right;
|
||||
int gridsize;
|
||||
char text[10];
|
||||
BOOL showcoords;
|
||||
|
||||
showcoords = [quakeed_i showCoordinates];
|
||||
|
||||
PSsetlinewidth (0);
|
||||
|
||||
gridsize = [xyview_i gridsize];
|
||||
|
||||
left = _bounds.origin.x;
|
||||
right = 24;
|
||||
|
||||
bottom = rect->origin.y-1;
|
||||
top = rect->origin.y+rect->size.height+2;
|
||||
|
||||
//
|
||||
// grid
|
||||
//
|
||||
// can't just divide by grid size because of negetive coordinate
|
||||
// truncating direction
|
||||
//
|
||||
if (gridsize>= 4/scale)
|
||||
{
|
||||
y = floor(bottom/gridsize);
|
||||
stopy = floor(top/gridsize);
|
||||
|
||||
y *= gridsize;
|
||||
stopy *= gridsize;
|
||||
if (y<bottom)
|
||||
y+= gridsize;
|
||||
|
||||
beginUserPath (upath,NO);
|
||||
|
||||
for ( ; y<=stopy ; y+= gridsize)
|
||||
if (y&31)
|
||||
{
|
||||
UPmoveto (upath, left, y);
|
||||
UPlineto (upath, right, y);
|
||||
}
|
||||
|
||||
endUserPath (upath, dps_ustroke);
|
||||
PSsetrgbcolor (0.8,0.8,1.0); // thin grid color
|
||||
sendUserPath (upath);
|
||||
}
|
||||
|
||||
//
|
||||
// half tiles
|
||||
//
|
||||
y = floor(bottom/32);
|
||||
stopy = floor(top/32);
|
||||
|
||||
if ( ! (((int)y + 4096) & 1) )
|
||||
y++;
|
||||
y *= 32;
|
||||
stopy *= 32;
|
||||
if (stopy >= top)
|
||||
stopy -= 32;
|
||||
|
||||
beginUserPath (upath,NO);
|
||||
|
||||
for ( ; y<=stopy ; y+= 64)
|
||||
{
|
||||
UPmoveto (upath, left, y);
|
||||
UPlineto (upath, right, y);
|
||||
}
|
||||
|
||||
endUserPath (upath, dps_ustroke);
|
||||
PSsetgray (12.0/16.0);
|
||||
sendUserPath (upath);
|
||||
|
||||
//
|
||||
// tiles
|
||||
//
|
||||
y = floor(bottom/64);
|
||||
stopy = floor(top/64);
|
||||
|
||||
y *= 64;
|
||||
stopy *= 64;
|
||||
if (y<bottom)
|
||||
y+= 64;
|
||||
if (stopy >= top)
|
||||
stopy -= 64;
|
||||
|
||||
beginUserPath (upath,NO);
|
||||
PSsetgray (0); // for text
|
||||
PSselectfont("Helvetica-Medium",10/scale);
|
||||
PSrotate(0);
|
||||
|
||||
for ( ; y<=stopy ; y+= 64)
|
||||
{
|
||||
if (showcoords)
|
||||
{
|
||||
sprintf (text, "%i",y);
|
||||
PSmoveto(left,y);
|
||||
PSshow(text);
|
||||
}
|
||||
UPmoveto (upath, left+24, y);
|
||||
UPlineto (upath, right, y);
|
||||
}
|
||||
|
||||
// divider
|
||||
UPmoveto (upath, 0, _bounds.origin.y);
|
||||
UPlineto (upath, 0, _bounds.origin.y + _bounds.size.height);
|
||||
|
||||
endUserPath (upath, dps_ustroke);
|
||||
PSsetgray (10.0/16.0);
|
||||
sendUserPath (upath);
|
||||
|
||||
//
|
||||
// origin
|
||||
//
|
||||
PSsetlinewidth (5);
|
||||
PSsetgray (4.0/16.0);
|
||||
PSmoveto (right,0);
|
||||
PSlineto (left,0);
|
||||
PSstroke ();
|
||||
PSsetlinewidth (0.15);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- drawZplane
|
||||
{
|
||||
PSsetrgbcolor (0.2, 0.2, 0);
|
||||
PSarc (0, zplane, 4, 0, M_PI*2);
|
||||
PSfill ();
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
drawSelf
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
- drawSelf:(const NSRect *)rects :(int)rectCount
|
||||
{
|
||||
NSRect visRect;
|
||||
|
||||
minheight = 999999;
|
||||
maxheight = -999999;
|
||||
|
||||
// allways draw the entire bar
|
||||
[self getVisibleRect:&visRect];
|
||||
rects = &visRect;
|
||||
|
||||
// erase window
|
||||
NSEraseRect (&rects[0]);
|
||||
|
||||
// draw grid
|
||||
[self drawGrid: &rects[0]];
|
||||
|
||||
// draw zplane
|
||||
// [self drawZplane];
|
||||
|
||||
// draw all entities
|
||||
[map_i makeUnselectedPerform: @selector(ZDrawSelf)];
|
||||
|
||||
// possibly resize the view
|
||||
[self newRealBounds];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
XYDrawSelf
|
||||
==============
|
||||
*/
|
||||
- XYDrawSelf
|
||||
{
|
||||
PSsetrgbcolor (0,0.5,1.0);
|
||||
PSsetlinewidth (0.15);
|
||||
PSmoveto (origin[0]-16, origin[1]-16);
|
||||
PSrlineto (32,32);
|
||||
PSmoveto (origin[0]-16, origin[1]+16);
|
||||
PSrlineto (32,-32);
|
||||
PSstroke ();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
getPoint: (NSPoint *)pt
|
||||
==============
|
||||
*/
|
||||
- getPoint: (NSPoint *)pt
|
||||
{
|
||||
pt->x = origin[0] + 0.333; // offset a bit to avoid edge cases
|
||||
pt->y = origin[1] + 0.333;
|
||||
return self;
|
||||
}
|
||||
|
||||
- setPoint: (NSPoint *)pt
|
||||
{
|
||||
origin[0] = pt->x;
|
||||
origin[1] = pt->y;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
MOUSE CLICKING
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
dragLoop:
|
||||
================
|
||||
*/
|
||||
static NSPoint oldreletive;
|
||||
- dragFrom: (NSEvent *)startevent
|
||||
useGrid: (BOOL)ug
|
||||
callback: (void (*) (float dy)) callback
|
||||
{
|
||||
NSEvent *event;
|
||||
NSPoint startpt, newpt;
|
||||
NSPoint reletive, delta;
|
||||
int gridsize;
|
||||
|
||||
gridsize = [xyview_i gridsize];
|
||||
|
||||
startpt = startevent->location;
|
||||
[self convertPoint:&startpt fromView:NULL];
|
||||
|
||||
oldreletive.x = oldreletive.y = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
event = [NSApp getNextEvent:
|
||||
NSLeftMouseUpMask | NSLeftMouseDraggedMask
|
||||
| NSRightMouseUpMask | NSRightMouseDraggedMask];
|
||||
if (event->type == NSLeftMouseUp || event->type == NSRightMouseUp)
|
||||
break;
|
||||
|
||||
newpt = event->location;
|
||||
[self convertPoint:&newpt fromView:NULL];
|
||||
|
||||
reletive.y = newpt.y - startpt.y;
|
||||
|
||||
if (ug)
|
||||
{ // we want truncate towards 0 behavior here
|
||||
reletive.y = gridsize * (int)(reletive.y / gridsize);
|
||||
}
|
||||
|
||||
if (reletive.y == oldreletive.y)
|
||||
continue;
|
||||
|
||||
delta.y = reletive.y - oldreletive.y;
|
||||
oldreletive = reletive;
|
||||
callback (delta.y);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
||||
void ZDragCallback (float dy)
|
||||
{
|
||||
sb_translate[0] = 0;
|
||||
sb_translate[1] = 0;
|
||||
sb_translate[2] = dy;
|
||||
|
||||
[map_i makeSelectedPerform: @selector(translate)];
|
||||
|
||||
[quakeed_i redrawInstance];
|
||||
}
|
||||
|
||||
- selectionDragFrom: (NSEvent*)theEvent
|
||||
{
|
||||
qprintf ("dragging selection");
|
||||
[self dragFrom: theEvent
|
||||
useGrid: YES
|
||||
callback: ZDragCallback ];
|
||||
[quakeed_i updateCamera];
|
||||
qprintf ("");
|
||||
return self;
|
||||
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
void ZScrollCallback (float dy)
|
||||
{
|
||||
NSRect basebounds;
|
||||
NSPoint neworg;
|
||||
float scale;
|
||||
|
||||
[ [zview_i _super_view] getBounds: &basebounds];
|
||||
[zview_i convertRectFromSuperview: &basebounds];
|
||||
|
||||
neworg.y = basebounds.origin.y - dy;
|
||||
|
||||
scale = [zview_i currentScale];
|
||||
|
||||
oldreletive.y -= dy;
|
||||
[zview_i setOrigin: &neworg scale: scale];
|
||||
}
|
||||
|
||||
- scrollDragFrom: (NSEvent*)theEvent
|
||||
{
|
||||
qprintf ("scrolling view");
|
||||
[self dragFrom: theEvent
|
||||
useGrid: YES
|
||||
callback: ZScrollCallback ];
|
||||
qprintf ("");
|
||||
return self;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
void ZControlCallback (float dy)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<numcontrolpoints ; i++)
|
||||
controlpoints[i][2] += dy;
|
||||
|
||||
[[map_i selectedBrush] calcWindings];
|
||||
[quakeed_i redrawInstance];
|
||||
}
|
||||
|
||||
- (BOOL)planeDragFrom: (NSEvent*)theEvent
|
||||
{
|
||||
NSPoint pt;
|
||||
vec3_t dragpoint;
|
||||
|
||||
if ([map_i numSelected] != 1)
|
||||
return NO;
|
||||
|
||||
pt= theEvent->location;
|
||||
[self convertPoint:&pt fromView:NULL];
|
||||
|
||||
dragpoint[0] = origin[0];
|
||||
dragpoint[1] = origin[1];
|
||||
dragpoint[2] = pt.y;
|
||||
|
||||
[[map_i selectedBrush] getZdragface: dragpoint];
|
||||
if (!numcontrolpoints)
|
||||
return NO;
|
||||
|
||||
qprintf ("dragging brush plane");
|
||||
|
||||
pt= theEvent->location;
|
||||
[self convertPoint:&pt fromView:NULL];
|
||||
|
||||
[self dragFrom: theEvent
|
||||
useGrid: YES
|
||||
callback: ZControlCallback ];
|
||||
|
||||
[[map_i selectedBrush] removeIfInvalid];
|
||||
|
||||
[quakeed_i updateCamera];
|
||||
qprintf ("");
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
|
||||
/*
|
||||
===================
|
||||
mouseDown
|
||||
===================
|
||||
*/
|
||||
- (void)mouseDown:(NSEvent *)theEvent
|
||||
{
|
||||
NSPoint pt;
|
||||
int flags;
|
||||
vec3_t p1;
|
||||
|
||||
pt= theEvent->location;
|
||||
[self convertPoint:&pt fromView:NULL];
|
||||
|
||||
p1[0] = origin[0];
|
||||
p1[1] = origin[1];
|
||||
p1[2] = pt.y;
|
||||
|
||||
flags = theEvent->flags & (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask);
|
||||
|
||||
//
|
||||
// shift click to select / deselect a brush from the world
|
||||
//
|
||||
if (flags == NSShiftKeyMask)
|
||||
{
|
||||
[map_i selectRay: p1 : p1 : NO];
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// alt click = set entire brush texture
|
||||
//
|
||||
if (flags == NSAlternateKeyMask)
|
||||
{
|
||||
[map_i setTextureRay: p1 : p1 : YES];
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// control click = position view
|
||||
//
|
||||
if (flags == NSControlKeyMask)
|
||||
{
|
||||
[cameraview_i setZOrigin: pt.y];
|
||||
[quakeed_i updateAll];
|
||||
[cameraview_i ZmouseDown: &pt flags:theEvent->flags];
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// bare click to drag icons or new brush drag
|
||||
//
|
||||
if ( flags == 0 )
|
||||
{
|
||||
// check eye
|
||||
if ( [cameraview_i ZmouseDown: &pt flags:theEvent->flags] )
|
||||
return;
|
||||
|
||||
if ([map_i numSelected])
|
||||
{
|
||||
if ( pt.x > 0)
|
||||
{
|
||||
if ([self planeDragFrom: theEvent])
|
||||
return;
|
||||
}
|
||||
[self selectionDragFrom: theEvent];
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
qprintf ("bad flags for click");
|
||||
NopSound ();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
rightMouseDown
|
||||
===================
|
||||
*/
|
||||
- (void)rightMouseDown:(NSEvent *)theEvent
|
||||
{
|
||||
NSPoint pt;
|
||||
int flags;
|
||||
|
||||
pt= theEvent->location;
|
||||
[self convertPoint:&pt fromView:NULL];
|
||||
|
||||
flags = theEvent->flags & (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask);
|
||||
|
||||
|
||||
//
|
||||
// click = scroll view
|
||||
//
|
||||
if (flags == 0)
|
||||
{
|
||||
[self scrollDragFrom: theEvent];
|
||||
}
|
||||
|
||||
qprintf ("bad flags for click");
|
||||
NopSound ();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
XY mouse view methods
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
================
|
||||
modalMoveLoop
|
||||
================
|
||||
*/
|
||||
- modalMoveLoop: (NSPoint *)basept :(vec3_t)movemod : converter
|
||||
{
|
||||
vec3_t originbase;
|
||||
NSEvent *event;
|
||||
NSPoint newpt;
|
||||
vec3_t delta;
|
||||
|
||||
int i;
|
||||
|
||||
VectorCopy (origin, originbase);
|
||||
|
||||
//
|
||||
// modal event loop using instance drawing
|
||||
//
|
||||
goto drawentry;
|
||||
|
||||
while (event->type != NSLeftMouseUp)
|
||||
{
|
||||
//
|
||||
// calculate new point
|
||||
//
|
||||
newpt = event->location;
|
||||
[converter convertPoint:&newpt fromView:NULL];
|
||||
|
||||
delta[0] = newpt.x-basept->x;
|
||||
delta[1] = newpt.y-basept->y;
|
||||
delta[2] = delta[1]; // height change
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
origin[i] = originbase[i]+movemod[i]*delta[i];
|
||||
|
||||
|
||||
drawentry:
|
||||
//
|
||||
// instance draw new frame
|
||||
//
|
||||
[quakeed_i newinstance];
|
||||
[self display];
|
||||
NSPing ();
|
||||
|
||||
event = [NSApp getNextEvent:
|
||||
NSLeftMouseUpMask | NSLeftMouseDraggedMask];
|
||||
}
|
||||
|
||||
//
|
||||
// draw the brush back into the window buffer
|
||||
//
|
||||
// [xyview_i display];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
XYmouseDown
|
||||
===============
|
||||
*/
|
||||
- (BOOL)XYmouseDown: (NSPoint *)pt
|
||||
{
|
||||
vec3_t movemod;
|
||||
|
||||
if (fabs(pt->x - origin[0]) > 16
|
||||
|| fabs(pt->y - origin[1]) > 16)
|
||||
return NO;
|
||||
|
||||
movemod[0] = 1;
|
||||
movemod[1] = 1;
|
||||
movemod[2] = 0;
|
||||
|
||||
[self modalMoveLoop: pt : movemod : xyview_i];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
611
tools/Forge/Bundles/MapEdit/cmdlib.c
Normal file
611
tools/Forge/Bundles/MapEdit/cmdlib.c
Normal file
|
@ -0,0 +1,611 @@
|
|||
// cmdlib.c
|
||||
|
||||
#include "cmdlib.h"
|
||||
|
||||
#define PATHSEPERATOR '/'
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
I_FloatTime
|
||||
================
|
||||
*/
|
||||
double I_FloatTime (void)
|
||||
{
|
||||
struct timeval tp;
|
||||
struct timezone tzp;
|
||||
static int secbase;
|
||||
|
||||
gettimeofday(&tp, &tzp);
|
||||
|
||||
if (!secbase)
|
||||
{
|
||||
secbase = tp.tv_sec;
|
||||
return tp.tv_usec/1000000.0;
|
||||
}
|
||||
|
||||
return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
|
||||
}
|
||||
|
||||
|
||||
char com_token[1024];
|
||||
boolean com_eof;
|
||||
|
||||
/*
|
||||
==============
|
||||
COM_Parse
|
||||
|
||||
Parse a token out of a string
|
||||
==============
|
||||
*/
|
||||
char *COM_Parse (char *data)
|
||||
{
|
||||
int c;
|
||||
int len;
|
||||
|
||||
com_eof = false;
|
||||
|
||||
len = 0;
|
||||
com_token[0] = 0;
|
||||
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
// skip whitespace
|
||||
skipwhite:
|
||||
while ( (c = *data) <= ' ')
|
||||
{
|
||||
if (c == 0)
|
||||
{
|
||||
com_eof = true;
|
||||
return NULL; // end of file;
|
||||
}
|
||||
data++;
|
||||
}
|
||||
|
||||
// skip // comments
|
||||
if (c=='/' && data[1] == '/')
|
||||
{
|
||||
while (*data && *data != '\n')
|
||||
data++;
|
||||
goto skipwhite;
|
||||
}
|
||||
|
||||
|
||||
// handle quoted strings specially
|
||||
if (c == '\"')
|
||||
{
|
||||
data++;
|
||||
do
|
||||
{
|
||||
c = *data++;
|
||||
if (c=='\"')
|
||||
{
|
||||
com_token[len] = 0;
|
||||
return data;
|
||||
}
|
||||
com_token[len] = c;
|
||||
len++;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
// parse single characters
|
||||
if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
|
||||
{
|
||||
com_token[len] = c;
|
||||
len++;
|
||||
com_token[len] = 0;
|
||||
return data+1;
|
||||
}
|
||||
|
||||
// parse a regular word
|
||||
do
|
||||
{
|
||||
com_token[len] = c;
|
||||
data++;
|
||||
len++;
|
||||
c = *data;
|
||||
if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
|
||||
break;
|
||||
} while (c>32);
|
||||
|
||||
com_token[len] = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
=
|
||||
= filelength
|
||||
=
|
||||
================
|
||||
*/
|
||||
|
||||
int filelength (int handle)
|
||||
{
|
||||
struct stat fileinfo;
|
||||
|
||||
if (fstat (handle,&fileinfo) == -1)
|
||||
{
|
||||
fprintf (stderr,"Error fstating");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
return fileinfo.st_size;
|
||||
}
|
||||
|
||||
int tell (int handle)
|
||||
{
|
||||
return lseek (handle, 0, L_INCR);
|
||||
}
|
||||
|
||||
char *strupr (char *start)
|
||||
{
|
||||
char *in;
|
||||
in = start;
|
||||
while (*in)
|
||||
{
|
||||
*in = toupper(*in);
|
||||
in++;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
char *strlower (char *start)
|
||||
{
|
||||
char *in;
|
||||
in = start;
|
||||
while (*in)
|
||||
{
|
||||
*in = tolower(*in);
|
||||
in++;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
char *getcwd (char *path, int length)
|
||||
{
|
||||
return getwd(path);
|
||||
}
|
||||
|
||||
|
||||
/* globals for command line args */
|
||||
extern int NXArgc;
|
||||
extern char **NXArgv;
|
||||
#define myargc NXArgc
|
||||
#define myargv NXArgv
|
||||
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
MISC FUNCTIONS
|
||||
|
||||
=============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
=
|
||||
= CheckParm
|
||||
=
|
||||
= Checks for the given parameter in the program's command line arguments
|
||||
=
|
||||
= Returns the argument number (1 to argc-1) or 0 if not present
|
||||
=
|
||||
=================
|
||||
*/
|
||||
|
||||
int CheckParm (char *check)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1;i<myargc;i++)
|
||||
{
|
||||
if ( !stricmp(check, myargv[i]) )
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int SafeOpenWrite (char *filename)
|
||||
{
|
||||
int handle;
|
||||
|
||||
umask (0);
|
||||
|
||||
handle = open(filename,O_RDWR | O_CREAT | O_TRUNC
|
||||
, 0666);
|
||||
|
||||
if (handle == -1)
|
||||
Error ("Error opening %s: %s",filename,strerror(errno));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
int SafeOpenRead (char *filename)
|
||||
{
|
||||
int handle;
|
||||
|
||||
handle = open(filename,O_RDONLY);
|
||||
|
||||
if (handle == -1)
|
||||
Error ("Error opening %s: %s",filename,strerror(errno));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
void SafeRead (int handle, void *buffer, long count)
|
||||
{
|
||||
int iocount;
|
||||
|
||||
iocount = read (handle,buffer,count);
|
||||
if (iocount != count)
|
||||
Error ("File read failure");
|
||||
}
|
||||
|
||||
|
||||
void SafeWrite (int handle, void *buffer, long count)
|
||||
{
|
||||
int iocount;
|
||||
|
||||
iocount = write (handle,buffer,count);
|
||||
if (iocount != count)
|
||||
Error ("File write failure");
|
||||
}
|
||||
|
||||
|
||||
void *SafeMalloc (long size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
ptr = malloc (size);
|
||||
|
||||
if (!ptr)
|
||||
Error ("Malloc failure for %lu bytes",size);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
=
|
||||
= LoadFile
|
||||
=
|
||||
= appends a 0 byte
|
||||
==============
|
||||
*/
|
||||
|
||||
long LoadFile (char *filename, void **bufferptr)
|
||||
{
|
||||
int handle;
|
||||
long length;
|
||||
void *buffer;
|
||||
|
||||
handle = SafeOpenRead (filename);
|
||||
length = filelength (handle);
|
||||
buffer = SafeMalloc (length+1);
|
||||
((char *)buffer)[length] = 0;
|
||||
SafeRead (handle, buffer, length);
|
||||
close (handle);
|
||||
|
||||
*bufferptr = buffer;
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
=
|
||||
= SaveFile
|
||||
=
|
||||
==============
|
||||
*/
|
||||
|
||||
void SaveFile (char *filename, void *buffer, long count)
|
||||
{
|
||||
int handle;
|
||||
|
||||
handle = SafeOpenWrite (filename);
|
||||
SafeWrite (handle, buffer, count);
|
||||
close (handle);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DefaultExtension (char *path, char *extension)
|
||||
{
|
||||
char *src;
|
||||
//
|
||||
// if path doesn't have a .EXT, append extension
|
||||
// (extension should include the .)
|
||||
//
|
||||
src = path + strlen(path) - 1;
|
||||
|
||||
while (*src != PATHSEPERATOR && src != path)
|
||||
{
|
||||
if (*src == '.')
|
||||
return; // it has an extension
|
||||
src--;
|
||||
}
|
||||
|
||||
strcat (path, extension);
|
||||
}
|
||||
|
||||
|
||||
void DefaultPath (char *path, char *basepath)
|
||||
{
|
||||
char temp[128];
|
||||
|
||||
if (path[0] == PATHSEPERATOR)
|
||||
return; // absolute path location
|
||||
strcpy (temp,path);
|
||||
strcpy (path,basepath);
|
||||
strcat (path,temp);
|
||||
}
|
||||
|
||||
|
||||
void StripFilename (char *path)
|
||||
{
|
||||
int length;
|
||||
|
||||
length = strlen(path)-1;
|
||||
while (length > 0 && path[length] != PATHSEPERATOR)
|
||||
length--;
|
||||
path[length] = 0;
|
||||
}
|
||||
|
||||
void StripExtension (char *path)
|
||||
{
|
||||
int length;
|
||||
|
||||
length = strlen(path)-1;
|
||||
while (length > 0 && path[length] != '.')
|
||||
length--;
|
||||
if (length)
|
||||
path[length] = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
=
|
||||
= Extract file parts
|
||||
=
|
||||
====================
|
||||
*/
|
||||
|
||||
void ExtractFilePath (char *path, char *dest)
|
||||
{
|
||||
char *src;
|
||||
|
||||
src = path + strlen(path) - 1;
|
||||
|
||||
//
|
||||
// back up until a \ or the start
|
||||
//
|
||||
while (src != path && *(src-1) != PATHSEPERATOR)
|
||||
src--;
|
||||
|
||||
memcpy (dest, path, src-path);
|
||||
dest[src-path] = 0;
|
||||
}
|
||||
|
||||
void ExtractFileBase (char *path, char *dest)
|
||||
{
|
||||
char *src;
|
||||
|
||||
src = path + strlen(path) - 1;
|
||||
|
||||
//
|
||||
// back up until a \ or the start
|
||||
//
|
||||
while (src != path && *(src-1) != PATHSEPERATOR)
|
||||
src--;
|
||||
|
||||
while (*src && *src != '.')
|
||||
{
|
||||
*dest++ = *src++;
|
||||
}
|
||||
*dest = 0;
|
||||
}
|
||||
|
||||
void ExtractFileExtension (char *path, char *dest)
|
||||
{
|
||||
char *src;
|
||||
|
||||
src = path + strlen(path) - 1;
|
||||
|
||||
//
|
||||
// back up until a . or the start
|
||||
//
|
||||
while (src != path && *(src-1) != '.')
|
||||
src--;
|
||||
if (src == path)
|
||||
{
|
||||
*dest = 0; // no extension
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy (dest,src);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
=
|
||||
= ParseNum / ParseHex
|
||||
=
|
||||
==============
|
||||
*/
|
||||
|
||||
long ParseHex (char *hex)
|
||||
{
|
||||
char *str;
|
||||
long num;
|
||||
|
||||
num = 0;
|
||||
str = hex;
|
||||
|
||||
while (*str)
|
||||
{
|
||||
num <<= 4;
|
||||
if (*str >= '0' && *str <= '9')
|
||||
num += *str-'0';
|
||||
else if (*str >= 'a' && *str <= 'f')
|
||||
num += 10 + *str-'a';
|
||||
else if (*str >= 'A' && *str <= 'F')
|
||||
num += 10 + *str-'A';
|
||||
else
|
||||
Error ("Bad hex number: %s",hex);
|
||||
str++;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
long ParseNum (char *str)
|
||||
{
|
||||
if (str[0] == '$')
|
||||
return ParseHex (str+1);
|
||||
if (str[0] == '0' && str[1] == 'x')
|
||||
return ParseHex (str+2);
|
||||
return atol (str);
|
||||
}
|
||||
|
||||
|
||||
int GetKey (void)
|
||||
{
|
||||
return getchar ();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============================================================================
|
||||
|
||||
BYTE ORDER FUNCTIONS
|
||||
|
||||
============================================================================
|
||||
*/
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
|
||||
short LittleShort (short l)
|
||||
{
|
||||
byte b1,b2;
|
||||
|
||||
b1 = l&255;
|
||||
b2 = (l>>8)&255;
|
||||
|
||||
return (b1<<8) + b2;
|
||||
}
|
||||
|
||||
short BigShort (short l)
|
||||
{
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
long LittleLong (long l)
|
||||
{
|
||||
byte b1,b2,b3,b4;
|
||||
|
||||
b1 = l&255;
|
||||
b2 = (l>>8)&255;
|
||||
b3 = (l>>16)&255;
|
||||
b4 = (l>>24)&255;
|
||||
|
||||
return ((long)b1<<24) + ((long)b2<<16) + ((long)b3<<8) + b4;
|
||||
}
|
||||
|
||||
long BigLong (long l)
|
||||
{
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
float LittleFloat (float l)
|
||||
{
|
||||
union {byte b[4]; float f;} in, out;
|
||||
|
||||
in.f = l;
|
||||
out.b[0] = in.b[3];
|
||||
out.b[1] = in.b[2];
|
||||
out.b[2] = in.b[1];
|
||||
out.b[3] = in.b[0];
|
||||
|
||||
return out.f;
|
||||
}
|
||||
|
||||
float BigFloat (float l)
|
||||
{
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
|
||||
short BigShort (short l)
|
||||
{
|
||||
byte b1,b2;
|
||||
|
||||
b1 = l&255;
|
||||
b2 = (l>>8)&255;
|
||||
|
||||
return (b1<<8) + b2;
|
||||
}
|
||||
|
||||
short LittleShort (short l)
|
||||
{
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
long BigLong (long l)
|
||||
{
|
||||
byte b1,b2,b3,b4;
|
||||
|
||||
b1 = l&255;
|
||||
b2 = (l>>8)&255;
|
||||
b3 = (l>>16)&255;
|
||||
b4 = (l>>24)&255;
|
||||
|
||||
return ((long)b1<<24) + ((long)b2<<16) + ((long)b3<<8) + b4;
|
||||
}
|
||||
|
||||
long LittleLong (long l)
|
||||
{
|
||||
return l;
|
||||
}
|
||||
|
||||
float BigFloat (float l)
|
||||
{
|
||||
union {byte b[4]; float f;} in, out;
|
||||
|
||||
in.f = l;
|
||||
out.b[0] = in.b[3];
|
||||
out.b[1] = in.b[2];
|
||||
out.b[2] = in.b[1];
|
||||
out.b[3] = in.b[0];
|
||||
|
||||
return out.f;
|
||||
}
|
||||
|
||||
float LittleFloat (float l)
|
||||
{
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
62
tools/Forge/Bundles/MapEdit/cmdlib.h
Normal file
62
tools/Forge/Bundles/MapEdit/cmdlib.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
// cmdlib.h
|
||||
|
||||
#ifndef __CMDLIB__
|
||||
#define __CMDLIB__
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define strcmpi strcasecmp
|
||||
#define stricmp strcasecmp
|
||||
char *strupr (char *in);
|
||||
char *strlower (char *in);
|
||||
int filelength (int handle);
|
||||
int tell (int handle);
|
||||
|
||||
#ifndef __BYTEBOOL__
|
||||
#define __BYTEBOOL__
|
||||
typedef enum {false, true} boolean;
|
||||
typedef unsigned char byte;
|
||||
#endif
|
||||
|
||||
double I_FloatTime (void);
|
||||
|
||||
int GetKey (void);
|
||||
|
||||
void Error (char *error, ...);
|
||||
int CheckParm (char *check);
|
||||
|
||||
int SafeOpenWrite (char *filename);
|
||||
int SafeOpenRead (char *filename);
|
||||
void SafeRead (int handle, void *buffer, long count);
|
||||
void SafeWrite (int handle, void *buffer, long count);
|
||||
void *SafeMalloc (long size);
|
||||
|
||||
long LoadFile (char *filename, void **bufferptr);
|
||||
void SaveFile (char *filename, void *buffer, long count);
|
||||
|
||||
void DefaultExtension (char *path, char *extension);
|
||||
void DefaultPath (char *path, char *basepath);
|
||||
void StripFilename (char *path);
|
||||
void StripExtension (char *path);
|
||||
|
||||
void ExtractFilePath (char *path, char *dest);
|
||||
void ExtractFileBase (char *path, char *dest);
|
||||
void ExtractFileExtension (char *path, char *dest);
|
||||
|
||||
long ParseNum (char *str);
|
||||
|
||||
short BigShort (short l);
|
||||
short LittleShort (short l);
|
||||
long BigLong (long l);
|
||||
long LittleLong (long l);
|
||||
float BigFloat (float l);
|
||||
float LittleFloat (float l);
|
||||
|
||||
extern char com_token[1024];
|
||||
extern boolean com_eof;
|
||||
|
||||
char *COM_Parse (char *data);
|
||||
|
||||
#endif
|
175
tools/Forge/Bundles/MapEdit/help.txt
Normal file
175
tools/Forge/Bundles/MapEdit/help.txt
Normal file
|
@ -0,0 +1,175 @@
|
|||
QuakeEd 2.0
|
||||
by John Carmack and John Romero
|
||||
|
||||
Terms:
|
||||
|
||||
Brush
|
||||
A convex polyhedron with a textruedef for each face.
|
||||
|
||||
Entity
|
||||
A set of brushes and key/value attributes. The world is allways entity 0. Lights, doors, monsters, etc are all entities. Entities can either have a fixed, non-modifiable size (monsters, lights, etc), or can be represented by an arbitrary collection of brushes (doors, plats, bridges, etc).
|
||||
|
||||
An entity is created by selecting one or more brushes in the world, then selecting a class in the Entity Browser, then clicking Create Entity (or double click the class name). If the entity class has a fixed size, the selected brushes are removed and a fixed sized brush is put in their place. Otherwise, the selected brushes are removed from the world entity and made the contents of the newly created entity.
|
||||
|
||||
Current Entity
|
||||
|
||||
Selection
|
||||
The current selection of brushes is drawn with a red outline.
|
||||
|
||||
The selection is not cleared when loading a new map, so selections can be brought over from other files.
|
||||
|
||||
Texturedef
|
||||
A texture name with offset numbers and flip flags. Determines what will be drawn on a brush face. By default, all faces of a brush have the same texturedef, but you can explicityly change single faces if desired.
|
||||
|
||||
|
||||
|
||||
Using QuakeEd:
|
||||
|
||||
The NeXT "cmd" key is the alt key on the left side of the space bar. "alt" is the alt key on the right side.
|
||||
|
||||
Camera View
|
||||
|
||||
click
|
||||
Sets the current texturedef to the value of the face clicked on.
|
||||
|
||||
Does not change any selection status.
|
||||
|
||||
f there are any brushes selected, they will all be changed to this texturedef.
|
||||
|
||||
If there are no brushes selected, the height of the next new brush created will be set based on the size of the brush clicked on.
|
||||
|
||||
|
||||
shift-click
|
||||
Toggles the selection status of the clicked on brush.
|
||||
|
||||
If the brush is part of a different entity than the current entity, all brushes in current are deselected before selecting the new brush. The current entity inpector is then changed to reflect the new entity.
|
||||
|
||||
alt-click
|
||||
Sets the entire brush clicked on to the current texturedef. Does not change any selection status.
|
||||
|
||||
alt-ctrl-click
|
||||
Sets the single face clicked on to the current texturedef without changing the other faces.
|
||||
|
||||
|
||||
right-click drag
|
||||
Change camera angle.
|
||||
|
||||
|
||||
XYView
|
||||
|
||||
double click
|
||||
Positions the Z checker at the click point.
|
||||
|
||||
click drag with no selection
|
||||
The camera or Z checker will be dragged if directly clicked on, otherwise a new rectilinear brush will be dragged out from the click point. The initial height of the brush is determined by the last brush selected or camera-clicked on.
|
||||
|
||||
If the button is released when the new brush has no volume (a veneer), the brush is discarded.
|
||||
|
||||
click drag with a single brush selected
|
||||
If the click is inside the selected brush, the brush will be dragged around without changing its shape.
|
||||
|
||||
If the click is outside the brush, dragging will move the planes that face the click point. Planes will only move directly along their normals, so a recrilinear brush will stay rectilinear.
|
||||
|
||||
Spawned entity brushes (monsters, etc) can not be resized at all.
|
||||
|
||||
click drag with multiple brushes selected
|
||||
No group shape changes are allowed, so the click must be inside one of the brushes, and all brushes are moved together.
|
||||
|
||||
cmd-click drag with a single brush selected
|
||||
Holds the plane that faces the click point, and shears the adjacent planes. To grab a single corner, click outside the brush in the overlap area between two planes. If you click inside the brush, the top plane will be held.
|
||||
|
||||
shift-click
|
||||
Toggles the selection status of the clicked on brush as in the camera view.
|
||||
|
||||
Entities are allways checked before the world, so you can shift-click on entities even if there is a ceiling above them.
|
||||
|
||||
cmd-shift click
|
||||
Connects two entities. The current entity has a target key generated, and the clicked on entity has a targetname key created.
|
||||
|
||||
alt-click
|
||||
alt-ctrl-click
|
||||
Sets textures as in the camera view.
|
||||
|
||||
ctrl-click
|
||||
Moves the camera to the point clicked on. It can be dragged dynamically if desired.
|
||||
|
||||
right click
|
||||
right ctrl-click
|
||||
Points the camera towards the click point. It can be dragged if desired.
|
||||
|
||||
cmd-right click drag
|
||||
Slides the XY view to expose more space.
|
||||
|
||||
alt-right click
|
||||
Positions a clipper tool endpoint. The first click places it at the minimum Z of the selection. A second click at the same point moves it to the maximum Z of the selection.
|
||||
|
||||
Two clipper points will carve as a straight line in the XY view. Three points (one of which must be at the max point instead of the min) will carve a slope.
|
||||
|
||||
Escape removes the current clippers, and enter actually cuts the selection.
|
||||
|
||||
|
||||
ZView
|
||||
|
||||
click drag
|
||||
The camera icon can be dragged if it is clicked on, otherwise no action if there is not a selection.
|
||||
|
||||
If multiple brushes are selected, they will allways be dragged as a unit, no matter where the click is.
|
||||
|
||||
If a single brush is selected, the location of the click determines if the drag will move the entire brush or just the top or bottom plane.
|
||||
|
||||
A click anywhere on the left side of the Z view will drag the full brush. If the click is on the right side, it will drag just the plane closest to it, changing the size of the brush. Clicking inside a brush on the right side also drags the full brush.
|
||||
|
||||
shift-click
|
||||
Toggles the selection status of the clicked on brush as in the camera view.
|
||||
|
||||
alt-click
|
||||
Sets the entire brush texture as in the camera view. (alt-ctrl click is meaningless without a direction)
|
||||
|
||||
ctrl-click
|
||||
Moves the camera to the height clicked on. It can be dragged dynamically if desired.
|
||||
|
||||
right click drag
|
||||
Slides the Z view to expose more space.
|
||||
|
||||
|
||||
Keyboard
|
||||
|
||||
F2 : set camera wire draw mode
|
||||
F3 : set camera solid draw mode
|
||||
F4 : set camera textured draw mode
|
||||
|
||||
F5 : set XY wire draw mode
|
||||
F6 : set XY textured draw mode
|
||||
|
||||
F8 : Home camera pitch angle
|
||||
|
||||
F12 : geometrically subtract the selection from the rest of the world.
|
||||
|
||||
PageUp : moves the camera view to the next floor up.
|
||||
PageDown : moves the camera view to the next floor down.
|
||||
|
||||
cursor arrows, a z d c , .
|
||||
Step move the camera with quake controls.
|
||||
|
||||
Esc or End: deselect all selected brushes
|
||||
Backspace : delete all selected brushes
|
||||
|
||||
/ : Flip the normal on the clipper tool
|
||||
|
||||
Enter : Carve selected brushes by the clipper tool
|
||||
|
||||
Space : clone all selected brushes. deselects the originals and selects the copies after offsetting by the grid size.
|
||||
|
||||
Keypad arrows: moves the current selection in gridsize incrmenets.
|
||||
|
||||
+ - : moves the current selection up or down by gridsize increments.
|
||||
|
||||
cmd-1 - cmd-7 : change inspectors
|
||||
|
||||
|
||||
Text commands
|
||||
|
||||
Obscure commands are entered by typing the command into the command field (under the output field).
|
||||
|
||||
texname : prints the names of the textures on the selected brush. Used to track down problems with accidentally taking graphics out of a wadfile.
|
||||
|
11858
tools/Forge/Bundles/MapEdit/jrbase1.map
Normal file
11858
tools/Forge/Bundles/MapEdit/jrbase1.map
Normal file
File diff suppressed because it is too large
Load diff
93
tools/Forge/Bundles/MapEdit/mathlib.c
Normal file
93
tools/Forge/Bundles/MapEdit/mathlib.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
// mathlib.c -- math primitives
|
||||
|
||||
#include "mathlib.h"
|
||||
#include <math.h>
|
||||
|
||||
vec3_t vec3_origin = {0,0,0};
|
||||
|
||||
double VectorLength(vec3_t v)
|
||||
{
|
||||
int i;
|
||||
double length;
|
||||
|
||||
length = 0;
|
||||
for (i=0 ; i< 3 ; i++)
|
||||
length += v[i]*v[i];
|
||||
length = sqrt (length); // FIXME
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc)
|
||||
{
|
||||
vc[0] = va[0] + scale*vb[0];
|
||||
vc[1] = va[1] + scale*vb[1];
|
||||
vc[2] = va[2] + scale*vb[2];
|
||||
}
|
||||
|
||||
boolean VectorCompare (vec3_t v1, vec3_t v2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
if (v1[i] != v2[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross)
|
||||
{
|
||||
cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
|
||||
cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
|
||||
cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
|
||||
}
|
||||
|
||||
vec_t _DotProduct (vec3_t v1, vec3_t v2)
|
||||
{
|
||||
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
|
||||
}
|
||||
|
||||
void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out)
|
||||
{
|
||||
out[0] = va[0]-vb[0];
|
||||
out[1] = va[1]-vb[1];
|
||||
out[2] = va[2]-vb[2];
|
||||
}
|
||||
|
||||
void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out)
|
||||
{
|
||||
out[0] = va[0]+vb[0];
|
||||
out[1] = va[1]+vb[1];
|
||||
out[2] = va[2]+vb[2];
|
||||
}
|
||||
|
||||
void _VectorCopy (vec3_t in, vec3_t out)
|
||||
{
|
||||
out[0] = in[0];
|
||||
out[1] = in[1];
|
||||
out[2] = in[2];
|
||||
}
|
||||
|
||||
void VectorNormalize (vec3_t v)
|
||||
{
|
||||
int i;
|
||||
float length;
|
||||
|
||||
length = 0;
|
||||
for (i=0 ; i< 3 ; i++)
|
||||
length += v[i]*v[i];
|
||||
length = sqrt (length);
|
||||
|
||||
for (i=0 ; i< 3 ; i++)
|
||||
v[i] /= length;
|
||||
}
|
||||
|
||||
void VectorScale (vec3_t v, vec_t scale, vec3_t out)
|
||||
{
|
||||
out[0] = v[0] * scale;
|
||||
out[1] = v[1] * scale;
|
||||
out[2] = v[2] * scale;
|
||||
}
|
||||
|
31
tools/Forge/Bundles/MapEdit/mathlib.h
Normal file
31
tools/Forge/Bundles/MapEdit/mathlib.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef __MATHLIB__
|
||||
#define __MATHLIB__
|
||||
|
||||
#include "cmdlib.h"
|
||||
|
||||
// mathlib.h
|
||||
|
||||
typedef float vec_t;
|
||||
typedef vec_t vec3_t[3];
|
||||
|
||||
extern vec3_t vec3_origin;
|
||||
|
||||
boolean VectorCompare (vec3_t v1, vec3_t v2);
|
||||
|
||||
#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
|
||||
#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];}
|
||||
#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
|
||||
#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];}
|
||||
|
||||
vec_t _DotProduct (vec3_t v1, vec3_t v2);
|
||||
void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out);
|
||||
void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out);
|
||||
void _VectorCopy (vec3_t in, vec3_t out);
|
||||
|
||||
void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross);
|
||||
void VectorNormalize (vec3_t v);
|
||||
void VectorScale (vec3_t v, vec_t scale, vec3_t out);
|
||||
double VectorLength(vec3_t v);
|
||||
void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc);
|
||||
|
||||
#endif
|
274
tools/Forge/Bundles/MapEdit/misc.m
Normal file
274
tools/Forge/Bundles/MapEdit/misc.m
Normal file
|
@ -0,0 +1,274 @@
|
|||
|
||||
#include "qedefs.h"
|
||||
|
||||
|
||||
char token[MAXTOKEN];
|
||||
boolean unget;
|
||||
char *script_p;
|
||||
int scriptline;
|
||||
|
||||
void StartTokenParsing (char *data)
|
||||
{
|
||||
scriptline = 1;
|
||||
script_p = data;
|
||||
unget = false;
|
||||
}
|
||||
|
||||
boolean GetToken (boolean crossline)
|
||||
{
|
||||
char *token_p;
|
||||
|
||||
if (unget) // is a token allready waiting?
|
||||
return true;
|
||||
|
||||
//
|
||||
// skip space
|
||||
//
|
||||
skipspace:
|
||||
while (*script_p <= 32)
|
||||
{
|
||||
if (!*script_p)
|
||||
{
|
||||
if (!crossline)
|
||||
Error ("Line %i is incomplete",scriptline);
|
||||
return false;
|
||||
}
|
||||
if (*script_p++ == '\n')
|
||||
{
|
||||
if (!crossline)
|
||||
Error ("Line %i is incomplete",scriptline);
|
||||
scriptline++;
|
||||
}
|
||||
}
|
||||
|
||||
if (script_p[0] == '/' && script_p[1] == '/') // comment field
|
||||
{
|
||||
if (!crossline)
|
||||
Error ("Line %i is incomplete\n",scriptline);
|
||||
while (*script_p++ != '\n')
|
||||
if (!*script_p)
|
||||
{
|
||||
if (!crossline)
|
||||
Error ("Line %i is incomplete",scriptline);
|
||||
return false;
|
||||
}
|
||||
goto skipspace;
|
||||
}
|
||||
|
||||
//
|
||||
// copy token
|
||||
//
|
||||
token_p = token;
|
||||
|
||||
if (*script_p == '"')
|
||||
{
|
||||
script_p++;
|
||||
while ( *script_p != '"' )
|
||||
{
|
||||
if (!*script_p)
|
||||
Error ("EOF inside quoted token");
|
||||
*token_p++ = *script_p++;
|
||||
if (token_p == &token[MAXTOKEN])
|
||||
Error ("Token too large on line %i",scriptline);
|
||||
}
|
||||
script_p++;
|
||||
}
|
||||
else while ( *script_p > 32 )
|
||||
{
|
||||
*token_p++ = *script_p++;
|
||||
if (token_p == &token[MAXTOKEN])
|
||||
Error ("Token too large on line %i",scriptline);
|
||||
}
|
||||
|
||||
*token_p = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UngetToken ()
|
||||
{
|
||||
unget = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void qprintf (char *fmt, ...) // prints text to cmd_out_i
|
||||
{
|
||||
va_list argptr;
|
||||
static char string[1024];
|
||||
|
||||
va_start (argptr, fmt);
|
||||
vsprintf (string, fmt,argptr);
|
||||
va_end (argptr);
|
||||
|
||||
[g_cmd_out_i setStringValue: [NSString stringWithCString: string]];
|
||||
//NSPing ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
Error
|
||||
|
||||
For abnormal program terminations
|
||||
=================
|
||||
*/
|
||||
BOOL in_error;
|
||||
void Error (char *error, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
static char string[1024];
|
||||
|
||||
if (in_error)
|
||||
[NSApp terminate: NULL];
|
||||
in_error = YES;
|
||||
|
||||
va_start (argptr,error);
|
||||
vsprintf (string,error,argptr);
|
||||
va_end (argptr);
|
||||
|
||||
strcat (string, "\nmap saved to "FN_CRASHSAVE);
|
||||
|
||||
[map_i writeMapFile: FN_CRASHSAVE useRegion: NO];
|
||||
NSRunAlertPanel (@"Error", [NSString stringWithCString: string],NULL,NULL,NULL);
|
||||
|
||||
[NSApp terminate: NULL];
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CleanupName (char *in, char *out)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i< 16 ; i++ )
|
||||
{
|
||||
if (!in[i])
|
||||
break;
|
||||
|
||||
out[i] = toupper(in[i]);
|
||||
}
|
||||
|
||||
for ( ; i< 16 ; i++ )
|
||||
out[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
void PrintRect (NSRect *r)
|
||||
{
|
||||
printf ("(%4.0f, %4.0f) + (%4.0f, %4.0f) = (%4.0f,%4.0f)\n"
|
||||
,r->origin.x,r->origin.y,
|
||||
r->size.width, r->size.height, r->origin.x+r->size.width,
|
||||
r->origin.y+r->size.height);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
FileTime
|
||||
|
||||
returns -1 if not present
|
||||
============
|
||||
*/
|
||||
int FileTime (char *path)
|
||||
{
|
||||
struct stat buf;
|
||||
|
||||
if (stat (path,&buf) == -1)
|
||||
return -1;
|
||||
|
||||
return buf.st_mtime;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
CreatePath
|
||||
============
|
||||
*/
|
||||
void CreatePath (char *path)
|
||||
{
|
||||
char *ofs;
|
||||
|
||||
for (ofs = path+1 ; *ofs ; ofs++)
|
||||
{
|
||||
if (*ofs == '/')
|
||||
{ // create the directory
|
||||
*ofs = 0;
|
||||
mkdir (path,0777);
|
||||
*ofs = '/';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int I_FileOpenRead (char *path, int *handle)
|
||||
{
|
||||
int h;
|
||||
struct stat fileinfo;
|
||||
|
||||
|
||||
h = open (path, O_RDONLY, 0666);
|
||||
*handle = h;
|
||||
if (h == -1)
|
||||
return -1;
|
||||
|
||||
if (fstat (h,&fileinfo) == -1)
|
||||
Error ("Error fstating %s", path);
|
||||
|
||||
return fileinfo.st_size;
|
||||
}
|
||||
|
||||
int I_FileOpenWrite (char *path)
|
||||
{
|
||||
int handle;
|
||||
|
||||
umask (0);
|
||||
|
||||
handle = open(path,O_RDWR | O_CREAT | O_TRUNC
|
||||
, 0666);
|
||||
|
||||
if (handle == -1)
|
||||
Error ("Error opening %s: %s", path,strerror(errno));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Sys_UpdateFile
|
||||
|
||||
Copies a more recent net file to the local drive
|
||||
============
|
||||
*/
|
||||
void Sys_UpdateFile (char *path, char *netpath)
|
||||
{
|
||||
int ltime, ntime;
|
||||
int in, out, size;
|
||||
char *buf;
|
||||
|
||||
ltime = FileTime (path);
|
||||
ntime = FileTime (netpath);
|
||||
|
||||
if (ntime <= ltime)
|
||||
return; // up to date
|
||||
|
||||
// copy the file
|
||||
printf ("UpdateFile: copying %s to %s...\n", netpath, path);
|
||||
|
||||
size = I_FileOpenRead (netpath, &in);
|
||||
buf = malloc (size);
|
||||
if (read (in, buf, size) != size)
|
||||
Error ("UpdateFile: couldn't read all of %s", netpath);
|
||||
close (in);
|
||||
|
||||
CreatePath (path);
|
||||
out = I_FileOpenWrite (path);
|
||||
write (out, buf, size);
|
||||
close (out);
|
||||
|
||||
}
|
||||
|
||||
|
57
tools/Forge/Bundles/MapEdit/qedefs.h
Normal file
57
tools/Forge/Bundles/MapEdit/qedefs.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <ctype.h>
|
||||
#import <sys/types.h>
|
||||
#import <sys/dir.h>
|
||||
#import <math.h>
|
||||
#import <unistd.h>
|
||||
#import <sys/fcntl.h>
|
||||
|
||||
#import "UserPath.h"
|
||||
#import "cmdlib.h"
|
||||
#import "mathlib.h"
|
||||
|
||||
#import "EntityClass.h"
|
||||
#import "Project.h"
|
||||
#import "QuakeEd.h"
|
||||
#import "Map.h"
|
||||
#import "TexturePalette.h"
|
||||
#import "SetBrush.h"
|
||||
#import "render.h"
|
||||
#import "Entity.h"
|
||||
|
||||
#import "XYView.h"
|
||||
#import "CameraView.h"
|
||||
#import "ZView.h"
|
||||
#import "ZScrollView.h"
|
||||
#import "Preferences.h"
|
||||
#import "InspectorControl.h"
|
||||
#import "PopScrollView.h"
|
||||
#import "KeypairView.h"
|
||||
#import "Things.h"
|
||||
#import "TextureView.h"
|
||||
#import "Clipper.h"
|
||||
|
||||
|
||||
void PrintRect (NSRect *r);
|
||||
int FileTime (char *path);
|
||||
void Sys_UpdateFile (char *path, char *netpath);
|
||||
void CleanupName (char *in, char *out);
|
||||
|
||||
extern BOOL in_error;
|
||||
void Error (char *error, ...);
|
||||
|
||||
#define MAXTOKEN 128
|
||||
extern char token[MAXTOKEN];
|
||||
extern int scriptline;
|
||||
void StartTokenParsing (char *data);
|
||||
boolean GetToken (boolean crossline); // returns false at eof
|
||||
void UngetToken ();
|
||||
|
||||
|
||||
#define FN_CMDOUT "/tmp/QuakeEdCmd.txt"
|
||||
#define FN_TEMPSAVE "/qcache/temp.map"
|
||||
#define FN_AUTOSAVE "/qcache/AutoSaveMap.map"
|
||||
#define FN_CRASHSAVE "/qcache/ErrorSaveMap.map"
|
||||
#define FN_DEVLOG "/qcache/devlog"
|
||||
|
13
tools/Forge/Bundles/MapEdit/quake.qpr
Normal file
13
tools/Forge/Bundles/MapEdit/quake.qpr
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
{"basepath" "/raid/quake/id1"}
|
||||
{"maps" "jrbase1 jrbase2 jrbase4 jrwiz1 jrwiz2 jrdungn jrmed1 jrmed2 jrstart tim4 tim5 tim6 tim7 tim9 tboss amtest98 ammap2 amtest1 amdm3 amdem1 ammet2 amlev13 ammech3 schurch smotte sramp2 scath sally spit stemple"}
|
||||
{"desc" "jrbase1 jrbase2 jrbase4 jrwiz1 jrwiz2 jrdungn jrmed1 jrmed2 jrstart tim4 tim5 tim6 tim7 tim9 tboss amtest98 ammap2 amtest1 amdm3 amdem1 ammet2 amlev13 ammech3 schurch smotte sramp2 scath sally spit stemple"}
|
||||
{"wads" "gfx/medieval.wad gfx/base.wad gfx/wizard.wad gfx/metal.wad gfx/tim.wad gfx/items.wad gfx/start.wad"}
|
||||
|
||||
{"bspfullvis" "rsh satan @/LocalApps/qbsp $1 $2 ; /LocalApps/light -extra $2 ; /LocalApps/vis $2@"}
|
||||
{"bspfastvis" "rsh satan @/LocalApps/qbsp $1 $2 ; /LocalApps/light $2 ; /LocalApps/vis -fast $2@"}
|
||||
{"bspnovis" "rsh satan @/LocalApps/qbsp $1 $2 ; /LocalApps/light $2@"}
|
||||
{"bsprelight" "rsh satan @/LocalApps/qbsp -onlyents $1 $2 ; /LocalApps/light -extra $2@"}
|
||||
{"bspleaktest" "rsh satan @/LocalApps/qbsp -mark -notjunc $1 $2 ; /LocalApps/light $2@"}
|
||||
{"bspentities" "rsh satan @/LocalApps/qbsp -onlyents $1 $2@"}
|
||||
}
|
33
tools/Forge/Bundles/MapEdit/readme.txt
Normal file
33
tools/Forge/Bundles/MapEdit/readme.txt
Normal file
|
@ -0,0 +1,33 @@
|
|||
|
||||
5/18/96
|
||||
|
||||
This is a dump of the current source code for QuakeEd, our map editing application.
|
||||
|
||||
This does not include everything necessary to build maps. There are graphics files, prog files, and other utilities needed. I plan on releasing a full development set of tools after the game ships. This is just intended to help out anyone working on their own map editor.
|
||||
|
||||
This is a NEXTSTEP application, so hardly anyone is going to be able to use the code as is. This is not an OPENSTEP application. It doesn't even use the foundation kit, so porting to gnustep or openstep-solaris/mach/nt would not be trivial.
|
||||
|
||||
There are lots of mixed case and >8 character filenames, so I'm using unix gnutar (compressed) format.
|
||||
|
||||
Because most people won't have access to a NEXTSTEP machine, I took pictures of some of the more important stuff from interface builder:
|
||||
|
||||
mainwindow.tiff : a screenshot of the primary window
|
||||
inspectors.tiff : a screenshot of the important inspector views
|
||||
help.txt : a dump of the (minimal) help inspector's contents.
|
||||
|
||||
I included some sample data to help you follow the code:
|
||||
|
||||
quake.qpr : our current project file
|
||||
jrbase1.map : a sample map
|
||||
triggers.qc : a sample qc source file that includes some /*QUAKED comments
|
||||
|
||||
There will not be any major changes to this code base. I am eagerly looking forward to writing a brand new editor for windows NT + open GL as soon as Quake ships.
|
||||
|
||||
This application was really not a very good fit for NEXTSTEP. The display postscript model fundamentally doesn't fit very well with what we need here -- if you run in an 8 bit color mode, the line drawing runs at an ok speed, but the texture view goes half the speed it should as it dithers from 24 bit color down to 8 bit. If you run in 24 bit color mode, you get less screen real estate and significantly slower line drawing as a 3 megabyte XY view is flushed. Sigh. If anyone does actually run this on NEXTSTEP be advised that you want a fast machine. I never had the time to properly optimize QuakeEd.
|
||||
|
||||
The texture view rendering code in here is crap. Anyone coding a new editor is strongly advised to just use an available optimized library, like open GL or direct 3D.
|
||||
|
||||
|
||||
John Carmack
|
||||
Id Software
|
||||
johnc@idsoftware.com
|
13
tools/Forge/Bundles/MapEdit/render.h
Normal file
13
tools/Forge/Bundles/MapEdit/render.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
extern int r_width, r_height;
|
||||
extern unsigned *r_picbuffer;
|
||||
extern float *r_zbuffer;
|
||||
|
||||
extern vec3_t r_origin, r_matrix[3];
|
||||
extern BOOL r_drawflat;
|
||||
|
||||
void REN_ClearBuffers (void);
|
||||
void REN_DrawCameraFace (face_t *idpol);
|
||||
void REN_DrawXYFace (face_t *idpol);
|
||||
void REN_BeginCamera (void);
|
||||
void REN_BeginXY (void);
|
748
tools/Forge/Bundles/MapEdit/render.m
Normal file
748
tools/Forge/Bundles/MapEdit/render.m
Normal file
|
@ -0,0 +1,748 @@
|
|||
|
||||
#import "qedefs.h"
|
||||
|
||||
|
||||
//define NOLIGHT
|
||||
|
||||
vec3_t r_origin, r_matrix[3];
|
||||
|
||||
int t_width, t_height;
|
||||
unsigned *t_data;
|
||||
int t_widthmask, t_heightmask, t_widthshift;
|
||||
float t_widthadd, t_heightadd;
|
||||
|
||||
int r_width, r_height;
|
||||
float *r_zbuffer;
|
||||
unsigned *r_picbuffer;
|
||||
|
||||
vec5_t rightside, leftside, rightstep,leftstep;
|
||||
|
||||
face_t *r_face;
|
||||
|
||||
BOOL r_drawflat;
|
||||
|
||||
pixel32_t r_flatcolor;
|
||||
|
||||
int sy[20];
|
||||
|
||||
/*
|
||||
====================
|
||||
REN_ClearBuffers
|
||||
====================
|
||||
*/
|
||||
void REN_ClearBuffers (void)
|
||||
{
|
||||
int size;
|
||||
|
||||
size = r_width * r_height*4;
|
||||
|
||||
memset (r_zbuffer, 0, size);
|
||||
memset (r_picbuffer, 0, size);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
REN_SetTexture
|
||||
====================
|
||||
*/
|
||||
|
||||
void REN_SetTexture (face_t *face)
|
||||
{
|
||||
int i;
|
||||
int t_heightshift;
|
||||
qtexture_t *q;
|
||||
|
||||
if (!face->qtexture)
|
||||
face->qtexture = TEX_ForName (face->texture.texture); // try to load
|
||||
q = face->qtexture;
|
||||
|
||||
t_width = q->width;
|
||||
t_height = q->height;
|
||||
t_data = q->data;
|
||||
r_flatcolor = q->flatcolor;
|
||||
|
||||
r_flatcolor.chan[0] *= r_face->light;
|
||||
r_flatcolor.chan[1] *= r_face->light;
|
||||
r_flatcolor.chan[2] *= r_face->light;
|
||||
|
||||
t_widthadd = t_width*1024;
|
||||
t_heightadd = t_height*1024;
|
||||
|
||||
t_widthmask = t_width-1;
|
||||
t_heightmask = t_height-1;
|
||||
|
||||
t_widthshift = 0;
|
||||
i = t_width;
|
||||
while (i >= 2)
|
||||
{
|
||||
t_widthshift++;
|
||||
i>>=1;
|
||||
}
|
||||
|
||||
t_heightshift = 0;
|
||||
i = t_width;
|
||||
while (i >= 2)
|
||||
{
|
||||
t_heightshift++;
|
||||
i>>=1;
|
||||
}
|
||||
|
||||
if ( (1<<t_widthshift) != t_width || (1<<t_heightshift) != t_height)
|
||||
t_widthshift = t_heightshift = 0; // non power of two
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
REN_DrawSpan
|
||||
==================
|
||||
*/
|
||||
void REN_DrawSpan (int y)
|
||||
{
|
||||
int x, count;
|
||||
int ofs;
|
||||
int tx, ty;
|
||||
int x1, x2;
|
||||
float ufrac, vfrac, zfrac, lightfrac, ustep, vstep, zstep;
|
||||
pixel32_t *in, *out;
|
||||
float scale;
|
||||
|
||||
if (y<0 || y >= r_height)
|
||||
return;
|
||||
|
||||
x1 = (leftside[0]);
|
||||
x2 = (rightside[0]);
|
||||
|
||||
count = x2 - x1;
|
||||
if (count < 0)
|
||||
return;
|
||||
|
||||
zfrac = leftside[2];
|
||||
ufrac = leftside[3];
|
||||
vfrac = leftside[4];
|
||||
lightfrac = r_face->light;
|
||||
|
||||
if (!count)
|
||||
scale = 1;
|
||||
else
|
||||
scale = 1.0/count;
|
||||
|
||||
zstep = (rightside[2] - zfrac)*scale;
|
||||
ustep = (rightside[3] - ufrac)*scale;
|
||||
vstep = (rightside[4] - vfrac)*scale;
|
||||
|
||||
if (x1 < 0)
|
||||
{
|
||||
ufrac -= x1*ustep;
|
||||
vfrac -= x1*vstep;
|
||||
zfrac -= x1*zstep;
|
||||
x1 = 0;
|
||||
}
|
||||
|
||||
if (x2 > r_width)
|
||||
x2 = r_width;
|
||||
|
||||
ofs = y*r_width+x1;
|
||||
|
||||
// this should be specialized for 1.0 / 0.5 / 0.75 light levels
|
||||
for (x=x1 ; x < x2 ; x++)
|
||||
{
|
||||
if (r_zbuffer[ofs] <= zfrac)
|
||||
{
|
||||
scale = 1/zfrac;
|
||||
|
||||
r_zbuffer[ofs] = zfrac;
|
||||
|
||||
if (t_widthshift)
|
||||
{
|
||||
tx = (int)((ufrac*scale)) & t_widthmask;
|
||||
ty = (int)((vfrac*scale)) & t_heightmask;
|
||||
in = (pixel32_t *)&t_data [(ty<<t_widthshift)+tx];
|
||||
}
|
||||
else
|
||||
{
|
||||
tx = (int)((ufrac*scale)+t_widthadd) % t_width;
|
||||
ty = (int)((vfrac*scale)+t_heightadd) % t_height;
|
||||
in = (pixel32_t *)&t_data [ty*t_width+tx];
|
||||
}
|
||||
|
||||
out = (pixel32_t *)&r_picbuffer[ofs];
|
||||
#ifdef NOLIGHT
|
||||
*out = *in;
|
||||
#else
|
||||
out->chan[0] = in->chan[0]*lightfrac;
|
||||
out->chan[1] = in->chan[1]*lightfrac;
|
||||
out->chan[2] = in->chan[2]*lightfrac;
|
||||
out->chan[3] = 0xff;
|
||||
#endif
|
||||
}
|
||||
ufrac += ustep;
|
||||
vfrac += vstep;
|
||||
zfrac += zstep;
|
||||
ofs++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
REN_DrawFlatSpan
|
||||
==================
|
||||
*/
|
||||
void REN_DrawFlatSpan (int y)
|
||||
{
|
||||
int x, count;
|
||||
int ofs;
|
||||
int x1, x2;
|
||||
float zfrac, zstep;
|
||||
int *out;
|
||||
|
||||
if (y<0 || y >= r_height)
|
||||
return;
|
||||
|
||||
x1 = (leftside[0]);
|
||||
x2 = (rightside[0]);
|
||||
|
||||
count = x2 - x1;
|
||||
if (count < 0)
|
||||
return;
|
||||
|
||||
zfrac = leftside[2];
|
||||
|
||||
zstep = (rightside[2] - zfrac)/count;
|
||||
|
||||
if (x1 < 0)
|
||||
{
|
||||
zfrac -= x1*zstep;
|
||||
x1 = 0;
|
||||
}
|
||||
|
||||
if (x2 > r_width)
|
||||
x2 = r_width;
|
||||
|
||||
ofs = y*r_width+x1;
|
||||
|
||||
// this should be specialized for 1.0 / 0.5 / 0.75 light levels
|
||||
for (x=x1 ; x < x2 ; x++)
|
||||
{
|
||||
if (r_zbuffer[ofs] <= zfrac)
|
||||
{
|
||||
r_zbuffer[ofs] = zfrac;
|
||||
out = (int *)&r_picbuffer[ofs];
|
||||
*out = r_flatcolor.p;
|
||||
}
|
||||
zfrac += zstep;
|
||||
ofs++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
REN_RasterizeFace
|
||||
|
||||
=====================
|
||||
*/
|
||||
void REN_RasterizeFace (winding_t *w)
|
||||
{
|
||||
int y;
|
||||
int i;
|
||||
int top, bot;
|
||||
int leftv, rightv;
|
||||
int count;
|
||||
int numvertex;
|
||||
|
||||
//
|
||||
// find top vertex
|
||||
//
|
||||
numvertex = w->numpoints;
|
||||
top = 0x7fffffff;
|
||||
bot = 0x80000000;
|
||||
leftv = 0;
|
||||
|
||||
for (i=0 ; i<numvertex ; i++)
|
||||
{
|
||||
w->points[i][3] *= w->points[i][2];
|
||||
w->points[i][4] *= w->points[i][2];
|
||||
|
||||
sy[i] = (int)w->points[i][1];
|
||||
|
||||
if (sy[i] < top)
|
||||
{
|
||||
top = sy[i];
|
||||
leftv = i;
|
||||
}
|
||||
if (sy[i] > bot)
|
||||
bot = sy[i];
|
||||
}
|
||||
rightv = leftv;
|
||||
|
||||
if (top < 0 || bot > r_height || top > bot)
|
||||
return; // shouldn't have to have this...
|
||||
|
||||
//
|
||||
// render a trapezoid
|
||||
//
|
||||
y = top;
|
||||
|
||||
while (y < bot)
|
||||
{
|
||||
if (y >= sy[leftv])
|
||||
{
|
||||
do
|
||||
{
|
||||
for (i=0 ; i<5 ; i++)
|
||||
leftside[i] = w->points[leftv][i];
|
||||
leftv--;
|
||||
if (leftv == -1)
|
||||
leftv = numvertex-1;
|
||||
} while (sy[leftv] <= y);
|
||||
count = sy[leftv]-y;
|
||||
for (i=0 ; i<5 ; i++)
|
||||
leftstep[i] = (w->points[leftv][i] - leftside[i])/count;
|
||||
}
|
||||
if (y >= sy[rightv])
|
||||
{
|
||||
do
|
||||
{
|
||||
for (i=0 ; i<5 ; i++)
|
||||
rightside[i] = w->points[rightv][i];
|
||||
rightv++;
|
||||
if (rightv == numvertex)
|
||||
rightv = 0;
|
||||
} while (sy[rightv] <= y);
|
||||
count = sy[rightv]-y;
|
||||
for (i=0 ; i<5 ; i++)
|
||||
rightstep[i] = (w->points[rightv][i] - rightside[i])/count;
|
||||
}
|
||||
|
||||
if (r_drawflat)
|
||||
REN_DrawFlatSpan (y);
|
||||
else
|
||||
REN_DrawSpan (y);
|
||||
|
||||
for (i=0 ; i<5 ; i++)
|
||||
{
|
||||
leftside[i] += leftstep[i];
|
||||
rightside[i] += rightstep[i];
|
||||
}
|
||||
|
||||
y++;
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
REN_DrawSpanLinear
|
||||
==================
|
||||
*/
|
||||
void REN_DrawSpanLinear (int y)
|
||||
{
|
||||
int x, count;
|
||||
int ofs;
|
||||
int tx, ty;
|
||||
int x1, x2;
|
||||
float ufrac, vfrac, zfrac, ustep, vstep, zstep;
|
||||
pixel32_t *in, *out;
|
||||
float scale;
|
||||
|
||||
if (y<0 || y >= r_height)
|
||||
return;
|
||||
|
||||
x1 = (leftside[0]);
|
||||
x2 = (rightside[0]);
|
||||
|
||||
count = x2 - x1;
|
||||
if (count < 0)
|
||||
return;
|
||||
|
||||
zfrac = leftside[2];
|
||||
ufrac = leftside[3];
|
||||
vfrac = leftside[4];
|
||||
|
||||
if (!count)
|
||||
scale = 1;
|
||||
else
|
||||
scale = 1.0/count;
|
||||
|
||||
zstep = (rightside[2] - zfrac)*scale;
|
||||
ustep = (rightside[3] - ufrac)*scale;
|
||||
vstep = (rightside[4] - vfrac)*scale;
|
||||
|
||||
|
||||
if (x1 < 0)
|
||||
{
|
||||
ufrac -= x1*ustep;
|
||||
vfrac -= x1*vstep;
|
||||
zfrac -= x1*zstep;
|
||||
x1 = 0;
|
||||
}
|
||||
|
||||
if (x2 > r_width)
|
||||
x2 = r_width;
|
||||
|
||||
ofs = y*r_width+x1;
|
||||
|
||||
for (x=x1 ; x < x2 ; x++)
|
||||
{
|
||||
if (r_zbuffer[ofs] <= zfrac)
|
||||
{
|
||||
r_zbuffer[ofs] = zfrac;
|
||||
|
||||
if (t_widthshift)
|
||||
{
|
||||
tx = (int)ufrac & t_widthmask;
|
||||
ty = (int)vfrac & t_heightmask;
|
||||
in = (pixel32_t *)&t_data [(ty<<t_widthshift)+tx];
|
||||
}
|
||||
else
|
||||
{
|
||||
tx = (int)(ufrac+t_widthadd) % t_width;
|
||||
ty = (int)(vfrac+t_heightadd) % t_height;
|
||||
in = (pixel32_t *)&t_data [ty*t_width+tx];
|
||||
}
|
||||
|
||||
out = (pixel32_t *)&r_picbuffer[ofs];
|
||||
*out = *in;
|
||||
}
|
||||
ufrac += ustep;
|
||||
vfrac += vstep;
|
||||
zfrac += zstep;
|
||||
ofs++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
REN_RasterizeFaceLinear
|
||||
|
||||
=====================
|
||||
*/
|
||||
void REN_RasterizeFaceLinear (winding_t *w)
|
||||
{
|
||||
int y;
|
||||
int i;
|
||||
int top, bot;
|
||||
int leftv, rightv;
|
||||
int count;
|
||||
int numvertex;
|
||||
|
||||
//
|
||||
// find top vertex
|
||||
//
|
||||
numvertex = w->numpoints;
|
||||
top = 0x7fffffff;
|
||||
bot = 0x80000000;
|
||||
|
||||
leftv = 0;
|
||||
for (i=0 ; i<numvertex ; i++)
|
||||
{
|
||||
sy[i] = (int)w->points[i][1];
|
||||
|
||||
if (sy[i] < top)
|
||||
{
|
||||
top = sy[i];
|
||||
leftv = i;
|
||||
}
|
||||
if (sy[i] > bot)
|
||||
bot = sy[i];
|
||||
}
|
||||
rightv = leftv;
|
||||
|
||||
if (top < 0 || bot > r_height || top > bot)
|
||||
return; // shouldn't have to have this...
|
||||
|
||||
//
|
||||
// render a trapezoid
|
||||
//
|
||||
y = top;
|
||||
|
||||
while (y < bot)
|
||||
{
|
||||
if (y >= sy[leftv])
|
||||
{
|
||||
do
|
||||
{
|
||||
for (i=0 ; i<5 ; i++)
|
||||
leftside[i] = w->points[leftv][i];
|
||||
leftv--;
|
||||
if (leftv == -1)
|
||||
leftv = numvertex-1;
|
||||
} while (sy[leftv] <= y);
|
||||
count = sy[leftv]-y;
|
||||
for (i=0 ; i<5 ; i++)
|
||||
leftstep[i] = (w->points[leftv][i] - leftside[i])/count;
|
||||
}
|
||||
if (y >= sy[rightv])
|
||||
{
|
||||
do
|
||||
{
|
||||
for (i=0 ; i<5 ; i++)
|
||||
rightside[i] = w->points[rightv][i];
|
||||
rightv++;
|
||||
if (rightv == numvertex)
|
||||
rightv = 0;
|
||||
} while (sy[rightv] <= y);
|
||||
count = sy[rightv]-y;
|
||||
for (i=0 ; i<5 ; i++)
|
||||
rightstep[i] = (w->points[rightv][i] - rightside[i])/count;
|
||||
}
|
||||
|
||||
REN_DrawSpanLinear (y);
|
||||
|
||||
for (i=0 ; i<5 ; i++)
|
||||
{
|
||||
leftside[i] += leftstep[i];
|
||||
rightside[i] += rightstep[i];
|
||||
}
|
||||
|
||||
y++;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
REN_BeginCamera
|
||||
===================
|
||||
*/
|
||||
float r_width_2, r_height_3;
|
||||
plane_t frustum[5];
|
||||
|
||||
void REN_BeginCamera (void)
|
||||
{
|
||||
r_width_2 = (float)r_width / 2;
|
||||
r_height_3 = (float)r_height / 3;
|
||||
|
||||
|
||||
// clip to right side
|
||||
frustum[0].normal[0] = -1;
|
||||
frustum[0].normal[1] = 0;
|
||||
frustum[0].normal[2] = 1;
|
||||
frustum[0].dist = 0;
|
||||
|
||||
// clip to left side
|
||||
frustum[1].normal[0] = 1;
|
||||
frustum[1].normal[1] = 0;
|
||||
frustum[1].normal[2] = 1;
|
||||
frustum[1].dist = 0;
|
||||
|
||||
// clip to top side
|
||||
frustum[2].normal[0] = 0;
|
||||
frustum[2].normal[1] = -1;
|
||||
frustum[2].normal[2] = r_height_3 / r_width_2;
|
||||
frustum[2].dist = 0;
|
||||
|
||||
// clip to bottom side
|
||||
frustum[3].normal[0] = 0;
|
||||
frustum[3].normal[1] = 1;
|
||||
frustum[3].normal[2] = 2*r_height_3 / r_width_2;
|
||||
frustum[3].dist = 0;
|
||||
|
||||
// near Z
|
||||
frustum[4].normal[0] = 0;
|
||||
frustum[4].normal[1] = 0;
|
||||
frustum[4].normal[2] = 1;
|
||||
frustum[4].dist = 1;
|
||||
}
|
||||
|
||||
|
||||
void REN_BeginXY (void)
|
||||
{
|
||||
frustum[0].normal[0] = 1;
|
||||
frustum[0].normal[1] = 0;
|
||||
frustum[0].normal[2] = 0;
|
||||
frustum[0].dist = 0;
|
||||
|
||||
frustum[1].normal[0] = -1;
|
||||
frustum[1].normal[1] = 0;
|
||||
frustum[1].normal[2] = 0;
|
||||
frustum[1].dist = -r_width;
|
||||
|
||||
frustum[2].normal[0] = 0;
|
||||
frustum[2].normal[1] = 1;
|
||||
frustum[2].normal[2] = 0;
|
||||
frustum[2].dist = 0;
|
||||
|
||||
frustum[3].normal[0] = 0;
|
||||
frustum[3].normal[1] = -1;
|
||||
frustum[3].normal[2] = 0;
|
||||
frustum[3].dist = -r_height;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
REN_DrawCameraFace
|
||||
=====================
|
||||
*/
|
||||
void REN_DrawCameraFace (face_t *idpol)
|
||||
{
|
||||
int i;
|
||||
float scale;
|
||||
int numvertex;
|
||||
winding_t *w, *in;
|
||||
vec3_t temp;
|
||||
|
||||
if (!idpol->w)
|
||||
return; // overconstrained plane
|
||||
|
||||
r_face = idpol;
|
||||
|
||||
//
|
||||
// back face cull
|
||||
//
|
||||
if (DotProduct (r_origin, idpol->plane.normal) <= idpol->plane.dist)
|
||||
return;
|
||||
|
||||
//
|
||||
// transform in 3D (FIXME: clip first, then transform)
|
||||
//
|
||||
in = idpol->w;
|
||||
numvertex = in->numpoints;
|
||||
|
||||
w = NewWinding (numvertex);
|
||||
w->numpoints = numvertex;
|
||||
for (i=0 ; i<numvertex ; i++)
|
||||
{
|
||||
VectorSubtract (in->points[i], r_origin, temp);
|
||||
|
||||
w->points[i][0] = DotProduct(temp,r_matrix[0]);
|
||||
w->points[i][1] = DotProduct(temp,r_matrix[1]);
|
||||
w->points[i][2] = DotProduct(temp,r_matrix[2]);
|
||||
|
||||
w->points[i][3] = in->points[i][3];
|
||||
w->points[i][4] = in->points[i][4];
|
||||
}
|
||||
|
||||
//
|
||||
// 3D clip
|
||||
//
|
||||
for (i=0 ; i<4 ; i++)
|
||||
{
|
||||
w = ClipWinding (w, &frustum[i]);
|
||||
if (!w)
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// project to 2D
|
||||
//
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
scale = r_width_2 / w->points[i][2];
|
||||
w->points[i][0] = r_width_2 + scale*w->points[i][0];
|
||||
w->points[i][1] = r_height_3 - scale*w->points[i][1];
|
||||
w->points[i][2] = scale;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// draw it
|
||||
//
|
||||
REN_SetTexture (idpol);
|
||||
|
||||
REN_RasterizeFace (w);
|
||||
free (w);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=====================
|
||||
REN_DrawXYFace
|
||||
=====================
|
||||
*/
|
||||
void REN_DrawXYFace (face_t *idpol)
|
||||
{
|
||||
int i, j, numvertex;
|
||||
winding_t *w, *in;
|
||||
float *dest, *source;
|
||||
float temp;
|
||||
|
||||
if (!idpol->w)
|
||||
return; // overconstrained plane
|
||||
w = idpol->w;
|
||||
|
||||
r_face = idpol;
|
||||
|
||||
//
|
||||
// back (and side) face cull
|
||||
//
|
||||
if (DotProduct (idpol->plane.normal, xy_viewnormal) > -VECTOR_EPSILON)
|
||||
return;
|
||||
|
||||
//
|
||||
// transform
|
||||
//
|
||||
in = idpol->w;
|
||||
numvertex = in->numpoints;
|
||||
|
||||
w = NewWinding (numvertex);
|
||||
w->numpoints = numvertex;
|
||||
|
||||
for (i=0 ; i<numvertex ; i++)
|
||||
{
|
||||
// using Z as a scale for the 2D projection
|
||||
w->points[i][0] = (in->points[i][0] - r_origin[0])*r_origin[2];
|
||||
w->points[i][1] = r_height - (in->points[i][1] - r_origin[1])*r_origin[2];
|
||||
w->points[i][2] = in->points[i][2] + 3000;
|
||||
w->points[i][3] = in->points[i][3];
|
||||
w->points[i][4] = in->points[i][4];
|
||||
}
|
||||
|
||||
//
|
||||
// clip
|
||||
//
|
||||
for (i=0 ; i<4 ; i++)
|
||||
{
|
||||
w = ClipWinding (w, &frustum[i]);
|
||||
if (!w)
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// project to 2D
|
||||
//
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
dest = w->points[i];
|
||||
if (dest[0] < 0)
|
||||
dest[0] = 0;
|
||||
if (dest[0] > r_width)
|
||||
dest[0] = r_width;
|
||||
if (dest[1] < 0)
|
||||
dest[1] = 0;
|
||||
if (dest[1] > r_height)
|
||||
dest[1] = r_height;
|
||||
if (xy_viewnormal[2] > 0)
|
||||
dest[2] = 4096-dest[2];
|
||||
}
|
||||
|
||||
if (xy_viewnormal[2] > 0)
|
||||
{ // flip order when upside down
|
||||
for (i=0 ; i<w->numpoints/2 ; i++)
|
||||
{
|
||||
dest = w->points[i];
|
||||
source = w->points[w->numpoints-1-i];
|
||||
for (j=0 ; j<5 ; j++)
|
||||
{
|
||||
temp = dest[j];
|
||||
dest[j] = source[j];
|
||||
source[j] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
REN_SetTexture (idpol);
|
||||
|
||||
|
||||
//
|
||||
// draw it
|
||||
//
|
||||
REN_RasterizeFaceLinear (w);
|
||||
free (w);
|
||||
}
|
||||
|
Loading…
Reference in a new issue