//----------------------------------------------------------------------------- // 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; }
void SplitBrush( bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back ) { bspbrush_t *b[2]; int i, j; winding_t *w, *cw[2], *midwinding; plane_t *plane, *plane2; side_t *s, *cs; float d, d_front, d_back; *front = *back = NULL; plane = &g_MainMap->mapplanes[planenum]; // check all points d_front = d_back = 0; for (i=0 ; i<brush->numsides ; i++) { w = brush->sides[i].winding; if (!w) continue; for (j=0 ; j<w->numpoints ; j++) { d = DotProduct (w->p[j], plane->normal) - plane->dist; if (d > 0 && d > d_front) d_front = d; if (d < 0 && d < d_back) d_back = d; } } if (d_front < 0.1) // PLANESIDE_EPSILON) { // only on back *back = CopyBrush (brush); return; } if (d_back > -0.1) // PLANESIDE_EPSILON) { // only on front *front = CopyBrush (brush); return; } // Move the CSG problem so that offset is at the origin // This gives us much better floating point precision in the clipping operations Vector offset = -0.5f * (brush->mins + brush->maxs); // create a new winding from the split plane w = BaseWindingForPlane (plane->normal, plane->dist + DotProduct(plane->normal,offset)); for (i=0 ; i<brush->numsides && w ; i++) { plane2 = &g_MainMap->mapplanes[brush->sides[i].planenum ^ 1]; ChopWindingInPlace (&w, plane2->normal, plane2->dist+DotProduct(plane2->normal,offset), 0); // PLANESIDE_EPSILON); } if (!w || WindingIsTiny (w) ) { // the brush isn't really split int side; side = BrushMostlyOnSide (brush, plane); if (side == PSIDE_FRONT) *front = CopyBrush (brush); if (side == PSIDE_BACK) *back = CopyBrush (brush); return; } if (WindingIsHuge (w)) { qprintf ("WARNING: huge winding\n"); } TranslateWinding( w, -offset ); midwinding = w; // // // split it for real // // // // allocate two new brushes referencing the original // for( i = 0; i < 2; i++ ) { b[i] = AllocBrush( brush->numsides + 1 ); b[i]->original = brush->original; } // // split all the current windings // for( i = 0; i < brush->numsides; i++ ) { // get the current side s = &brush->sides[i]; // get the sides winding w = s->winding; if( !w ) continue; // clip the winding ClipWindingEpsilon_Offset( w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1], offset ); for( j = 0; j < 2; j++ ) { // does winding exist? if( !cw[j] ) continue; #if 0 if (WindingIsTiny (cw[j])) { FreeWinding (cw[j]); continue; } #endif // // create a clipped "side" with the new winding // cs = &b[j]->sides[b[j]->numsides]; b[j]->numsides++; *cs = *s; cs->winding = cw[j]; cs->tested = false; // save the original side information //cs->original = s->original; } } // see if we have valid polygons on both sides for (i=0 ; i<2 ; i++) { BoundBrush (b[i]); for (j=0 ; j<3 ; j++) { if (b[i]->mins[j] < MIN_COORD_INTEGER || b[i]->maxs[j] > MAX_COORD_INTEGER) { qprintf ("bogus brush after clip\n"); break; } } if (b[i]->numsides < 3 || j < 3) { FreeBrush (b[i]); b[i] = NULL; } } if ( !(b[0] && b[1]) ) { if (!b[0] && !b[1]) qprintf ("split removed brush\n"); else qprintf ("split not on both sides\n"); if (b[0]) { FreeBrush (b[0]); *front = CopyBrush (brush); } if (b[1]) { FreeBrush (b[1]); *back = CopyBrush (brush); } return; } // add the midwinding to both sides for (i=0 ; i<2 ; i++) { cs = &b[i]->sides[b[i]->numsides]; b[i]->numsides++; cs->planenum = planenum^i^1; cs->texinfo = TEXINFO_NODE; // initialize the displacement map index cs->pMapDisp = NULL; cs->visible = false; cs->tested = false; if (i==0) cs->winding = CopyWinding (midwinding); else cs->winding = midwinding; } { vec_t v1; int i; for (i=0 ; i<2 ; i++) { v1 = BrushVolume (b[i]); if (v1 < 1.0) { FreeBrush (b[i]); b[i] = NULL; // qprintf ("tiny volume after clip\n"); } } } *front = b[0]; *back = b[1]; }