//----------------------------------------------------------------------------- // 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* MakePlaneList(face_t* top, face_t* bottom) { face_t* fList; face_t* curFace; face_t* newTop; face_t* newBot; int i; int j; int k; vec3_t t1, t2, t3; fList = NULL; newTop = Face_Alloc(); CopyFace(top, newTop); newBot = Face_Alloc(); CopyFace(bottom, newBot); WrapFaces(newTop, newBot); for (i = 0; i<newTop->face_winding->numpoints; i++) { if (i == (newTop->face_winding->numpoints - 1)) { j = 0; } else { j = i + 1; } curFace = Face_Alloc(); VectorCopy(newTop->face_winding->points[j],curFace->planepts[0]); VectorCopy(newTop->face_winding->points[i],curFace->planepts[1]); VectorCopy(newBot->face_winding->points[i],curFace->planepts[2]); for (k=0 ; k<3 ; k++) { t1[k] = curFace->planepts[0][k] - curFace->planepts[1][k]; t2[k] = curFace->planepts[2][k] - curFace->planepts[1][k]; t3[k] = curFace->planepts[1][k]; } CrossProduct(t1,t2, curFace->plane.normal); if (VectorCompare (curFace->plane.normal, vec3_origin)) { printf ("WARNING: brush plane with no normal\n"); } VectorNormalize (curFace->plane.normal); curFace->plane.dist = DotProduct (t3, curFace->plane.normal); curFace->next = fList; fList = curFace; } // for loop return fList; }
// Clips f to a list of potential cutting brushes // If f clips into new faces, returns the list of new faces in pOutputList static void ClipFaceToBrushList( face_t *f, const CUtlVector<bspbrush_t *> &cutBrushes, face_t **pOutputList ) { *pOutputList = NULL; if ( f->split[0] ) return; face_t *pClipList = CopyFace( f ); pClipList->next = NULL; bool clipped = false; for ( int i = 0; i < cutBrushes.Count(); i++ ) { bspbrush_t *cut = cutBrushes[i]; for ( face_t *pCutFace = pClipList; pCutFace; pCutFace = pCutFace->next ) { face_t *pClip = NULL; // already split, no need to clip if ( pCutFace->split[0] ) continue; if ( ClipFaceToBrush( pCutFace, cut, &pClip ) ) { clipped = true; // mark face bad, the brush clipped it away pCutFace->split[0] = pCutFace; } else if ( pClip ) { clipped = true; // mark this face as split pCutFace->split[0] = pCutFace; // insert face fragments at head of list (UNDONE: reverses order, do we care?) while ( pClip ) { face_t *next = pClip->next; pClip->next = pClipList; pClipList = pClip; pClip = next; } } } } if ( clipped ) { *pOutputList = pClipList; } else { // didn't do any clipping, go ahead and free the copy of the face here. FreeFaceList( pClipList ); } }
/* ================== CopyFacesToOutside Make a copy of all the faces of the brush, so they can be chewed up by other brushes. All of the faces start on the outside list. As other brushes take bites out of the faces, the fragments are moved to the inside list, so they can be freed when they are determined to be completely enclosed in solid. ================== */ bface_t *CopyFacesToOutside (brushhull_t *bh) { bface_t *f, *newf; bface_t *outside; outside = NULL; for (f=bh->faces ; f ; f=f->next) { brushfaces++; newf = CopyFace (f); WindingBounds (newf->w, newf->mins, newf->maxs); newf->next = outside; outside = newf; } return outside; }
//----------------------------------------------------------------------------- // 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; }
// Returns false if union of brushes is obviously zero static void AddPlaneToUnion(brushhull_t* hull, const int planenum) { bool need_new_face = false; bface_t* new_face_list; bface_t* face; bface_t* next; plane_t* split; Winding* front; Winding* back; new_face_list = NULL; next = NULL; hlassert(hull); if (!hull->faces) { return; } hlassert(hull->faces->w); for (face = hull->faces; face; face = next) { hlassert(face->w); next = face->next; // Duplicate plane, ignore if (face->planenum == planenum) { AddFaceToList(&new_face_list, CopyFace(face)); continue; } split = &g_mapplanes[planenum]; face->w->Clip(split->normal, split->dist, &front, &back); if (front) { delete front; need_new_face = true; if (back) { // Intersected the face delete face->w; face->w = back; AddFaceToList(&new_face_list, CopyFace(face)); } } else { // Completely missed it, back is identical to face->w so it is destroyed if (back) { delete back; AddFaceToList(&new_face_list, CopyFace(face)); } } hlassert(face->w); } FreeFaceList(hull->faces); hull->faces = new_face_list; if (need_new_face && (NumberOfHullFaces(hull) > 2)) { Winding* new_winding = NewWindingFromPlane(hull, planenum); if (new_winding) { bface_t* new_face = (bface_t*)Alloc(sizeof(bface_t)); new_face->planenum = planenum; new_face->w = new_winding; new_face->next = hull->faces; hull->faces = new_face; } } }
void WrapFaces( face_t* top, face_t* bottom) { face_t* tempFace; int i; float maxX; float maxY; int pointFlag; tempFace = Face_Alloc(); //wrap the top face points the other way CopyFace(top, tempFace); for ( i = 0; i<top->face_winding->numpoints; i++) { VectorCopy(top->face_winding->points[top->face_winding->numpoints-1-i],tempFace->face_winding->points[i]); } CopyFace(tempFace,top); // top and bottom are now wrapped with normals pointing upward // now grab the point in top with most positive x (and y if there are more than one) maxX = top->face_winding->points[0][0]; maxY = top->face_winding->points[0][1]; pointFlag = 0; for ( i = 1; i<top->face_winding->numpoints; i++) { if ( maxX > top->face_winding->points[i][0] ) { continue; } else { if ( maxX == top->face_winding->points[i][0] ) { if (top->face_winding->points[i][1] > maxY) { maxY = top->face_winding->points[i][1]; pointFlag = i; } } else { maxX = top->face_winding->points[i][0]; maxY = top->face_winding->points[i][1]; pointFlag = i; } } } // now, starting at the point[pointflag] in top, write the sequence starting at [0] in tempFace for ( i = 0; i<top->face_winding->numpoints; i++) { if (pointFlag == top->face_winding->numpoints) { pointFlag = 0; } VectorCopy(top->face_winding->points[pointFlag], tempFace->face_winding->points[i]); pointFlag++; } CopyFace(tempFace,top); //repeat with bottom CopyFace(bottom, tempFace); maxX = bottom->face_winding->points[0][0]; maxY = bottom->face_winding->points[0][1]; pointFlag = 0; for ( i = 1; i<bottom->face_winding->numpoints; i++) { if ( maxX > bottom->face_winding->points[i][0] ) { continue; } else { if ( maxX == bottom->face_winding->points[i][0] ) { if (bottom->face_winding->points[i][1] > maxY) { maxY = bottom->face_winding->points[i][1]; pointFlag = i; } } else { maxX = bottom->face_winding->points[i][0]; maxY = bottom->face_winding->points[i][1]; pointFlag = i; } } } // now, starting at the point[pointflag] in bottom, write the sequence starting at [0] in tempFace for ( i = 0; i<bottom->face_winding->numpoints; i++) { if (pointFlag == bottom->face_winding->numpoints) { pointFlag = 0; } VectorCopy(bottom->face_winding->points[pointFlag], tempFace->face_winding->points[i]); pointFlag++; } CopyFace(tempFace,bottom); Face_Free(tempFace); return; }
/* =========== 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); } }