int TexelDelta( face_t *f, dplane_t *plane ) { int current, delta; current = delta = 0; // Does the plane split the face? if ( FaceSide (f, plane) == SIDE_ON ) { face_t *front, *back; // Compute the change in texture cache from splitting the face current = TexelSize( f ); // Speculatively split the face SplitFaceTmp(f, plane, &front, &back); if (!front || !back) // Didn't actually split the face delta = 0; else { delta = 0;//TexelSize( front ) + TexelSize( back ) - current; // Change in texel size FreeFace( front ); // Free new faces FreeFace( back ); } } return delta; }
static void FreeTree_r (node_t *node) { face_t *f, *nextf; /* free children */ if (node->planenum != PLANENUM_LEAF) { FreeTree_r(node->children[0]); FreeTree_r(node->children[1]); } /* free bspbrushes */ FreeBrushList(node->brushlist); /* free faces */ for (f = node->faces; f; f = nextf) { nextf = f->next; FreeFace(f); } /* free the node */ if (node->volume) FreeBrush(node->volume); if (threadstate.numthreads == 1) c_nodes--; Mem_Free(node); }
//----------------------------------------------------------------------------- // Purpose: Loop through each face and filter it into the tree // Input : *out - // *pFaces - //----------------------------------------------------------------------------- face_t *FilterFacesIntoTree( tree_t *out, face_t *pFaces ) { face_t *pLeafFaceList = NULL; for ( face_t *f = pFaces; f; f = f->next ) { if( f->merged || f->split[0] || f->split[1] ) continue; face_t *tmp = CopyFace( f ); face_t *original = CopyFace( f ); if ( MergeFace_r( out->headnode, tmp, original ) ) { // clear out portal (comes from a different tree) original->portal = NULL; original->next = pLeafFaceList; pLeafFaceList = original; } else { FreeFace( original ); } } return pLeafFaceList; }
face_t *MergeFaceToList (face_t *face, face_t *list) { face_t *newf, *f; for (f=list ; f ; f=f->next) { //CheckColinear (f); if (mergedebug) { Draw_ClearWindow (); Draw_DrawFace (face); Draw_DrawFace (f); Draw_SetBlack (); } newf = TryMerge (face, f); if (!newf) continue; FreeFace (face); f->numpoints = -1; // merged out return MergeFaceToList (newf, list); } // didn't merge, so add at start face->next = list; return face; }
/* ============= FreeTree_r ============= */ void FreeTree_r( node_t *node ){ face_t *f, *nextf; // free children if ( node->planenum != PLANENUM_LEAF ) { FreeTree_r( node->children[0] ); FreeTree_r( node->children[1] ); } // free bspbrushes FreeBrushList( node->brushlist ); // free faces for ( f = node->faces ; f ; f = nextf ) { nextf = f->next; FreeFace( f ); } // free the node if ( node->volume ) { FreeBrush( node->volume ); } if ( numthreads == 1 ) { c_nodes--; } free( node ); }
/* ================== SaveOutside The faces remaining on the outside list are final polygons. Write them to the output file. Passable contents (water, lava, etc) will generate a mirrored copy of the face to be seen from the inside. ================== */ void SaveOutside (brush_t *b, int hull, bface_t *outside, int mirrorcontents) { bface_t *f , *next, *f2; int i; int planenum; vec3_t temp; for (f=outside ; f ; f=next) { next = f->next; if (WindingArea (f->w) < 1.0) { c_tiny++; qprintf ("Entity %i, Brush %i: tiny fragment\n" , b->entitynum, b->brushnum); continue; } // count unique faces if (!hull) { for (f2=b->hulls[hull].faces ; f2 ; f2=f2->next) { if (f2->planenum == f->planenum) { if (!f2->used) { f2->used = true; c_outfaces++; } break; } } } WriteFace (hull, f); // if (mirrorcontents != CONTENTS_SOLID) { f->planenum ^= 1; f->plane = &mapplanes[f->planenum]; f->contents = mirrorcontents; // swap point orders for (i=0 ; i<f->w->numpoints/2 ; i++) // add points backwards { VectorCopy (f->w->p[i], temp); VectorCopy (f->w->p[f->w->numpoints-1-i] , f->w->p[i]); VectorCopy (temp, f->w->p[f->w->numpoints-1-i]); } WriteFace (hull, f); } FreeFace (f); } }
/* ================= FreeBrushFaces ================= */ void FreeBrushFaces(face_t *pFaceList) { face_t *pFace, *pNext; for (pFace = pFaceList; pFace; pFace = pNext) { pNext = pFace->next; FreeFace(pFace); } }
//----------------------------------------------------------------------------- // Purpose: Free the list of faces stored at the leaves //----------------------------------------------------------------------------- void FreeLeafFaces( face_t *pLeafFaceList ) { int count = 0; face_t *f, *next; f = pLeafFaceList; while ( f ) { next = f->next; FreeFace( f ); f = next; count++; } }
/* ================== FreeInside Free all the faces that got clipped out ================== */ static void FreeInside( int contents ) { face_t *f, *next; for( f = inside; f; f = next ) { next = f->next; if( contents == CONTENTS_SOLID ) { FreeFace (f); continue; } f->contents[0] = contents; f->next = outside; outside = f; } }
/* ============= FreeLeafSurfs ============= */ void FreeLeafSurfs (node_t *leaf) { surface_t *surf, *snext; face_t *f, *fnext; for (surf = leaf->surfaces ; surf ; surf=snext) { snext = surf->next; for (f=surf->faces ; f ; f=fnext) { fnext = f->next; FreeFace (f); } FreeSurface (surf); } leaf->surfaces = NULL; }
/* =============== MergeFaceToList =============== */ face_t *MergeFaceToList (face_t *face, face_t *list) { face_t *newf, *f; for (f=list ; f ; f=f->next) { newf = TryMerge (face, f); if (!newf) continue; FreeFace (face); ResizeFace (f, 0); f->numpoints = -1; // merged out return MergeFaceToList (newf, list); } // didn't merge, so add at start face->next = list; return face; }
/* =============== FreeMergeListScraps =============== */ face_t *FreeMergeListScraps (face_t *merged) { face_t *head, *next; head = NULL; for ( ; merged ; merged = next) { next = merged->next; if (merged->numpoints == -1) FreeFace (merged); else { merged->next = head; head = merged; } } return head; }
/* =========== FreeDrawNodes_r =========== */ void FreeDrawNodes_r (node_t *node) { int i; face_t *f, *next; for (i=0 ; i<2 ; i++) if (node->children[i]->planenum != -1) FreeDrawNodes_r (node->children[i]); // // free the faces on the node // for (f=node->faces ; f ; f=next) { next = f->next; FreeFace (f); } free (node); }
/* ============================================================================= GatherNodeFaces Frees the current node tree and returns a new chain of the surfaces that have inside faces. ============================================================================= */ static void GatherNodeFaces_r (node_t *node) { face_t *f, *next; if (node->planenum != PLANENUM_LEAF) { // // decision node // for (f = node->faces ; f ; f = next) { next = f->next; if (!f->numpoints) { // face was removed outside FreeFace (f); } else { f->next = validfaces[f->planenum]; validfaces[f->planenum] = f; } } GatherNodeFaces_r (node->children[0]); GatherNodeFaces_r (node->children[1]); free (node); } else { // // leaf node // free (node); } }
/* ================== LinkConvexFaces Determines the contents of the leaf and creates the final list of original faces that have some fragment inside this leaf ================== */ void LinkConvexFaces (surface_t *planelist, node_t *leafnode) { face_t *f, *next; surface_t *surf, *pnext; int i, count; leafnode->faces = NULL; leafnode->contents = 0; leafnode->planenum = -1; count = 0; for ( surf = planelist ; surf ; surf = surf->next) { for (f = surf->faces ; f ; f=f->next) { count++; if (!leafnode->contents) leafnode->contents = f->contents[0]; else if (leafnode->contents != f->contents[0]) Error ("LinkConvexFaces: Mixed face contents in leafnode"); } } if (!leafnode->contents) leafnode->contents = CONTENTS_SOLID; switch (leafnode->contents) { case CONTENTS_EMPTY: c_empty++; break; case CONTENTS_SOLID: c_solid++; break; case CONTENTS_WATER: case CONTENTS_SLIME: case CONTENTS_LAVA: case CONTENTS_SKY: c_water++; break; default: Error ("LinkConvexFaces: bad contents number"); } // // write the list of faces, and free the originals // leaffaces += count; leafnode->markfaces = malloc(sizeof(face_t *)*(count+1)); i = 0; for ( surf = planelist ; surf ; surf = pnext) { pnext = surf->next; for (f = surf->faces ; f ; f=next) { next = f->next; leafnode->markfaces[i] = f->original; i++; FreeFace (f); } FreeSurface (surf); } leafnode->markfaces[i] = NULL; // sentinal }
//----------------------------------------------------------------------------- // Purpose: Recursively filter a face into the tree leaving references to the // original face in any visible leaves that a clipped fragment falls // into. // Input : *node - current head of tree // *face - clipped face fragment // *original - unclipped original face // Output : Returns true if any references were left //----------------------------------------------------------------------------- bool MergeFace_r( node_t *node, face_t *face, face_t *original ) { bool referenced = false; if ( node->planenum == PLANENUM_LEAF ) { if ( node->contents & CONTENTS_SOLID ) { FreeFace( face ); return false; } leafface_t *plist = new leafface_t; plist->pFace = original; plist->pNext = node->leaffacelist; node->leaffacelist = plist; referenced = true; } else { // UNDONE: Don't copy the faces each time unless it's necessary!?!?! plane_t *plane = &g_MainMap->mapplanes[node->planenum]; winding_t *frontwinding, *backwinding, *onwinding; Vector offset; WindingCenter( face->w, offset ); // UNDONE: Export epsilon from original face clipping code ClassifyWindingEpsilon_Offset(face->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, &onwinding, -offset); if ( onwinding ) { // face is in the split plane, go down the appropriate side according to the facing direction assert( frontwinding == NULL ); assert( backwinding == NULL ); if ( DotProduct( g_MainMap->mapplanes[face->planenum].normal, g_MainMap->mapplanes[node->planenum].normal ) > 0 ) { frontwinding = onwinding; } else { backwinding = onwinding; } } if ( frontwinding ) { face_t *tmp = NewFaceFromFace( face ); tmp->w = frontwinding; referenced = MergeFace_r( node->children[0], tmp, original ); } if ( backwinding ) { face_t *tmp = NewFaceFromFace( face ); tmp->w = backwinding; bool test = MergeFace_r( node->children[1], tmp, original ); referenced = referenced || test; } } FreeFace( face ); return referenced; }
// Compute a list of faces that are visible on the detail brush sides face_t *ComputeVisibleBrushSides( bspbrush_t *list ) { face_t *pTotalFaces = NULL; CUtlVector<bspbrush_t *> cutBrushes; // Go through the whole brush list for ( bspbrush_t *pbrush = list; pbrush; pbrush = pbrush->next ) { face_t *pFaces = NULL; mapbrush_t *mb = pbrush->original; if ( !(mb->contents & ALL_VISIBLE_CONTENTS) ) continue; // Make a face for each brush side, then clip it by the other // details to see if any fragments are visible for ( int i = 0; i < pbrush->numsides; i++ ) { winding_t *winding = pbrush->sides[i].winding; if ( !winding ) continue; if (! (pbrush->sides[i].contents & ALL_VISIBLE_CONTENTS) ) continue; side_t *side = FindOriginalSide( mb, pbrush->sides + i ); face_t *f = MakeBrushFace( side, winding ); // link to head of face list f->next = pFaces; pFaces = f; } // Make a list of brushes that can cut the face list for this brush cutBrushes.RemoveAll(); if ( GetListOfCutBrushes( cutBrushes, pbrush, list ) ) { // now cut each face to find visible fragments for ( face_t *f = pFaces; f; f = f->next ) { // this will be a new list of faces that this face cuts into face_t *pClip = NULL; ClipFaceToBrushList( f, cutBrushes, &pClip ); if ( pClip ) { int outCount = CountFaceList(pClip); // it cut into more faces (or it was completely cut away) if ( outCount <= 1 ) { // was removed or cut down, mark as split f->split[0] = f; // insert face fragments at head of list (UNDONE: reverses order, do we care?) while ( pClip ) { face_t *next = pClip->next; pClip->next = pFaces; pFaces = pClip; pClip = next; } } else { // it cut into more than one visible fragment // Don't fragment details // UNDONE: Build 2d convex hull of this list and swap face winding // with that polygon? That would fix the remaining issues. FreeFaceList( pClip ); pClip = NULL; } } } } // move visible fragments to global face list while ( pFaces ) { face_t *next = pFaces->next; if ( pFaces->split[0] ) { FreeFace( pFaces ); } else { pFaces->next = pTotalFaces; pTotalFaces = pFaces; } pFaces = next; } } return pTotalFaces; }
//----------------------------------------------------------------------------- // Purpose: // Input : *pFace - input face to test // *pbrush - brush to clip face against // **pOutputList - list of faces clipped from pFace // Output : Returns true if the brush completely clips the face //----------------------------------------------------------------------------- // NOTE: This assumes the brushes have already been chopped so that no solid space // is enclosed by more than one brush!! bool ClipFaceToBrush( face_t *pFace, bspbrush_t *pbrush, face_t **pOutputList ) { int planenum = pFace->planenum & (~1); int foundSide = -1; CUtlVector<int> sortedSides; int i; for ( i = 0; i < pbrush->numsides && foundSide < 0; i++ ) { int bplane = pbrush->sides[i].planenum & (~1); if ( bplane == planenum ) foundSide = i; } Vector offset = -0.5f * (pbrush->maxs + pbrush->mins); face_t *currentface = CopyFace( pFace ); if ( foundSide >= 0 ) { sortedSides.RemoveAll(); for ( i = 0; i < pbrush->numsides; i++ ) { // don't clip to bevels if ( pbrush->sides[i].bevel ) continue; if ( g_MainMap->mapplanes[pbrush->sides[i].planenum].type <= PLANE_Z ) { sortedSides.AddToHead( i ); } else { sortedSides.AddToTail( i ); } } for ( i = 0; i < sortedSides.Size(); i++ ) { int index = sortedSides[i]; if ( index == foundSide ) continue; plane_t *plane = &g_MainMap->mapplanes[pbrush->sides[index].planenum]; winding_t *frontwinding, *backwinding; ClipWindingEpsilon_Offset(currentface->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, offset); // only clip if some part of this face is on the back side of all brush sides if ( !backwinding || WindingIsTiny(backwinding)) { FreeFaceList( *pOutputList ); *pOutputList = NULL; break; } if ( frontwinding && !WindingIsTiny(frontwinding) ) { // add this fragment to the return list // make a face for the fragment face_t *f = NewFaceFromFace( pFace ); f->w = frontwinding; // link the fragment in f->next = *pOutputList; *pOutputList = f; } // update the current winding to be the part behind each plane FreeWinding( currentface->w ); currentface->w = backwinding; } // free the bit that is left in solid or not clipped (if we broke out early) FreeFace( currentface ); // if we made it all the way through and didn't produce any fragments then the whole face was clipped away if ( !*pOutputList && i == sortedSides.Size() ) { return true; } } return false; }
/* ================== ClearOutFaces_r Removes unused nodes ================== */ node_t *ClearOutFaces_r (node_t *node) { face_t *f, *fnext; face_t **fp; portal_t *p; // mark the node and all it's faces, so they // can be removed if no children use them node->valid = 0; // will be set if any children touch it for (f=node->faces ; f ; f=f->next) f->outputnumber = -1; // go down the children if (node->planenum != -1) { // // decision node // node->children[0] = ClearOutFaces_r (node->children[0]); node->children[1] = ClearOutFaces_r (node->children[1]); // free any faces not in open child leafs f=node->faces; node->faces = NULL; for ( ; f ; f=fnext) { fnext = f->next; if (f->outputnumber == -1) { // never referenced, so free it c_free_faces++; FreeFace (f); } else { c_keep_faces++; f->next = node->faces; node->faces = f; } } if (!node->valid) { // this node does not touch any interior leafs // if both children are solid, just make this node solid if (node->children[0]->contents == CONTENTS_SOLID && node->children[1]->contents == CONTENTS_SOLID) { node->contents = CONTENTS_SOLID; node->planenum = -1; return node; } // if one child is solid, shortcut down the other side if (node->children[0]->contents == CONTENTS_SOLID) return node->children[1]; if (node->children[1]->contents == CONTENTS_SOLID) return node->children[0]; c_falsenodes++; } return node; } // // leaf node // if (node->contents != CONTENTS_SOLID) { // this node is still inside // mark all the nodes used as portals for (p = node->portals ; p ; ) { if (p->onnode) p->onnode->valid = 1; if (p->nodes[0] == node) // only write out from first leaf p = p->next[0]; else p = p->next[1]; } // mark all of the faces to be drawn for (fp = node->markfaces ; *fp ; fp++) (*fp)->outputnumber = 0; return node; } // this was a filled in node, so free the markfaces if (node->planenum != -1) free (node->markfaces); return node; }
/* =========== CSGBrush =========== */ void CSGBrush (int brushnum) { int hull; brush_t *b1, *b2; brushhull_t *bh1, *bh2; int bn; qboolean overwrite; int i; bface_t *f, *f2, *next, *fcopy; bface_t *outside, *oldoutside; entity_t *e; vec_t area; SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_ABOVE_NORMAL); b1 = &mapbrushes[brushnum]; e = &entities[b1->entitynum]; for (hull = 0 ; hull<NUM_HULLS ; hull++) { bh1 = &b1->hulls[hull]; // set outside to a copy of the brush's faces outside = CopyFacesToOutside (bh1); overwrite = false; for (bn=0 ; bn<e->numbrushes ; bn++) { // see if b2 needs to clip a chunk out of b1 if (bn==brushnum) { overwrite = true; // later brushes now overwrite continue; } b2 = &mapbrushes[e->firstbrush + bn]; bh2 = &b2->hulls[hull]; if (!bh2->faces) continue; // brush isn't in this hull // check brush bounding box first for (i=0 ; i<3 ; i++) if (bh1->mins[i] > bh2->maxs[i] || bh1->maxs[i] < bh2->mins[i]) break; if (i<3) continue; // divide faces by the planes of the b2 to find which // fragments are inside f = outside; outside = NULL; for ( ; f ; f=next) { next = f->next; // check face bounding box first for (i=0 ; i<3 ; i++) if (bh2->mins[i] > f->maxs[i] || bh2->maxs[i] < f->mins[i]) break; if (i<3) { // this face doesn't intersect brush2's bbox f->next = outside; outside = f; continue; } oldoutside = outside; fcopy = CopyFace (f); // save to avoid fake splits // throw pieces on the front sides of the planes // into the outside list, return the remains on the inside for (f2=bh2->faces ; f2 && f ; f2=f2->next) f = ClipFace (b1, f, &outside, f2->planenum, overwrite); area = f ? WindingArea (f->w) : 0; if (f && area < 1.0) { qprintf ("Entity %i, Brush %i: tiny penetration\n" , b1->entitynum, b1->brushnum); c_tiny_clip++; FreeFace (f); f = NULL; } if (f) { // there is one convex fragment of the original // face left inside brush2 FreeFace (fcopy); if (b1->contents > b2->contents) { // inside a water brush f->contents = b2->contents; f->next = outside; outside = f; } else // inside a solid brush FreeFace (f); // throw it away } else { // the entire thing was on the outside, even // though the bounding boxes intersected, // which will never happen with axial planes // free the fragments chopped to the outside while (outside != oldoutside) { f2 = outside->next; FreeFace (outside); outside = f2; } // revert to the original face to avoid // unneeded false cuts fcopy->next = outside; outside = fcopy; } } } // all of the faces left in outside are real surface faces SaveOutside (b1, hull, outside, b1->contents); } }
/* ================== SplitFace ================== */ void SplitFace (face_t *in, plane_t *split, face_t **front, face_t **back) { double dists[MAXEDGES+1]; int sides[MAXEDGES+1]; int counts[3]; double dot; int i, j; face_t *newf, *new2; double *p1, *p2; vec3_t mid; if (in->numpoints < 0) Error ("%s: freed face", __thisfunc__); counts[0] = counts[1] = counts[2] = 0; // determine sides for each point for (i = 0 ; i < in->numpoints ; i++) { dot = DotProduct (in->pts[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]) { *front = NULL; *back = in; return; } if (!counts[1]) { *front = in; *back = NULL; return; } *back = newf = NewFaceFromFace (in); *front = new2 = NewFaceFromFace (in); // distribute the points and generate splits for (i = 0 ; i < in->numpoints ; i++) { if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES) Error ("%s: numpoints > MAXEDGES", __thisfunc__); p1 = in->pts[i]; if (sides[i] == SIDE_ON) { VectorCopy (p1, newf->pts[newf->numpoints]); newf->numpoints++; VectorCopy (p1, new2->pts[new2->numpoints]); new2->numpoints++; continue; } if (sides[i] == SIDE_FRONT) { VectorCopy (p1, new2->pts[new2->numpoints]); new2->numpoints++; } else { VectorCopy (p1, newf->pts[newf->numpoints]); newf->numpoints++; } if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; // generate a split point p2 = in->pts[(i+1)%in->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; else mid[j] = p1[j] + dot*(p2[j]-p1[j]); } VectorCopy (mid, newf->pts[newf->numpoints]); newf->numpoints++; VectorCopy (mid, new2->pts[new2->numpoints]); new2->numpoints++; } if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES) Error ("%s: numpoints > MAXEDGES", __thisfunc__); #if 0 CheckFace (newf); CheckFace (new2); #endif // free the original face now that is is represented by the fragments FreeFace (in); }
/* ================= CreateBrushFaces ================= */ void CreateBrushFaces (void) { int i,j, k; vec_t r; face_t *f, *next; winding_t *w; plane_t clipplane, faceplane; mface_t *mf; vec3_t offset, point; offset[0] = offset[1] = offset[2] = 0; ClearBounds( brush_mins, brush_maxs ); brush_faces = NULL; if (!strncmp(ValueForKey(CurrentEntity, "classname"), "rotate_", 7)) { entity_t *FoundEntity; char *searchstring; char text[20]; searchstring = ValueForKey (CurrentEntity, "target"); FoundEntity = FindTargetEntity(searchstring); if (FoundEntity) GetVectorForKey(FoundEntity, "origin", offset); sprintf(text, "%g %g %g", offset[0], offset[1], offset[2]); SetKeyValue(CurrentEntity, "origin", text); } GetVectorForKey(CurrentEntity, "origin", offset); //printf("%i brushfaces at offset %f %f %f\n", numbrushfaces, offset[0], offset[1], offset[2]); for (i = 0;i < numbrushfaces;i++) { mf = &faces[i]; //printf("plane %f %f %f %f\n", mf->plane.normal[0], mf->plane.normal[1], mf->plane.normal[2], mf->plane.dist); faceplane = mf->plane; w = BaseWindingForPlane (&faceplane); //VectorNegate( faceplane.normal, point ); for (j = 0;j < numbrushfaces && w;j++) { clipplane = faces[j].plane; if( j == i/* || VectorCompare( clipplane.normal, point )*/ ) continue; // flip the plane, because we want to keep the back side VectorNegate(clipplane.normal, clipplane.normal); clipplane.dist *= -1; w = ClipWindingEpsilon (w, &clipplane, ON_EPSILON, true); } if (!w) { //printf("----- skipped plane -----\n"); continue; // overcontrained plane } // this face is a keeper f = AllocFace (); f->winding = w; for (j = 0;j < w->numpoints;j++) { for (k = 0;k < 3;k++) { point[k] = w->points[j][k] - offset[k]; r = Q_rint( point[k] ); if ( fabs( point[k] - r ) < ZERO_EPSILON) w->points[j][k] = r; else w->points[j][k] = point[k]; // check for incomplete brushes if( w->points[j][k] >= BOGUS_RANGE || w->points[j][k] <= -BOGUS_RANGE ) break; } // remove this brush if (k < 3) { FreeFace (f); for (f = brush_faces; f; f = next) { next = f->next; FreeFace (f); } brush_faces = NULL; //printf("----- skipped brush -----\n"); return; } AddPointToBounds( w->points[j], brush_mins, brush_maxs ); } CheckWinding( w ); faceplane.dist -= DotProduct(faceplane.normal, offset); f->texturenum = mf->texinfo; f->planenum = FindPlane (&faceplane, &f->planeside); f->next = brush_faces; brush_faces = f; } // Rotatable objects have to have a bounding box big enough // to account for all its rotations. if (DotProduct(offset, offset)) { vec_t delta; delta = RadiusFromBounds( brush_mins, brush_maxs ); for (k = 0;k < 3;k++) { brush_mins[k] = -delta; brush_maxs[k] = delta; } } //printf("%i : %f %f %f : %f %f %f\n", numbrushfaces, brush_mins[0], brush_mins[1], brush_mins[2], brush_maxs[0], brush_maxs[1], brush_maxs[2]); }