/* ================== CreateBrushWindings ================== */ void CreateBrushWindings (bspbrush_t *brush) { int i, j; winding_t *w; side_t *side; plane_t *plane; // translate the CSG problem to improve precision Vector insidePoint = PointInsideBrush( brush ); Vector offset = -insidePoint; for (i=0 ; i<brush->numsides ; i++) { side = &brush->sides[i]; plane = &g_MainMap->mapplanes[side->planenum]; w = BaseWindingForPlane (plane->normal, plane->dist + DotProduct(plane->normal, offset)); for (j=0 ; j<brush->numsides && w; j++) { if (i == j) continue; if (brush->sides[j].bevel) continue; plane = &g_MainMap->mapplanes[brush->sides[j].planenum^1]; ChopWindingInPlace (&w, plane->normal, plane->dist + DotProduct(plane->normal, offset), 0); //CLIP_EPSILON); } TranslateWinding( w, -offset ); side->winding = w; } BoundBrush (brush); }
/** * @brief makes basewindigs for sides and mins/maxs for the brush * @returns false if the brush doesn't enclose a valid volume */ static void CreateBrushWindings (bspbrush_t* brush) { int i; for (i = 0; i < brush->numsides; i++) { side_t* side = &brush->sides[i]; const plane_t* plane = &mapplanes[side->planenum]; int j; /* evidence that winding_t represents a hessian normal plane */ winding_t* w = BaseWindingForPlane(plane->normal, plane->dist); for (j = 0; j < brush->numsides && w; j++) { if (i == j) continue; /* back side clipaway */ if (brush->sides[j].planenum == (side->planenum ^ 1)) continue; if (brush->sides[j].bevel) continue; plane = &mapplanes[brush->sides[j].planenum ^ 1]; ChopWindingInPlace(&w, plane->normal, plane->dist, 0); /*CLIP_EPSILON); */ /* fix broken windings that would generate trifans */ if (!FixWinding(w)) Verb_Printf(VERB_EXTRA, "removed degenerated edge(s) from winding\n"); } side->winding = w; } BoundBrush(brush); }
/* ================== CreateBrushWindings ================== */ void CreateBrushWindings (bspbrush_t *brush) { int i, j; winding_t *w; side_t *side; plane_t *plane; for (i=0 ; i<brush->numsides ; i++) { side = &brush->sides[i]; plane = &mapplanes[side->planenum]; w = BaseWindingForPlane (plane->normal, plane->dist); for (j=0 ; j<brush->numsides && w; j++) { if (i == j) continue; if (brush->sides[j].bevel) continue; plane = &mapplanes[brush->sides[j].planenum^1]; ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON); } side->winding = w; } BoundBrush (brush); }
/* ================== CreateBrushWindings makes basewindigs for sides and mins / maxs for the brush returns false if the brush doesn't enclose a valid volume ================== */ bool CreateBrushWindings (uBrush_t *brush) { int i, j; idWinding *w; idPlane *plane; side_t *side; for ( i = 0; i < brush->numsides; i++ ) { side = &brush->sides[i]; plane = &dmapGlobals.mapPlanes[side->planenum]; w = new idWinding( *plane ); for ( j = 0; j < brush->numsides && w; j++ ) { if ( i == j ) { continue; } if ( brush->sides[j].planenum == ( brush->sides[i].planenum ^ 1 ) ) { continue; // back side clipaway } plane = &dmapGlobals.mapPlanes[brush->sides[j].planenum^1]; w = w->Clip( *plane, 0 );//CLIP_EPSILON); } if ( side->winding ) { delete side->winding; } side->winding = w; } return BoundBrush( brush ); }
/* ============ idBrush::FromWinding ============ */ bool idBrush::FromWinding( const idWinding& w, const idPlane& windingPlane ) { int i, j, bestAxis; idPlane plane; idVec3 normal, axialNormal; sides.Append( new idBrushSide( windingPlane, -1 ) ); sides.Append( new idBrushSide( -windingPlane, -1 ) ); bestAxis = 0; for( i = 1; i < 3; i++ ) { if( idMath::Fabs( windingPlane.Normal()[i] ) > idMath::Fabs( windingPlane.Normal()[bestAxis] ) ) { bestAxis = i; } } axialNormal = vec3_origin; if( windingPlane.Normal()[bestAxis] > 0.0f ) { axialNormal[bestAxis] = 1.0f; } else { axialNormal[bestAxis] = -1.0f; } for( i = 0; i < w.GetNumPoints(); i++ ) { j = ( i + 1 ) % w.GetNumPoints(); normal = ( w[j].ToVec3() - w[i].ToVec3() ).Cross( axialNormal ); if( normal.Normalize() < 0.5f ) { continue; } plane.SetNormal( normal ); plane.FitThroughPoint( w[j].ToVec3() ); sides.Append( new idBrushSide( plane, -1 ) ); } if( sides.Num() < 4 ) { for( i = 0; i < sides.Num(); i++ ) { delete sides[i]; } sides.Clear(); return false; } sides[0]->winding = w.Copy(); windingsValid = true; BoundBrush(); return true; }
qboolean CreateBrushWindings( brush_t *brush ) { dwinding_t *dw; side_t *side; plane_t *plane; int i, j; /* walk the list of brush sides */ for( i = 0; i < brush->numsides; i++ ) { /* get side and plane */ side = &brush->sides[ i ]; plane = &mapplanes[ side->planenum ]; /* make huge winding (double precision) */ dw = BaseDWindingForPlane( plane->normal, plane->dist, MAX_WORLD_COORD ); /* walk the list of brush sides */ for( j = 0; j < brush->numsides && dw != NULL; j++ ) { if( i == j ) continue; if( brush->sides[ j ].planenum == (brush->sides[ i ].planenum ^ 1) ) continue; /* back side clipaway */ if( brush->sides[ j ].bevel ) continue; plane = &mapplanes[ brush->sides[ j ].planenum ^ 1 ]; ChopDWindingInPlace( &dw, plane->normal, plane->dist, 0 ); /* ydnar: fix broken windings that would generate trifans */ FixDWinding( dw ); } /* set side winding */ side->winding = 0; if ( dw != NULL ) { side->winding = WindingFromDWinding( dw ); FreeDWinding( dw ); } } /* find brush bounds */ return BoundBrush( brush ); }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void CheckBSPBrush(bspbrush_t *brush) { int i, j; plane_t *plane1, *plane2; //check if the brush is convex... flipped planes make a brush non-convex for (i = 0; i < brush->numsides; i++) { for (j = 0; j < brush->numsides; j++) { if (i == j) continue; plane1 = &mapplanes[brush->sides[i].planenum]; plane2 = &mapplanes[brush->sides[j].planenum]; // if (WindingsNonConvex(brush->sides[i].winding, brush->sides[j].winding, plane1->normal, plane2->normal, plane1->dist, plane2->dist)) { Log_Print("non convex brush"); break; } //end if } //end for } //end for BoundBrush(brush); //check for out of bound brushes for (i = 0; i < 3; i++) { if (brush->mins[i] < -MAX_MAP_BOUNDS || brush->maxs[i] > MAX_MAP_BOUNDS) { Log_Print("brush: bounds out of range\n"); Log_Print("ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, brush->mins[i], i, brush->maxs[i]); break; } //end if if (brush->mins[i] > MAX_MAP_BOUNDS || brush->maxs[i] < -MAX_MAP_BOUNDS) { Log_Print("brush: no visible sides on brush\n"); Log_Print("ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, brush->mins[i], i, brush->maxs[i]); break; } //end if } //end for } //end of the function CheckBSPBrush
/* ================== CreateBrushWindings makes basewindigs for sides and mins / maxs for the brush returns false if the brush doesn't enclose a valid volume ================== */ qboolean CreateBrushWindings(bspBrush_t * brush) { int i, j; winding_t *w; side_t *side; plane_t *plane; for(i = 0; i < brush->numsides; i++) { side = &brush->sides[i]; // don't create a winding for a bevel if(side->bevel) { continue; } plane = &mapPlanes[side->planenum]; w = BaseWindingForPlane(plane->normal, plane->dist); for(j = 0; j < brush->numsides && w; j++) { if(i == j) continue; if(brush->sides[j].planenum == (brush->sides[i].planenum ^ 1)) continue; // back side clipaway if(brush->sides[j].bevel) continue; if(brush->sides[j].backSide) continue; plane = &mapPlanes[brush->sides[j].planenum ^ 1]; ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON); } // free any existing winding if(side->winding) { FreeWinding(side->winding); } side->winding = w; } return BoundBrush(brush); }
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]; }
void SplitBrush( brush_t *brush, int planenum, brush_t **front, brush_t **back ){ brush_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 = NULL; *back = NULL; plane = &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; } // create a new winding from the split plane w = BaseWindingForPlane( plane->normal, plane->dist ); for ( i = 0 ; i < brush->numsides && w ; i++ ) { plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; ChopWindingInPlace( &w, plane2->normal, plane2->dist, 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 ) ) { Sys_FPrintf( SYS_VRB,"WARNING: huge winding\n" ); } midwinding = w; // split it for real for ( i = 0 ; i < 2 ; i++ ) { b[i] = AllocBrush( brush->numsides + 1 ); memcpy( b[i], brush, sizeof( brush_t ) - sizeof( brush->sides ) ); b[i]->numsides = 0; b[i]->next = NULL; b[i]->original = brush->original; } // split all the current windings for ( i = 0 ; i < brush->numsides ; i++ ) { s = &brush->sides[i]; w = s->winding; if ( !w ) { continue; } /* strict, in parallel case we get the face back because it also is the midwinding */ ClipWindingEpsilonStrict( w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1] ); for ( j = 0 ; j < 2 ; j++ ) { if ( !cw[j] ) { continue; } cs = &b[j]->sides[b[j]->numsides]; b[j]->numsides++; *cs = *s; cs->winding = cw[j]; } } // see if we have valid polygons on both sides for ( i = 0 ; i < 2 ; i++ ) { if ( b[i]->numsides < 3 || !BoundBrush( b[i] ) ) { if ( b[i]->numsides >= 3 ) { Sys_FPrintf( SYS_VRB,"bogus brush after clip\n" ); } FreeBrush( b[i] ); b[i] = NULL; } } if ( !( b[0] && b[1] ) ) { if ( !b[0] && !b[1] ) { Sys_FPrintf( SYS_VRB,"split removed brush\n" ); } else{ Sys_FPrintf( SYS_VRB,"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->shaderInfo = NULL; 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; // Sys_FPrintf (SYS_VRB,"tiny volume after clip\n"); } } } *front = b[0]; *back = b[1]; }
qboolean CreateBrushWindings( brush_t *brush ){ int i, j; #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES winding_accu_t *w; #else winding_t *w; #endif side_t *side; plane_t *plane; /* walk the list of brush sides */ for ( i = 0; i < brush->numsides; i++ ) { /* get side and plane */ side = &brush->sides[ i ]; plane = &mapplanes[ side->planenum ]; /* make huge winding */ #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES w = BaseWindingForPlaneAccu( plane->normal, plane->dist ); #else w = BaseWindingForPlane( plane->normal, plane->dist ); #endif /* walk the list of brush sides */ for ( j = 0; j < brush->numsides && w != NULL; j++ ) { if ( i == j ) { continue; } if ( brush->sides[ j ].planenum == ( brush->sides[ i ].planenum ^ 1 ) ) { continue; /* back side clipaway */ } if ( brush->sides[ j ].bevel ) { continue; } plane = &mapplanes[ brush->sides[ j ].planenum ^ 1 ]; #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES ChopWindingInPlaceAccu( &w, plane->normal, plane->dist, 0 ); #else ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); // CLIP_EPSILON ); #endif /* ydnar: fix broken windings that would generate trifans */ #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES // I think it's better to FixWindingAccu() once after we chop with all planes // so that error isn't multiplied. There is nothing natural about welding // the points unless they are the final endpoints. ChopWindingInPlaceAccu() // is able to handle all kinds of degenerate windings. #else FixWinding( w ); #endif } /* set side winding */ #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES if ( w != NULL ) { FixWindingAccu( w ); if ( w->numpoints < 3 ) { FreeWindingAccu( w ); w = NULL; } } side->winding = ( w ? CopyWindingAccuToRegular( w ) : NULL ); if ( w ) { FreeWindingAccu( w ); } #else side->winding = w; #endif } /* find brush bounds */ return BoundBrush( brush ); }
/* ================ SplitBrush Generates two new brushes, leaving the original unchanged ================ */ 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 = &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; } // create a new winding from the split plane w = BaseWindingForPlane (plane->normal, plane->dist); for (i=0 ; i<brush->numsides && w ; i++) { plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; ChopWindingInPlace (&w, plane2->normal, plane2->dist, 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"); } midwinding = w; // split it for real 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++) { s = &brush->sides[i]; w = s->winding; if (!w) continue; ClipWindingEpsilon (w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); for (j=0 ; j<2 ; j++) { if (!cw[j]) continue; #if 0 if (WindingIsTiny (cw[j])) { FreeWinding (cw[j]); continue; } #endif cs = &b[j]->sides[b[j]->numsides]; b[j]->numsides++; *cs = *s; // cs->planenum = s->planenum; // cs->texinfo = s->texinfo; // cs->visible = s->visible; // cs->original = s->original; cs->winding = cw[j]; cs->tested = false; } } // 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] < -4096 || b[i]->maxs[j] > 4096) { 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; 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]; }
/* ================ SplitBrush Generates two new brushes, leaving the original unchanged ================ */ void SplitBrush( uBrush_t *brush, int planenum, uBrush_t **front, uBrush_t **back ) { uBrush_t *b[2]; int i, j; idWinding *w, *cw[2], *midwinding; side_t *s, *cs; float d, d_front, d_back; *front = *back = NULL; idPlane &plane = dmapGlobals.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->GetNumPoints(); j++ ) { d = plane.Distance( ( *w ) [j].ToVec3() ); 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; } // create a new winding from the split plane w = new idWinding( plane ); for( i = 0; i < brush->numsides && w; i++ ) { idPlane &plane2 = dmapGlobals.mapPlanes[brush->sides[i].planenum ^ 1]; w = w->Clip( plane2, 0 ); // PLANESIDE_EPSILON); } if( !w || w->IsTiny() ) { // 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( w->IsHuge() ) { common->Printf( "WARNING: huge winding\n" ); } midwinding = w; // split it for real for( i = 0; i < 2; i++ ) { b[i] = AllocBrush( brush->numsides + 1 ); memcpy( b[i], brush, sizeof( uBrush_t ) - sizeof( brush->sides ) ); b[i]->numsides = 0; b[i]->next = NULL; b[i]->original = brush->original; } // split all the current windings for( i = 0; i < brush->numsides; i++ ) { s = &brush->sides[i]; w = s->winding; if( !w ) { continue; } w->Split( plane, 0, &cw[0], &cw[1] ); for( j = 0; j < 2; j++ ) { if( !cw[j] ) { continue; } cs = &b[j]->sides[b[j]->numsides]; b[j]->numsides++; *cs = *s; cs->winding = cw[j]; } } // see if we have valid polygons on both sides for( i = 0; i < 2; i++ ) { if( !BoundBrush( b[i] ) ) { break; } if( b[i]->numsides < 3 ) { FreeBrush( b[i] ); b[i] = NULL; } } if( !( b[0] && b[1] ) ) { if( !b[0] && !b[1] ) { common->Printf( "split removed brush\n" ); } else { common->Printf( "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->material = NULL; if( i == 0 ) { cs->winding = midwinding->Copy(); } else { cs->winding = midwinding; } } for( i = 0; i < 2; i++ ) { float v1 = BrushVolume( b[i] ); if( v1 < 1.0 ) { FreeBrush( b[i] ); b[i] = NULL; //common->Printf ("tiny volume after clip\n"); } } *front = b[0]; *back = b[1]; }
/* ============ idBrush::TryMerge ============ */ bool idBrush::TryMerge( const idBrush* brush, const idPlaneSet& planeList ) { int i, j, k, l, m, seperatingPlane; const idBrush* brushes[2]; const idWinding* w; const idPlane* plane; // brush bounds should overlap for( i = 0; i < 3; i++ ) { if( bounds[0][i] > brush->bounds[1][i] + 0.1f ) { return false; } if( bounds[1][i] < brush->bounds[0][i] - 0.1f ) { return false; } } // the brushes should share an opposite plane seperatingPlane = -1; for( i = 0; i < GetNumSides(); i++ ) { for( j = 0; j < brush->GetNumSides(); j++ ) { if( GetSide( i )->GetPlaneNum() == ( brush->GetSide( j )->GetPlaneNum() ^ 1 ) ) { // may only have one seperating plane if( seperatingPlane != -1 ) { return false; } seperatingPlane = GetSide( i )->GetPlaneNum(); break; } } } if( seperatingPlane == -1 ) { return false; } brushes[0] = this; brushes[1] = brush; for( i = 0; i < 2; i++ ) { j = !i; for( k = 0; k < brushes[i]->GetNumSides(); k++ ) { // if the brush side plane is the seprating plane if( !( ( brushes[i]->GetSide( k )->GetPlaneNum() ^ seperatingPlane ) >> 1 ) ) { continue; } plane = &brushes[i]->GetSide( k )->GetPlane(); // all the non seperating brush sides of the other brush should be at the back or on the plane for( l = 0; l < brushes[j]->GetNumSides(); l++ ) { w = brushes[j]->GetSide( l )->GetWinding(); if( !w ) { continue; } if( !( ( brushes[j]->GetSide( l )->GetPlaneNum() ^ seperatingPlane ) >> 1 ) ) { continue; } for( m = 0; m < w->GetNumPoints(); m++ ) { if( plane->Distance( ( *w )[m].ToVec3() ) > 0.1f ) { return false; } } } } } // add any sides from the other brush to this brush for( i = 0; i < brush->GetNumSides(); i++ ) { for( j = 0; j < GetNumSides(); j++ ) { if( !( ( brush->GetSide( i )->GetPlaneNum() ^ GetSide( j )->GetPlaneNum() ) >> 1 ) ) { break; } } if( j < GetNumSides() ) { sides[j]->flags &= brush->GetSide( i )->GetFlags(); continue; } sides.Append( brush->GetSide( i )->Copy() ); } // remove any side from this brush that is the opposite of a side of the other brush for( i = 0; i < GetNumSides(); i++ ) { for( j = 0; j < brush->GetNumSides(); j++ ) { if( GetSide( i )->GetPlaneNum() == ( brush->GetSide( j )->GetPlaneNum() ^ 1 ) ) { break; } } if( j < brush->GetNumSides() ) { delete sides[i]; sides.RemoveIndex( i ); i--; continue; } } contents |= brush->contents; CreateWindings(); BoundBrush(); return true; }
/** * @brief Generates two new brushes, leaving the original unchanged */ void SplitBrush(brush_t *brush, int32_t plane_num, brush_t **front, brush_t **back) { brush_t *b[2]; int32_t i, j; winding_t *w, *cw[2], *midwinding; map_plane_t *plane, *plane2; side_t *s, *cs; vec_t d, d_front, d_back; *front = *back = NULL; plane = &map_planes[plane_num]; // check all points d_front = d_back = 0; for (i = 0; i < brush->num_sides; i++) { w = brush->sides[i].winding; if (!w) { continue; } for (j = 0; j < w->num_points; j++) { d = DotProduct(w->points[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; } // create a new winding from the split plane w = WindingForPlane(plane->normal, plane->dist); for (i = 0; i < brush->num_sides && w; i++) { plane2 = &map_planes[brush->sides[i].plane_num ^ 1]; ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON); } if (!w || WindingIsTiny(w)) { // the brush isn't really split int32_t side; side = BrushMostlyOnSide(brush, plane); if (side == SIDE_FRONT) { *front = CopyBrush(brush); } if (side == SIDE_BACK) { *back = CopyBrush(brush); } return; } if (WindingIsHuge(w)) { Mon_SendWinding(ERROR_WARN, (const vec3_t *) w->points, w->num_points, "Large winding"); } midwinding = w; // split it for real for (i = 0; i < 2; i++) { b[i] = AllocBrush(brush->num_sides + 1); b[i]->original = brush->original; } // split all the current windings for (i = 0; i < brush->num_sides; i++) { s = &brush->sides[i]; w = s->winding; if (!w) { continue; } ClipWindingEpsilon(w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON */, &cw[0], &cw[1]); for (j = 0; j < 2; j++) { if (!cw[j]) { continue; } cs = &b[j]->sides[b[j]->num_sides]; b[j]->num_sides++; *cs = *s; cs->winding = cw[j]; cs->tested = false; } } // 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_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD) { Com_Debug(DEBUG_ALL, "bogus brush after clip\n"); break; } } if (b[i]->num_sides < 3 || j < 3) { FreeBrush(b[i]); b[i] = NULL; } } if (!(b[0] && b[1])) { if (!b[0] && !b[1]) { Com_Debug(DEBUG_ALL, "split removed brush\n"); } else { Com_Debug(DEBUG_ALL, "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]->num_sides]; b[i]->num_sides++; cs->plane_num = plane_num ^ i ^ 1; cs->texinfo = TEXINFO_NODE; cs->visible = false; cs->tested = false; if (i == 0) { cs->winding = CopyWinding(midwinding); } else { cs->winding = midwinding; } } { vec_t v1; for (i = 0; i < 2; i++) { v1 = BrushVolume(b[i]); if (v1 < 1.0) { FreeBrush(b[i]); b[i] = NULL; Com_Debug(DEBUG_ALL, "tiny volume after clip\n"); } } } *front = b[0]; *back = b[1]; }
//=========================================================================== // NOTE: can't keep brush->original intact // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bspbrush_t *TryMergeBrushes(bspbrush_t *brush1, bspbrush_t *brush2) { int i, j, k, n, shared; side_t *side1, *side2, *cs; plane_t *plane1, *plane2; bspbrush_t *newbrush; //check for bounding box overlapp for (i = 0; i < 3; i++) { if (brush1->mins[i] > brush2->maxs[i] + 2 || brush1->maxs[i] < brush2->mins[i] - 2) { return NULL; } //end if } //end for // shared = 0; //check if the brush is convex... flipped planes make a brush non-convex for (i = 0; i < brush1->numsides; i++) { side1 = &brush1->sides[i]; //don't check the "shared" sides for (k = 0; k < brush2->numsides; k++) { side2 = &brush2->sides[k]; if (side1->planenum == (side2->planenum^1)) { shared++; //there may only be ONE shared side if (shared > 1) return NULL; break; } //end if } //end for if (k < brush2->numsides) continue; // for (j = 0; j < brush2->numsides; j++) { side2 = &brush2->sides[j]; //don't check the "shared" sides for (n = 0; n < brush1->numsides; n++) { side1 = &brush1->sides[n]; if (side1->planenum == (side2->planenum^1)) break; } //end for if (n < brush1->numsides) continue; // side1 = &brush1->sides[i]; //if the side is in the same plane //* if (side1->planenum == side2->planenum) { if (side1->texinfo != TEXINFO_NODE && side2->texinfo != TEXINFO_NODE && side1->texinfo != side2->texinfo) return NULL; continue; } //end if // plane1 = &mapplanes[side1->planenum]; plane2 = &mapplanes[side2->planenum]; // if (WindingsNonConvex(side1->winding, side2->winding, plane1->normal, plane2->normal, plane1->dist, plane2->dist)) { return NULL; } //end if } //end for } //end for newbrush = AllocBrush(brush1->numsides + brush2->numsides); newbrush->original = brush1->original; newbrush->numsides = 0; //newbrush->side = brush1->side; //brush contents //fix texinfos for sides lying in the same plane for (i = 0; i < brush1->numsides; i++) { side1 = &brush1->sides[i]; // for (n = 0; n < brush2->numsides; n++) { side2 = &brush2->sides[n]; //if both sides are in the same plane get the texinfo right if (side1->planenum == side2->planenum) { if (side1->texinfo == TEXINFO_NODE) side1->texinfo = side2->texinfo; if (side2->texinfo == TEXINFO_NODE) side2->texinfo = side1->texinfo; } //end if } //end for } //end for // for (i = 0; i < brush1->numsides; i++) { side1 = &brush1->sides[i]; //don't add the "shared" sides for (n = 0; n < brush2->numsides; n++) { side2 = &brush2->sides[n]; if (side1->planenum == (side2->planenum ^ 1)) break; } //end for if (n < brush2->numsides) continue; // for (n = 0; n < newbrush->numsides; n++) { cs = &newbrush->sides[n]; if (cs->planenum == side1->planenum) { Log_Print("brush duplicate plane\n"); break; } //end if } //end if if (n < newbrush->numsides) continue; //add this side cs = &newbrush->sides[newbrush->numsides]; newbrush->numsides++; *cs = *side1; } //end for for (j = 0; j < brush2->numsides; j++) { side2 = &brush2->sides[j]; for (n = 0; n < brush1->numsides; n++) { side1 = &brush1->sides[n]; //if the side is in the same plane if (side2->planenum == side1->planenum) break; //don't add the "shared" sides if (side2->planenum == (side1->planenum ^ 1)) break; } //end for if (n < brush1->numsides) continue; // for (n = 0; n < newbrush->numsides; n++) { cs = &newbrush->sides[n]; if (cs->planenum == side2->planenum) { Log_Print("brush duplicate plane\n"); break; } //end if } //end if if (n < newbrush->numsides) continue; //add this side cs = &newbrush->sides[newbrush->numsides]; newbrush->numsides++; *cs = *side2; } //end for BSPBrushWindings(newbrush); BoundBrush(newbrush); CheckBSPBrush(newbrush); return newbrush; } //end of the function TryMergeBrushes
/** * @brief Generates two new brushes, leaving the original unchanged */ void SplitBrush (const bspbrush_t* brush, uint16_t planenum, bspbrush_t** front, bspbrush_t** back) { bspbrush_t* b[2]; int i, j; winding_t* w, *cw[2], *midwinding; plane_t* plane; float d_front, d_back; *front = *back = nullptr; plane = &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++) { const float d = DotProduct(w->p[j], plane->normal) - plane->dist; if (d > 0 && d > d_front) d_front = d; else 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; } /* create a new winding from the split plane */ w = BaseWindingForPlane(plane->normal, plane->dist); for (i = 0; i < brush->numsides && w; i++) { plane_t* plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); /* PLANESIDE_EPSILON); */ } /* the brush isn't really split */ if (!w || WindingIsTiny(w)) { const int side = BrushMostlyOnSide(brush, plane); if (side == PSIDE_FRONT) *front = CopyBrush(brush); else if (side == PSIDE_BACK) *back = CopyBrush(brush); return; } if (WindingIsHuge(w)) { /** @todo Print brush and entnum either of the brush that was splitted * or the plane that was used as splitplane */ Com_Printf("WARNING: Large winding\n"); } midwinding = w; /* split it for real */ 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++) { const side_t* s = &brush->sides[i]; w = s->winding; if (!w) continue; ClipWindingEpsilon(w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); for (j = 0; j < 2; j++) { side_t* cs; if (!cw[j]) continue; cs = &b[j]->sides[b[j]->numsides]; b[j]->numsides++; *cs = *s; cs->winding = cw[j]; cs->tested = false; } } /* 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] < -MAX_WORLD_WIDTH || b[i]->maxs[j] > MAX_WORLD_WIDTH) { /** @todo Print brush and entnum either of the brush that was split * or the plane that was used as splitplane */ Verb_Printf(VERB_EXTRA, "bogus brush after clip\n"); break; } } if (b[i]->numsides < 3 || j < 3) { FreeBrush(b[i]); b[i] = nullptr; } } if (!(b[0] && b[1])) { /** @todo Print brush and entnum either of the brush that was splitted * or the plane that was used as splitplane */ if (!b[0] && !b[1]) Verb_Printf(VERB_EXTRA, "split removed brush\n"); else Verb_Printf(VERB_EXTRA, "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++) { side_t* cs = &b[i]->sides[b[i]->numsides]; b[i]->numsides++; cs->planenum = planenum ^ i ^ 1; cs->texinfo = TEXINFO_NODE; cs->visible = false; cs->tested = false; if (i == 0) cs->winding = CopyWinding(midwinding); else cs->winding = midwinding; } for (i = 0; i < 2; i++) { const vec_t v1 = BrushVolume(b[i]); if (v1 < 1.0) { FreeBrush(b[i]); b[i] = nullptr; /** @todo Print brush and entnum either of the brush that was splitted * or the plane that was used as splitplane */ Verb_Printf(VERB_EXTRA, "tiny volume after clip\n"); } } *front = b[0]; *back = b[1]; }
//=========================================================================== // Generates two new brushes, leaving the original // unchanged // // modified for Half-Life because there are quite a lot of tiny node leaves // in the Half-Life bsps // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Q1_SplitBrush(bspbrush_t *brush, int planenum, int nodenum, 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 = &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; } //end for } //end for if (d_front < 0.1) // PLANESIDE_EPSILON) { // only on back *back = CopyBrush (brush); Log_Print("Q1_SplitBrush: only on back\n"); return; } //end if if (d_back > -0.1) // PLANESIDE_EPSILON) { // only on front *front = CopyBrush (brush); Log_Print("Q1_SplitBrush: only on front\n"); return; } //end if // create a new winding from the split plane w = BaseWindingForPlane (plane->normal, plane->dist); for (i = 0; i < brush->numsides && w; i++) { plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON); } //end for if (!w || WindingIsTiny(w)) { // the brush isn't really split int side; Log_Print("Q1_SplitBrush: no split winding\n"); side = BrushMostlyOnSide (brush, plane); if (side == PSIDE_FRONT) *front = CopyBrush (brush); if (side == PSIDE_BACK) *back = CopyBrush (brush); return; } if (WindingIsHuge(w)) { Log_Print("Q1_SplitBrush: WARNING huge split winding\n"); } //end of midwinding = w; // split it for real for (i = 0; i < 2; i++) { b[i] = AllocBrush (brush->numsides+1); b[i]->original = brush->original; } //end for // split all the current windings for (i=0 ; i<brush->numsides ; i++) { s = &brush->sides[i]; w = s->winding; if (!w) continue; ClipWindingEpsilon (w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); for (j=0 ; j<2 ; j++) { if (!cw[j]) continue; #if 0 if (WindingIsTiny (cw[j])) { FreeWinding (cw[j]); continue; } #endif cs = &b[j]->sides[b[j]->numsides]; b[j]->numsides++; *cs = *s; // cs->planenum = s->planenum; // cs->texinfo = s->texinfo; // cs->visible = s->visible; // cs->original = s->original; cs->winding = cw[j]; cs->flags &= ~SFL_TESTED; } //end for } //end for // 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] < -4096 || b[i]->maxs[j] > 4096) { Log_Print("Q1_SplitBrush: bogus brush after clip\n"); break; } //end if } //end for if (b[i]->numsides < 3 || j < 3) { FreeBrush (b[i]); b[i] = NULL; Log_Print("Q1_SplitBrush: numsides < 3\n"); } //end if } //end for if ( !(b[0] && b[1]) ) { if (!b[0] && !b[1]) Log_Print("Q1_SplitBrush: split removed brush\n"); else Log_Print("Q1_SplitBrush: split not on both sides\n"); if (b[0]) { FreeBrush (b[0]); *front = CopyBrush (brush); } //end if if (b[1]) { FreeBrush (b[1]); *back = CopyBrush (brush); } //end if return; } //end if // 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 = 0; //store the node number in the surf to find the texinfo later on cs->surf = nodenum; // cs->flags &= ~SFL_VISIBLE; cs->flags &= ~SFL_TESTED; cs->flags &= ~SFL_TEXTURED; if (i==0) cs->winding = CopyWinding (midwinding); else cs->winding = midwinding; } //end for { vec_t v1; int i; for (i=0 ; i<2 ; i++) { v1 = BrushVolume (b[i]); if (v1 < 1) { FreeBrush (b[i]); b[i] = NULL; Log_Print("Q1_SplitBrush: tiny volume after clip\n"); } //end if } //end for } //*/ *front = b[0]; *back = b[1]; } //end of the function Q1_SplitBrush