This is SetBrush.m in view mode; [Download] [Up]
#import "qedefs.h" @implementation SetBrush /* ================== textureAxisFromPlane ================== */ #if 1 vec3_t baseaxis[18] = { {0,0,1}, {1,0,0}, {0,-1,0}, // floor {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling {1,0,0}, {0,1,0}, {0,0,-1}, // west wall {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall {0,1,0}, {1,0,0}, {0,0,-1}, // south wall {0,-1,0}, {1,0,0}, {0,0,-1} // north wall }; #else vec3_t baseaxis[18] = { {0,0,1}, {1,0,0}, {0,-1,0}, // floor {0,0,-1}, {1,0,0}, {0,1,0}, // ceiling {1,0,0}, {0,1,0}, {0,0,-1}, // west wall {-1,0,0}, {0,-1,0}, {0,0,-1}, // east wall {0,1,0}, {-1,0,0}, {0,0,-1}, // south wall {0,-1,0}, {1,0,0}, {0,0,-1} // north wall }; #endif float TextureAxisFromPlane(plane_t *pln, float *xv, float *yv) { int bestaxis; float dot,best; int i; best = 0; bestaxis = 0; for (i=0 ; i<6 ; i++) { dot = DotProduct (pln->normal, baseaxis[i*3]); if (dot > best) { best = dot; bestaxis = i; } } VectorCopy (baseaxis[bestaxis*3+1], xv); VectorCopy (baseaxis[bestaxis*3+2], yv); return lightaxis[bestaxis>>1]; } #define BOGUS_RANGE 18000 /* ================= CheckFace Note: this will not catch 0 area polygons ================= */ void CheckFace (face_t *f) { int i, j; float *p1, *p2; float d, edgedist; vec3_t dir, edgenormal; winding_t *w; w = f->w; if (!w) Error ("CheckFace: no winding"); if (w->numpoints < 3) Error ("CheckFace: %i points",w->numpoints); for (i=0 ; i<w->numpoints ; i++) { p1 = w->points[i]; for (j=0 ; j<3 ; j++) if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE) Error ("CheckFace: BUGUS_RANGE: %f",p1[j]); j = i+1 == w->numpoints ? 0 : i+1; // check the point is on the face plane d = DotProduct (p1, f->plane.normal) - f->plane.dist; if (d < -ON_EPSILON || d > ON_EPSILON) Error ("CheckFace: point off plane"); // check the edge isn't degenerate p2 = w->points[j]; VectorSubtract (p2, p1, dir); if (VectorLength (dir) < ON_EPSILON) Error ("CheckFace: degenerate edge"); CrossProduct (f->plane.normal, dir, edgenormal); VectorNormalize (edgenormal); edgedist = DotProduct (p1, edgenormal); edgedist += ON_EPSILON; // all other points must be on front side for (j=0 ; j<w->numpoints ; j++) { if (j == i) continue; d = DotProduct (w->points[j], edgenormal); if (d > edgedist) Error ("CheckFace: non-convex"); } } } /* ============================================================================= TURN PLANES INTO GROUPS OF FACES ============================================================================= */ /* ================== NewWinding ================== */ winding_t *NewWinding (int points) { winding_t *w; int size; if (points > MAX_POINTS_ON_WINDING) Error ("NewWinding: %i points", points); size = (int)((winding_t *)0)->points[points]; w = malloc (size); memset (w, 0, size); return w; } /* ================== CopyWinding ================== */ winding_t *CopyWinding (winding_t *w) { int size; winding_t *c; size = (int)((winding_t *)0)->points[w->numpoints]; c = malloc (size); memcpy (c, w, size); return c; } /* ================== ClipWinding Clips the winding to the plane, returning the new winding on the positive side Frees the input winding. ================== */ winding_t *ClipWinding (winding_t *in, plane_t *split) { float dists[MAX_POINTS_ON_WINDING]; int sides[MAX_POINTS_ON_WINDING]; int counts[3]; float dot; int i, j; float *p1, *p2, *mid; winding_t *neww; int maxpts; counts[0] = counts[1] = counts[2] = 0; // determine sides for each point for (i=0 ; i<in->numpoints ; i++) { dot = DotProduct (in->points[i], split->normal); dot -= split->dist; dists[i] = dot; if (dot > ON_EPSILON) sides[i] = SIDE_FRONT; else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK; else { sides[i] = SIDE_ON; } counts[sides[i]]++; } sides[i] = sides[0]; dists[i] = dists[0]; if (!counts[0] && !counts[1]) return in; if (!counts[0]) { free (in); return NULL; } if (!counts[1]) return in; maxpts = in->numpoints+4; // can't use counts[0]+2 because // of fp grouping errors neww = NewWinding (maxpts); for (i=0 ; i<in->numpoints ; i++) { p1 = in->points[i]; mid = neww->points[neww->numpoints]; if (sides[i] == SIDE_FRONT || sides[i] == SIDE_ON) { VectorCopy (p1, mid); mid[3] = p1[3]; mid[4] = p1[4]; neww->numpoints++; if (sides[i] == SIDE_ON) continue; mid = neww->points[neww->numpoints]; } if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; // generate a split point if (i == in->numpoints - 1) p2 = in->points[0]; else p2 = p1 + 5; neww->numpoints++; dot = dists[i] / (dists[i]-dists[i+1]); for (j=0 ; j<3 ; j++) { // avoid round off error when possible if (split->normal[j] == 1) mid[j] = split->dist; else if (split->normal[j] == -1) mid[j] = -split->dist; mid[j] = p1[j] + dot*(p2[j]-p1[j]); } mid[3] = p1[3] + dot*(p2[3]-p1[3]); mid[4] = p1[4] + dot*(p2[4]-p1[4]); } if (neww->numpoints > maxpts) Error ("ClipWinding: points exceeded estimate"); // free the original winding free (in); return neww; } /* ================= BasePolyForPlane There has GOT to be a better way of doing this... ================= */ winding_t *BasePolyForPlane (face_t *f) { int i, x; float max, v; vec3_t org, vright, vup; vec3_t xaxis, yaxis; winding_t *w; texturedef_t *td; plane_t *p; float ang, sinv, cosv; float s, t, ns, nt; p = &f->plane; // find the major axis max = -BOGUS_RANGE; x = -1; for (i=0 ; i<3; i++) { v = fabs(p->normal[i]); if (v > max) { x = i; max = v; } } if (x==-1) Error ("BasePolyForPlane: no axis found"); VectorCopy (vec3_origin, vup); switch (x) { case 0: case 1: vup[2] = 1; break; case 2: vup[0] = 1; break; } v = DotProduct (vup, p->normal); VectorMA (vup, -v, p->normal, vup); VectorNormalize (vup); VectorScale (p->normal, p->dist, org); CrossProduct (vup, p->normal, vright); VectorScale (vup, 8192, vup); VectorScale (vright, 8192, vright); // project a really big axis aligned box onto the plane w = NewWinding (4); w->numpoints = 4; VectorSubtract (org, vright, w->points[0]); VectorAdd (w->points[0], vup, w->points[0]); VectorAdd (org, vright, w->points[1]); VectorAdd (w->points[1], vup, w->points[1]); VectorAdd (org, vright, w->points[2]); VectorSubtract (w->points[2], vup, w->points[2]); VectorSubtract (org, vright, w->points[3]); VectorSubtract (w->points[3], vup, w->points[3]); // set texture values f->light = TextureAxisFromPlane(&f->plane, xaxis, yaxis); td = &f->texture; // rotate axis ang = td->rotate / 180 * M_PI; sinv = sin(ang); cosv = cos(ang); if (!td->scale[0]) td->scale[0] = 1; if (!td->scale[1]) td->scale[1] = 1; for (i=0 ; i<4 ; i++) { s = DotProduct (w->points[i], xaxis); t = DotProduct (w->points[i], yaxis); ns = cosv * s - sinv * t; nt = sinv * s + cosv * t; w->points[i][3] = ns/td->scale[0] + td->shift[0]; w->points[i][4] = nt/td->scale[1] + td->shift[1]; } return w; } /* =========== calcWindings recalc the faces and mins / maxs from the planes If a face has a NULL winding, it is an overconstraining plane and can be removed. =========== */ - calcWindings { int i,j, k; float v; face_t *f; winding_t *w; plane_t plane; vec3_t t1, t2, t3; BOOL useplane[MAX_FACES]; bmins[0] = bmins[1] = bmins[2] = 99999; bmaxs[0] = bmaxs[1] = bmaxs[2] = -99999; invalid = NO; [self freeWindings]; for (i=0 ; i<MAX_FACES ; i++) { f = &faces[i]; // calc a plane from the points for (j=0 ; j<3 ; j++) { t1[j] = f->planepts[0][j] - f->planepts[1][j]; t2[j] = f->planepts[2][j] - f->planepts[1][j]; t3[j] = f->planepts[1][j]; } CrossProduct(t1,t2, f->plane.normal); if (VectorCompare (f->plane.normal, vec3_origin)) { useplane[i] = NO; break; } VectorNormalize (f->plane.normal); f->plane.dist = DotProduct (t3, f->plane.normal); // if the plane duplicates another plane, ignore it // (assume it is a brush being edited that will be fixed) useplane[i] = YES; for (j=0 ; j< i ; j++) { if ( f->plane.normal[0] == faces[j].plane.normal[0] && f->plane.normal[1] == faces[j].plane.normal[1] && f->plane.normal[2] == faces[j].plane.normal[2] && f->plane.dist == faces[j].plane.dist ) { useplane[i] = NO; break; } } } for (i=0 ; i<numfaces ; i++) { if (!useplane[i]) continue; // duplicate plane f = &faces[i]; w = BasePolyForPlane (f); for (j=0 ; j<numfaces && w ; j++) { if (j == i) continue; // flip the plane, because we want to keep the back side VectorSubtract (vec3_origin, faces[j].plane.normal, plane.normal); plane.dist = -faces[j].plane.dist; w = ClipWinding (w, &plane); } f->w = w; if (w) { CheckFace (f); for (j=0 ; j<w->numpoints ; j++) { for (k=0 ; k<3 ; k++) { v = w->points[j][k]; if (fabs(v - rint(v)) < FP_EPSILON) v = w->points[j][k] = rint(v); if (v < bmins[k]) bmins[k] = v; if (v > bmaxs[k]) bmaxs[k] = v; } } } } if (bmins[0] == 99999) { invalid = YES; VectorCopy (vec3_origin, bmins); VectorCopy (vec3_origin, bmaxs); return nil; } return self; } //============================================================================ /* =========== initOwner::: =========== */ - initOwner: own mins:(float *)mins maxs:(float *)maxs texture:(texturedef_t *)tex { [super init]; parent = own; [self setTexturedef: tex]; [self setMins: mins maxs: maxs]; return self; } - setMins:(float *)mins maxs:(float *)maxs { int i, j; vec3_t pts[4][2]; for (i=0 ; i<3 ; i++) { if (maxs[i] - mins[i] <= 0) { VectorCopy (mins, bmins); VectorCopy (maxs, bmaxs); invalid = YES; numfaces = 0; return self; } } pts[0][0][0] = mins[0]; pts[0][0][1] = mins[1]; pts[1][0][0] = mins[0]; pts[1][0][1] = maxs[1]; pts[2][0][0] = maxs[0]; pts[2][0][1] = maxs[1]; pts[3][0][0] = maxs[0]; pts[3][0][1] = mins[1]; for (i=0 ; i<4 ; i++) { pts[i][0][2] = mins[2]; pts[i][1][0] = pts[i][0][0]; pts[i][1][1] = pts[i][0][1]; pts[i][1][2] = maxs[2]; } numfaces = 6; for (i=0 ; i<4 ; i++) { j = (i+1)%4; faces[i].planepts[0][0] = pts[j][1][0]; faces[i].planepts[0][1] = pts[j][1][1]; faces[i].planepts[0][2] = pts[j][1][2]; faces[i].planepts[1][0] = pts[i][1][0]; faces[i].planepts[1][1] = pts[i][1][1]; faces[i].planepts[1][2] = pts[i][1][2]; faces[i].planepts[2][0] = pts[i][0][0]; faces[i].planepts[2][1] = pts[i][0][1]; faces[i].planepts[2][2] = pts[i][0][2]; } faces[4].planepts[0][0] = pts[0][1][0]; faces[4].planepts[0][1] = pts[0][1][1]; faces[4].planepts[0][2] = pts[0][1][2]; faces[4].planepts[1][0] = pts[1][1][0]; faces[4].planepts[1][1] = pts[1][1][1]; faces[4].planepts[1][2] = pts[1][1][2]; faces[4].planepts[2][0] = pts[2][1][0]; faces[4].planepts[2][1] = pts[2][1][1]; faces[4].planepts[2][2] = pts[2][1][2]; faces[5].planepts[0][0] = pts[2][0][0]; faces[5].planepts[0][1] = pts[2][0][1]; faces[5].planepts[0][2] = pts[2][0][2]; faces[5].planepts[1][0] = pts[1][0][0]; faces[5].planepts[1][1] = pts[1][0][1]; faces[5].planepts[1][2] = pts[1][0][2]; faces[5].planepts[2][0] = pts[0][0][0]; faces[5].planepts[2][1] = pts[0][0][1]; faces[5].planepts[2][2] = pts[0][0][2]; [self calcWindings]; return self; } - parent { return parent; } - setParent: (id)p { parent = p; return self; } - setEntityColor: (vec3_t)color { VectorCopy (color, entitycolor); return self; } - freeWindings { int i; for (i=0 ; i<MAX_FACES ; i++) if (faces[i].w) { free (faces[i].w); faces[i].w = NULL; } return self; } - copyFromZone:(NXZone *)zone { id new; [self freeWindings]; new = [super copyFromZone: zone]; [self calcWindings]; [new calcWindings]; return new; } - free { [self freeWindings]; return [super free]; } /* =========== initOwner: fromTokens =========== */ int numsb; - initFromTokens: own { face_t *f; int i,j; [self init]; parent = own; f = faces; numfaces = 0; do { if (!GetToken (true)) break; if (!strcmp (token, "}") ) break; for (i=0 ; i<3 ; i++) { if (i != 0) GetToken (true); if (strcmp (token, "(") ) Error ("parsing map file"); for (j=0 ; j<3 ; j++) { GetToken (false); f->planepts[i][j] = atoi(token); } GetToken (false); if (strcmp (token, ")") ) Error ("parsing map file"); } GetToken (false); strcpy (f->texture.texture, token); GetToken (false); f->texture.shift[0] = atof(token); GetToken (false); f->texture.shift[1] = atof(token); GetToken (false); f->texture.rotate = atof(token); GetToken (false); f->texture.scale[0] = atof(token); GetToken (false); f->texture.scale[1] = atof(token); #if 0 flags = atoi(token); flags &= 7; f->texture.rotate = 0; f->texture.scale[0] = 1; f->texture.scale[1] = 1; #define TEX_FLIPAXIS 1 #define TEX_FLIPS 2 #define TEX_FLIPT 4 if (flags & TEX_FLIPAXIS) { f->texture.rotate = 90; if ( !(flags & TEX_FLIPT) ) f->texture.scale[0] = -1; if (flags & TEX_FLIPS) f->texture.scale[1] = -1; } else { if (flags & TEX_FLIPS) f->texture.scale[0] = -1; if (flags & TEX_FLIPT) f->texture.scale[1] = -1; } #endif f++; numfaces++; } while (1); numsb++; [self calcWindings]; return self; } /* =========== writeToFILE =========== */ - writeToFILE: (FILE *)f region: (BOOL)reg { int i,j; face_t *fa; texturedef_t *td; if (reg && regioned) return self; fprintf (f, "{\n"); for (i=0 ; i<numfaces ; i++) { fa = &faces[i]; for (j=0 ; j<3 ; j++) fprintf (f,"( %d %d %d ) ", (int)fa->planepts[j][0], (int)fa->planepts[j][1], (int)fa->planepts[j][2]); td = &fa->texture; fprintf (f,"%s %d %d %d %f %f\n", td->texture, (int)td->shift[0], (int)td->shift[1], (int)td->rotate, td->scale[0], td->scale[1]); } fprintf (f, "}\n"); return self; } /* ============================================================================== INTERACTION ============================================================================== */ - getMins: (vec3_t)mins maxs: (vec3_t)maxs { VectorCopy (bmins, mins); VectorCopy (bmaxs, maxs); return self; } - (BOOL)selected { return selected; } - setSelected: (BOOL)s { selected = s; return self; } - (BOOL)regioned { return regioned; } - setRegioned: (BOOL)s { regioned = s; return self; } /* =========== setTexturedef =========== */ - setTexturedef: (texturedef_t *)tex { int i; for (i=0 ; i<MAX_FACES ; i++) { faces[i].texture = *tex; faces[i].qtexture = NULL; // recache next render } [self calcWindings]; // in case texture coords changed return self; } - setTexturedef: (texturedef_t *)tex forFace:(int)f { if ( (unsigned)f > numfaces) Error ("setTexturedef:forFace: bad face number %i",f); faces[f].texture = *tex; faces[f].qtexture = NULL; // recache next render [self calcWindings]; // in case texture coords changed return self; } /* =========== texturedef =========== */ - (texturedef_t *)texturedef { return &faces[0].texture; } - (texturedef_t *)texturedefForFace: (int)f { return &faces[f].texture; } /* =========== removeIfInvalid So created veneers don't stay around =========== */ - removeIfInvalid { int i, j; for (i=0 ; i<numfaces ; i++) { if (faces[i].w) continue; for (j=i+1 ; j<numfaces ; j++) faces[j-1] = faces[j]; i--; numfaces--; } for ( ; i<MAX_FACES ; i++) faces[i].w = NULL; if (numfaces<4) { invalid = YES; [self remove]; return nil; } return self; } /* =========== containsPoint =========== */ - (BOOL)containsPoint: (vec3_t)pt { int i; for (i=0 ; i<numfaces ; i++) if (DotProduct (faces[i].plane.normal, pt) >= faces[i].plane.dist) return NO; return YES; } /* =========== clipRay =========== */ - clipRay: (vec3_t)p1 : (vec3_t) p2 :(vec3_t)frontpoint : (int *)f_face :(vec3_t)backpoint : (int *)b_face { int frontface, backface; int i, j; face_t *f; float d1, d2, m; float *start; start = p1; frontface = -2; backface = -2; f = faces; for (i=0 ; i<numfaces ; i++, f++) { if (!f->w) continue; // clipped off plane d1 = DotProduct (p1, f->plane.normal) - f->plane.dist; d2 = DotProduct (p2, f->plane.normal) - f->plane.dist; if (d1 >= 0 && d2 >= 0) { // the entire ray is in front of the polytope *f_face = -1; *b_face = -1; return self; } if (d1 > 0 && d2 < 0) { // new front plane frontface = i; m = d1 / (d1-d2); for (j=0 ; j<3 ; j++) frontpoint[j] = p1[j] + m*(p2[j]-p1[j]); p1 = frontpoint; } if (d1 < 0 && d2 > 0) { // new back plane backface = i; m = d1 / (d1-d2); for (j=0 ; j<3 ; j++) backpoint[j] = p1[j] + m*(p2[j]-p1[j]); p2 = backpoint; } } *f_face = frontface; *b_face = backface; return self; } /* =========== hitByRay =========== */ - hitByRay: (vec3_t)p1 : (vec3_t) p2 : (float *)time : (int *)face { vec3_t frontpoint, backpoint, dir; int frontface, backface; if (regioned) { *time = -1; *face = -1; return self; } [self clipRay: p1 : p2 : frontpoint: &frontface : backpoint : &backface]; if (frontface == -2 && backface == -2) { // entire ray is inside the brush, select first face *time = 0; *face = 0; return self; } if (frontface < 0) { // ray started inside the polytope, don't select it *time = -1; *face = -1; return self; } VectorSubtract (p2, p1, dir); VectorNormalize (dir); VectorSubtract (frontpoint, p1, frontpoint); *time = DotProduct (frontpoint, dir); if (*time < 0) Error ("hitByRay: negative t"); *face = frontface; return self; } /* ============================================================================== DRAWING ROUTINES ============================================================================== */ BOOL fakebrush; - drawConnections { id obj; int c, i; vec3_t dest, origin; vec3_t mid; vec3_t forward, right; char *targname; vec3_t min, max, temp; char targ[64]; strcpy (targ, [parent valueForQKey: "target"]); if (!targ || !targ[0]) return self; origin[0] = (bmins[0] + bmaxs[0]) /2; origin[1] = (bmins[1] + bmaxs[1]) /2; c = [map_i count]; for (i=0 ; i<c ; i++) { obj = [map_i objectAt: i]; targname = [obj valueForQKey: "targetname"]; if (strcmp (targ, targname)) continue; [[obj objectAt:0] getMins: min maxs: max]; dest[0] = (min[0] + max[0]) /2; dest[1] = (min[1] + max[1]) /2; XYmoveto (origin); XYlineto (dest); forward[0] = dest[0] - origin[0]; forward[1] = dest[1] - origin[1]; forward[2] = 0; if (!forward[0] && !forward[1]) continue; VectorNormalize (forward); forward[0] = 8*forward[0]; forward[1] = 8*forward[1]; right[0] = forward[1]; right[1] = -forward[0]; mid[0] = (dest[0] + origin[0])/2; mid[1] = (dest[1] + origin[1])/2; temp[0] = mid[0] + right[0] - forward[0]; temp[1] = mid[1] + right[1] - forward[1]; XYmoveto (temp); XYlineto (mid); temp[0] = mid[0] - right[0] - forward[0]; temp[1] = mid[1] - right[1] - forward[1]; XYlineto (temp); } return self; } - (BOOL)fakeBrush: (SEL)call { id copy; face_t face; if (!selected || fakebrush) return NO; if (![clipper_i getFace: &face]) return NO; fakebrush = YES; copy = [self copy]; copy = [copy addFace: &face]; if (copy) { [copy perform:call]; [copy free]; } fakebrush = NO; return YES; } /* =========== XYDrawSelf =========== */ - XYDrawSelf { int i, j; winding_t *w; vec3_t mid, end, s1, s2; char *val; float ang; id worldent, currentent; BOOL keybrush; if ([self fakeBrush: @selector(XYDrawSelf)]) return self; [xyview_i addToScrollRange: bmins[0] : bmins[1]]; [xyview_i addToScrollRange: bmaxs[0] : bmaxs[1]]; worldent = [map_i objectAt: 0]; currentent = [map_i currentEntity]; if (parent != worldent && self == [parent objectAt: 0]) keybrush = YES; else keybrush = NO; if (parent != worldent && worldent == currentent) linecolor (entitycolor[0], entitycolor[1], entitycolor[2]); else if (selected) linecolor (1,0,0); // selected else if (parent == currentent) linecolor (0,0,0); // unselected, but in same entity else linecolor (0,0.5,0); // other entity green if (keybrush) [self drawConnections]; // target line if (!selected && ( bmaxs[0] < xy_draw_rect.origin.x || bmaxs[1] < xy_draw_rect.origin.y || bmins[0] > xy_draw_rect.origin.x + xy_draw_rect.size.width || bmins[1] > xy_draw_rect.origin.y + xy_draw_rect.size.height) ) return self; // off view, don't bother for (i=0 ; i<numfaces ; i++) { w = faces[i].w; if (!w) continue; if (DotProduct (faces[i].plane.normal,xy_viewnormal) > -VECTOR_EPSILON) continue; XYmoveto (w->points[w->numpoints-1]); for (j=0 ; j<w->numpoints ; j++) XYlineto (w->points[j]); } if (keybrush) { // angle arrow val = [parent valueForQKey: "angle"]; if (val && val[0]) { ang = atof(val) * M_PI / 180; if (ang > 0) // negative values are up/down flags { mid[0] = (bmins[0]+bmaxs[0])/2; mid[1] = (bmins[1]+bmaxs[1])/2; end[0] = mid[0] + 16*cos(ang); end[1] = mid[1] + 16*sin(ang); s1[0] = mid[0] + 12*cos(ang+0.4); s1[1] = mid[1] + 12*sin(ang+0.4); s2[0] = mid[0] + 12*cos(ang-0.4); s2[1] = mid[1] + 12*sin(ang-0.4); XYmoveto ( mid); XYlineto ( end ); XYmoveto ( s1); XYlineto ( end ); XYlineto ( s2 ); } } } return self; } /* =========== ZDrawSelf =========== */ - ZDrawSelf { int i; vec3_t p1, p2; vec3_t frontpoint, backpoint; int frontface, backface; qtexture_t *q; if ([self fakeBrush: @selector(ZDrawSelf)]) return self; [zview_i addToHeightRange: bmins[2]]; [zview_i addToHeightRange: bmaxs[2]]; if (selected) { PSmoveto (1, bmaxs[2]); PSlineto (23, bmaxs[2]); PSlineto (23, bmins[2]); PSlineto (1, bmins[2]); PSlineto (1, bmaxs[2]); PSsetrgbcolor (1,0,0); PSstroke (); } [zview_i getPoint: (NXPoint *)p1]; for (i=0 ; i<2 ; i++) if (bmins[i] >= p1[i] || bmaxs[i] <= p1[i]) return self; p1[2] = 4096; p2[0] = p1[0]; p2[1] = p1[1]; p2[2] = -4096; [self clipRay: p1 : p2 : frontpoint: &frontface : backpoint : &backface]; if (frontface == -1 || backface == -1) return self; q = TEX_ForName (faces[frontface].texture.texture); PSmoveto (-8, frontpoint[2]); PSlineto (8, frontpoint[2]); PSlineto (8, backpoint[2]); PSlineto (-8, backpoint[2]); PSlineto (-8, frontpoint[2]); PSsetrgbcolor (q->flatcolor.chan[0]/255.0 , q->flatcolor.chan[1]/255.0 , q->flatcolor.chan[2]/255.0); PSfill (); PSmoveto (-12, frontpoint[2]); PSlineto (12, frontpoint[2]); PSlineto (12, backpoint[2]); PSlineto (-12, backpoint[2]); PSlineto (-12, frontpoint[2]); PSsetrgbcolor (0,0,0); PSstroke (); return self; } /* =========== CameraDrawSelf =========== */ - CameraDrawSelf { int i, j; winding_t *w; id worldent, currentent; if ([self fakeBrush: @selector(CameraDrawSelf)]) return self; worldent = [map_i objectAt: 0]; currentent = [map_i currentEntity]; if (parent != worldent && worldent == currentent) linecolor (entitycolor[0], entitycolor[1], entitycolor[2]); else if (selected) linecolor (1,0,0); else if (parent == [map_i currentEntity]) linecolor (0,0,0); else linecolor (0,0.5,0); for (i=0 ; i<numfaces ; i++) { w = faces[i].w; if (!w) continue; CameraMoveto (w->points[w->numpoints-1]); for (j=0 ; j<w->numpoints ; j++) CameraLineto (w->points[j]); } return self; } /* =========== XYRenderSelf =========== */ - XYRenderSelf { int i; if ([self fakeBrush: @selector(XYRenderSelf)]) return self; for (i=0 ; i<numfaces ; i++) REN_DrawXYFace (&faces[i]); return self; } /* =========== CameraRenderSelf =========== */ - CameraRenderSelf { int i; BOOL olddraw; extern qtexture_t badtex; pixel32_t p; if ([self fakeBrush: @selector(CameraRenderSelf)]) return self; // hack to draw entity boxes as single flat color if ( ![parent modifiable] ) { olddraw = r_drawflat; r_drawflat = YES; p = badtex.flatcolor; badtex.flatcolor.chan[0] = entitycolor[0]*255; badtex.flatcolor.chan[1] = entitycolor[1]*255; badtex.flatcolor.chan[2] = entitycolor[2]*255; for (i=0 ; i<numfaces ; i++) REN_DrawCameraFace (&faces[i]); badtex.flatcolor = p; r_drawflat = olddraw; } else { for (i=0 ; i<numfaces ; i++) REN_DrawCameraFace (&faces[i]); } return self; } /* ============================================================================== SINGLE BRUSH ACTIONS ============================================================================== */ face_t *dragface, *dragface2; int numcontrolpoints; float *controlpoints[MAX_FACES*3]; - (BOOL)checkModifiable { // int i; if ( [parent modifiable] ) return YES; // don't stretch spawned entities, move all points #if 0 numcontrolpoints = numfaces*3; for (i=0 ; i<numcontrolpoints ; i++) controlpoints[i] = faces[i/3].planepts[i%3]; #endif return NO; } - getZdragface: (vec3_t)dragpoint { int i, j; float d; if (![self checkModifiable]) return self; numcontrolpoints = 0; for (i=0 ; i<numfaces ; i++) { if (!faces[i].w) continue; if (faces[i].plane.normal[2] == 1) d = dragpoint[2] - faces[i].plane.dist; else if (faces[i].plane.normal[2] == -1) d = -faces[i].plane.dist - dragpoint[2]; else continue; if (d <= 0) continue; for (j=0 ; j<3 ; j++) { controlpoints[numcontrolpoints] = faces[i].planepts[j]; numcontrolpoints++; } } return self; } - getXYdragface: (vec3_t)dragpoint { int i,j; float d; numcontrolpoints = 0; if (![self checkModifiable]) return self; for (i=0 ; i<numfaces ; i++) { if (!faces[i].w) continue; if (faces[i].plane.normal[2]) continue; d = DotProduct(faces[i].plane.normal, dragpoint) - faces[i].plane.dist; if (d <= 0) continue; for (j=0 ; j<3 ; j++) { controlpoints[numcontrolpoints] = faces[i].planepts[j]; numcontrolpoints++; } } return self; } - getXYShearPoints: (vec3_t)dragpoint { int i,j, k; int facectl; float d; int numdragplanes; BOOL dragplane[MAX_FACES]; winding_t *w; face_t *f; BOOL onplane[MAX_POINTS_ON_WINDING]; if (![self checkModifiable]) return self; numcontrolpoints = 0; numdragplanes = 0; for (i=0 ; i<numfaces ; i++) { dragplane[i] = NO; if (!faces[i].w) continue; // if (faces[i].plane.normal[2]) // continue; d = DotProduct(faces[i].plane.normal, dragpoint) - faces[i].plane.dist; if (d <= -ON_EPSILON) continue; dragplane[i] = YES; numdragplanes++; } // find faces that just share an edge with a drag plane for (i=0 ; i<numfaces ; i++) { f = &faces[i]; w = f->w; if (!w) continue; if (dragplane[i] && numdragplanes == 1) { for (j=0 ; j<3 ; j++) { controlpoints[numcontrolpoints] = faces[i].planepts[j]; numcontrolpoints++; } continue; } if (!dragplane[i] && numdragplanes > 1) continue; facectl = 0; for (j=0 ; j<w->numpoints ; j++) { onplane[j] = NO; for (k=0 ; k<numfaces ; k++) { if (!dragplane[k]) continue; if (k == i) continue; d = DotProduct (w->points[j], faces[k].plane.normal) - faces[k].plane.dist; if (fabs(d) > ON_EPSILON) continue; onplane[j] = YES; facectl++; break; } } if (facectl == 0) continue; // find one or two static points to go with the controlpoints // and change the plane points k = 0; for (j=0 ; j<w->numpoints ; j++) { if (!onplane[j]) continue; if (facectl >= 2 && !onplane[(j+1)%w->numpoints]) continue; if (facectl == 3 && !onplane[(j+2)%w->numpoints]) continue; VectorCopy (w->points[j], f->planepts[k]); controlpoints[numcontrolpoints] = f->planepts[k]; numcontrolpoints++; k++; if (facectl >= 2) { VectorCopy (w->points[(j+1)%w->numpoints], f->planepts[k]); controlpoints[numcontrolpoints] = f->planepts[k]; numcontrolpoints++; k++; } if (facectl == 3) { VectorCopy (w->points[(j+2)%w->numpoints], f->planepts[k]); controlpoints[numcontrolpoints] = f->planepts[k]; numcontrolpoints++; k++; } break; } for ( ; j<w->numpoints && k != 3 ; j++) if (!onplane[j]) { VectorCopy (w->points[j], f->planepts[k]); k++; } for (j=0 ; j<w->numpoints && k != 3 ; j++) if (!onplane[j]) { VectorCopy (w->points[j], f->planepts[k]); k++; } if (k != 3) { // Error ("getXYShearPoints: didn't get three points on plane"); numcontrolpoints = 0; return self; } for (j=0 ; j<3 ; j++) for (k=0 ; k<3 ; k++) f->planepts[j][k] = rint(f->planepts[j][k]); } return self; } /* ============================================================================== MULTIPLE BRUSH ACTIONS ============================================================================== */ vec3_t region_min, region_max; /* =========== newRegion Set the regioned flag based on if the object is containted in region_min/max =========== */ - newRegion { int i; char *name; // filter away entities if (parent != [map_i objectAt: 0]) { if (filter_entities) { regioned = YES; return self; } name = [parent valueForQKey: "classname"]; if ( (filter_light && !strncmp(name,"light",5) ) || (filter_path && !strncmp(name,"path",4) ) ) { regioned = YES; return self; } } else if (filter_world) { regioned = YES; return self; } if (filter_clip_brushes && !strcasecmp(faces[0].texture.texture, "clip")) { regioned = YES; return self; } if (filter_water_brushes && faces[0].texture.texture[0] == '*') { regioned = YES; return self; } for (i=0 ; i<3 ; i++) { if (region_min[i] >= bmaxs[i] || region_max[i] <= bmins[i]) { if (selected) [self deselect]; regioned = YES; return self; } } regioned = NO; return self; } vec3_t select_min, select_max; - selectPartial { int i; for (i=0 ; i<3 ; i++) if (select_min[i] >= bmaxs[i] || select_max[i] <= bmins[i]) return self; selected = YES; return self; } - selectComplete { int i; for (i=0 ; i<3 ; i++) if (select_min[i] > bmins[i] || select_max[i] < bmaxs[i]) return self; selected = YES; return self; } - regionPartial { int i; for (i=0 ; i<3 ; i++) if (select_min[i] >= bmaxs[i] || select_max[i] <= bmins[i]) return self; selected = YES; return self; } - regionComplete { int i; for (i=0 ; i<3 ; i++) if (select_min[i] > bmins[i] || select_max[i] < bmaxs[i]) return self; selected = YES; return self; } id sb_newowner; - moveToEntity { id eclass; float *c; [parent removeObject: self]; parent = sb_newowner; // hack to allow them to be copied to another map if ( [parent respondsTo:@selector(valueForQKey:)]) { eclass = [entity_classes_i classForName: [parent valueForQKey: "classname"]]; c = [eclass drawColor]; [self setEntityColor: c]; } [parent addObject: self]; return self; } vec3_t sb_translate; - translate { int i, j; // move the planes for (i=0; i<numfaces ; i++) for (j=0 ; j<3 ; j++) { VectorAdd (faces[i].planepts[j], sb_translate, faces[i].planepts[j]); } [self calcWindings]; return self; } vec3_t sb_mins, sb_maxs; - addToBBox { int k; if (numfaces < 4) return self; for (k=0 ; k<3 ; k++) { if (bmins[k] < sb_mins[k]) sb_mins[k] = bmins[k]; if (bmaxs[k] > sb_maxs[k]) sb_maxs[k] = bmaxs[k]; } return self; } - flushTextures { // call when texture palette changes int i; for (i=0 ; i<MAX_FACES ; i++) faces[i].qtexture = NULL; [self calcWindings]; return self; } - select { [map_i setCurrentEntity: parent]; selected = YES; return self; } - deselect { selected = NO; // the last selected brush determines if (invalid) printf ("WARNING: deselected invalid brush\n"); [map_i setCurrentMinZ: bmins[2]]; [map_i setCurrentMaxZ: bmaxs[2]]; return self; } - remove { // the last selected brush determines if (!invalid) { [map_i setCurrentMinZ: bmins[2]]; [map_i setCurrentMaxZ: bmaxs[2]]; } [parent removeObject: self]; [self free]; return nil; } vec3_t sel_x, sel_y, sel_z; vec3_t sel_org; - transform { int i,j; vec3_t old; float *p; for (i=0 ; i<numfaces ; i++) for (j=0 ; j<3 ; j++) { p = faces[i].planepts[j]; VectorCopy (p, old); VectorSubtract (old, sel_org, old); p[0] = DotProduct (old, sel_x); p[1] = DotProduct (old, sel_y); p[2] = DotProduct (old, sel_z); VectorAdd (p, sel_org, p); } [self calcWindings]; return self; } - flipNormals // used after an inside-out transform (flip x/y/z) { int i; vec3_t temp; for (i=0 ; i<numfaces ; i++) { VectorCopy (faces[i].planepts[0], temp); VectorCopy (faces[i].planepts[2], faces[i].planepts[0]); VectorCopy (temp, faces[i].planepts[2]); } [self calcWindings]; return self; } - carveByClipper { face_t face; if (![clipper_i getFace: &face]) return self; [self addFace: &face]; return self; } - takeCurrentTexture { texturedef_t td; [texturepalette_i getTextureDef: &td]; [self setTexturedef: &td]; return self; } float sb_floor_dir, sb_floor_dist; - feetToFloor { float oldz; vec3_t p1, p2; int frontface, backface; vec3_t frontpoint, backpoint; float dist; [cameraview_i getOrigin: p1]; VectorCopy (p1, p2); oldz = p1[2] - 48; p1[2] = 4096; p2[2] = -4096; [self clipRay: p1 : p2 : frontpoint : &frontface : backpoint : &backface]; if (frontface == -1) return self; dist = frontpoint[2] - oldz; if (sb_floor_dir == 1) { if (dist > 0 && dist < sb_floor_dist) sb_floor_dist = dist; } else { if (dist < 0 && dist > sb_floor_dist) sb_floor_dist = dist; } return self; } /* =============================================================================== BRUSH SUBTRACTION =============================================================================== */ vec3_t carvemin, carvemax; int numcarvefaces; face_t *carvefaces; id carve_in, carve_out; // returns the new brush formed after the addition of the given plane // nil is returned if it faced all of the original setbrush - addFace: (face_t *)f { if (numfaces == MAX_FACES) Error ("addFace: numfaces == MAX_FACES"); faces[numfaces] = *f; faces[numfaces].texture = faces[0].texture; faces[numfaces].qtexture = NULL; faces[numfaces].w = NULL; numfaces++; [self calcWindings]; // remove any degenerate faces return [self removeIfInvalid]; } - clipByFace: (face_t *)fa front:(id *)f back:(id *)b { id front, back; face_t fb; vec3_t temp; fb = *fa; VectorCopy (fb.planepts[0], temp); VectorCopy (fb.planepts[2], fb.planepts[0]); VectorCopy (temp, fb.planepts[2]); front = [self copy]; back = [self copy]; *b = [back addFace: fa]; *f = [front addFace: &fb]; return self; } - carve { int i; id front, back; #if 0 if ( (i = NXMallocCheck()) ) Error ("MallocCheck failure"); #endif // check bboxes for (i=0 ; i<3 ; i++) if (bmins[i] >= carvemax[i] || bmaxs[i] <= carvemin[i]) { [carve_out addObject: self]; return self; } // carve by the planes back = self; for (i=0 ; i<numcarvefaces ; i++) { [back clipByFace: &carvefaces[i] front:&front back:&back]; if (front) [carve_out addObject: front]; if (!back) return nil; // nothing completely inside } [carve_in addObject: back]; return self; } /* ================== setCarveVars ================== */ - setCarveVars { VectorCopy (bmins, carvemin); VectorCopy (bmaxs, carvemax); numcarvefaces = numfaces; carvefaces = faces; return self; } - (int) getNumBrushFaces { return numfaces; } - (face_t *) getBrushFace: (int)which { return &faces[which]; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.