#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 ; inumpoints ; 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 ; jnumpoints ; 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 ; inumpoints ; 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]) { dealloc (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 ; inumpoints ; 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 dealloc (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 ; iplanepts[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 ; iw = w; if (w) { CheckFace (f); for (j=0 ; jnumpoints ; 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 ; iplanepts[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 ; iplanepts[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 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= 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 ; iw) 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 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 -VECTOR_EPSILON) continue; XYmoveto (w->points[w->numpoints-1]); for (j=0 ; jnumpoints ; 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: (NSPoint *)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 objectAtIndex: 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 ; ipoints[w->numpoints-1]); for (j=0 ; jnumpoints ; j++) CameraLineto (w->points[j]); } return self; } /* =========== XYRenderSelf =========== */ - XYRenderSelf { int i; if ([self fakeBrush: @selector(XYRenderSelf)]) return self; for (i=0 ; iw; 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 ; jnumpoints ; j++) { onplane[j] = NO; for (k=0 ; kpoints[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 ; jnumpoints ; 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 ( ; jnumpoints && k != 3 ; j++) if (!onplane[j]) { VectorCopy (w->points[j], f->planepts[k]); k++; } for (j=0 ; jnumpoints && 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 objectAtIndex: 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 sb_maxs[k]) sb_maxs[k] = bmaxs[k]; } return self; } - flushTextures { // call when texture palette changes int i; for (i=0 ; i 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