/* ================ 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]; }
//=========================================================================== // 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
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_WRN | SYS_VRBflag, "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; } ClipWindingEpsilonStrict( w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1] ); /* strict, in parallel case we get the face back because it also is the midwinding */ 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_WRN | SYS_VRBflag, "bogus brush after clip\n" ); } FreeBrush( b[i] ); b[i] = NULL; } } if ( !( b[0] && b[1] ) ) { if ( !b[0] && !b[1] ) { Sys_FPrintf( SYS_WRN | SYS_VRBflag, "split removed brush\n" ); } else{ Sys_FPrintf( SYS_WRN | SYS_VRBflag, "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_WRN | SYS_VRBflag, "tiny volume after clip\n" ); } } } *front = b[0]; *back = b[1]; }
void ProcessSubModel( void ) { entity_t *e; tree_t *tree; brush_t *b, *bc; node_t *node; /* start a brush model */ BeginModel(); e = &entities[ mapEntityNum ]; e->firstDrawSurf = numMapDrawSurfs; /* ydnar: gs mods */ ClearMetaTriangles(); /* check for patches with adjacent edges that need to lod together */ PatchMapDrawSurfs( e ); /* allocate a tree */ node = AllocNode(); node->planenum = PLANENUM_LEAF; tree = AllocTree(); tree->headnode = node; /* add the sides to the tree */ ClipSidesIntoTree( e, tree ); /* ydnar: create drawsurfs for triangle models */ AddTriangleModels( e ); /* create drawsurfs for surface models */ AddEntitySurfaceModels( e ); /* generate bsp brushes from map brushes */ EmitBrushes( e->brushes, &e->firstBrush, &e->numBrushes ); /* just put all the brushes in headnode */ for( b = e->brushes; b; b = b->next ) { bc = CopyBrush( b ); bc->next = node->brushlist; node->brushlist = bc; } /* subdivide each drawsurf as required by shader tesselation */ if( !nosubdivide ) SubdivideFaceSurfaces( e, tree ); /* add in any vertexes required to fix t-junctions */ if( !notjunc ) FixTJunctions( e ); /* ydnar: classify the surfaces and project lightmaps */ ClassifyEntitySurfaces( e ); /* ydnar: project decals */ MakeEntityDecals( e ); /* ydnar: meta surfaces */ MakeEntityMetaTriangles( e ); SmoothMetaTriangles(); FixMetaTJunctions(); MergeMetaTriangles(); /* add references to the final drawsurfs in the apropriate clusters */ FilterDrawsurfsIntoTree( e, tree ); /* match drawsurfaces back to original brushsides (sof2) */ FixBrushSides( e ); /* finish */ EndModel( e, node ); FreeTree( tree ); }
/** * @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]; }
/* ================= FinishBrush produces a final brush based on the buildBrush->sides array and links it to the current entity ================= */ brush_t *FinishBrush( void ) { brush_t *b; // create windings for sides and bounds for brush if( !CreateBrushWindings( buildBrush )) return NULL; // origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity. // after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush if( buildBrush->compileFlags & C_ORIGIN ) { char string[32]; vec3_t size, movedir, origin; if( numEntities == 1 ) { Msg( "Entity %i, Brush %i: origin brushes not allowed in world\n", mapEnt->mapEntityNum, entitySourceBrushes ); return NULL; } // calcualte movedir (Xash 0.4 style) VectorAverage( buildBrush->mins, buildBrush->maxs, origin ); VectorSubtract( buildBrush->maxs, buildBrush->mins, size ); if( size[2] > size[0] && size[2] > size[1] ) VectorSet( movedir, 0.0f, 1.0f, 0.0f ); // x-rotate else if( size[1] > size[2] && size[1] > size[0] ) VectorSet( movedir, 1.0f, 0.0f, 0.0f ); // y-rotate else if( size[0] > size[2] && size[0] > size[1] ) VectorSet( movedir, 0.0f, 0.0f, 1.0f ); // z-rotate else VectorClear( movedir ); // custom movedir #if 0 if( !VectorIsNull( movedir )) { com.snprintf( string, sizeof( string ), "%i %i %i", (int)movedir[0], (int)movedir[1], (int)movedir[2] ); SetKeyValue( &entities[numEntities - 1], "movedir", string ); } #endif if(!VectorIsNull( origin )) { com.snprintf( string, sizeof( string ), "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2] ); SetKeyValue( &entities[numEntities - 1], "origin", string ); VectorCopy( origin, entities[numEntities - 1].origin ); } // don't keep this brush return NULL; } // determine if the brush is an area portal if( buildBrush->compileFlags & C_AREAPORTAL ) { if( numEntities != 1 ) { Msg( "Entity %i, Brush %i: areaportals only allowed in world\n", mapEnt->mapEntityNum, entitySourceBrushes ); return NULL; } } AddBrushBevels(); b = CopyBrush( buildBrush ); b->entityNum = mapEnt->mapEntityNum; b->brushNum = entitySourceBrushes; b->original = b; // link opaque brushes to head of list, translucent brushes to end if( b->opaque || mapEnt->lastBrush == NULL ) { b->next = mapEnt->brushes; mapEnt->brushes = b; if( mapEnt->lastBrush == NULL ) mapEnt->lastBrush = b; } else { b->next = NULL; mapEnt->lastBrush->next = b; mapEnt->lastBrush = b; } // link colorMod volume brushes to the entity directly if( b->contentShader != NULL && b->contentShader->colorMod != NULL && b->contentShader->colorMod->type == CM_VOLUME ) { b->nextColorModBrush = mapEnt->colorModBrushes; mapEnt->colorModBrushes = b; } return b; }
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]; }
/* ================ 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++) { if(brush->sides[i].backSide) { continue; // fake back-sided polygons never split } 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(bspBrush_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; ClipWindingEpsilon(w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON */ , &cw[0], &cw[1]); for(j = 0; j < 2; j++) { if(!cw[j]) continue; /* if (WindingIsTiny (cw[j])) { FreeWinding (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++) { BoundBrush(b[i]); for(j = 0; j < 3; j++) { if(b[i]->mins[j] < MIN_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD) { Sys_FPrintf(SYS_VRB, "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]) 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; // qprintf ("tiny volume after clip\n"); } } } *front = b[0]; *back = b[1]; }
void CSG_SplitBrush_new( cbspbrush_t *in, cplane_t *plane, cbspbrush_t **front, cbspbrush_t **back ) { int i; int exact; polygon_t *splitpoly; cbspbrush_t *b, *f; *front = *back = NULL; // // split plane part of brush ? // exact = CSG_IsExactOnPlane( in, plane ); if ( exact == BRUSH_BACK_ON ) { *back = CopyBrush( in ); return; } if ( exact == BRUSH_FRONT_ON ) { *front = CopyBrush( in ); return; } // // real check // splitpoly = BasePolygonForPlane( plane->norm, plane->dist ); for ( i = 0; i < in->surfacenum; i++ ) { ClipPolygonInPlace( &splitpoly, in->surfaces[i].pl->norm, in->surfaces[i].pl->dist ); if ( !splitpoly ) break; } if ( !splitpoly ) { // no splitpoly => brush is not split by plane // determine on which side of plane is the complete brush int check = -1; for ( i = 0; i < in->surfacenum; i++ ) { if ( !in->surfaces[i].p ) continue; check = CheckPolygonWithPlane( in->surfaces[i].p, plane->norm, plane->dist ); if ( check == POLY_BACK ) break; else if ( check == POLY_FRONT ) break; else if ( check == POLY_ON ) continue; printf( "?" ); } if ( check == POLY_BACK ) { *back = CopyBrush( in ); return; } else if ( check == POLY_FRONT ) { *front = CopyBrush( in ); return; } else { printf( "can't get planeside of brush.\n" ); *front = NULL; *back = NULL; return; } } // // split input brush // f = NewBrush( in->surfacenum + 2 ); b = NewBrush( in->surfacenum + 2 ); f->surfacenum = b->surfacenum = 0; f->original = in->original; b->original = in->original; f->contents = in->contents; b->contents = in->contents; for( i = 0; i < in->surfacenum; i++ ) { polygon_t *fpoly, *bpoly; SplitPolygon( in->surfaces[i].p, plane->norm, plane->dist, &fpoly, &bpoly ); if ( fpoly ) { // add polygon to front brush and copy the rest if ( f->surfacenum == in->surfacenum + 2 ) Error( "reached max surfs\n" ); memcpy( &f->surfaces[f->surfacenum], &in->surfaces[i], sizeof( csurface_t ) ); f->surfaces[f->surfacenum].p = fpoly; f->surfacenum++; } if ( bpoly ) { // add polygon to back brush and copy the rest if ( b->surfacenum == in->surfacenum + 2 ) Error( "reached max surfs\n" ); memcpy( &b->surfaces[b->surfacenum], &in->surfaces[i], sizeof( csurface_t ) ); b->surfaces[b->surfacenum].p = bpoly; b->surfacenum++; } } // // add split plane to front and back brush // // the backside brush gets the plane if ( f->surfacenum == in->surfacenum + 2 ) Error( "reached max surfs\n" ); b->surfaces[b->surfacenum].pl = plane; b->surfaces[b->surfacenum].td = NULL; b->surfaces[b->surfacenum].state = SURFACE_STATE_BYSPLIT; b->surfaces[b->surfacenum].contents = 0; b->surfaces[b->surfacenum].p = splitpoly; b->surfacenum++; // the frontside brush gets the flipplane if ( b->surfacenum == in->surfacenum + 2 ) Error( "reached max surfs\n" ); f->surfaces[f->surfacenum].pl = plane->flipplane; f->surfaces[f->surfacenum].td = NULL; f->surfaces[f->surfacenum].state = SURFACE_STATE_BYSPLIT; f->surfaces[f->surfacenum].contents = 0; f->surfaces[f->surfacenum].p = PolygonFlip( splitpoly ); f->surfacenum++; if ( b->surfacenum < 4 ) { printf( "no back %d\n", b->surfacenum ); FreeBrush( b ); b = NULL; } if ( f->surfacenum < 4 ) { printf( "no front %d\n", f->surfacenum ); FreeBrush( f ); f = NULL; } if ( b ) CalcBrushBounds( b ); if ( f ) CalcBrushBounds( f ); *back = b; *front = f; }
//=========================================================================== // Generates two new brushes, leaving the original // unchanged // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== 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.2 ) { // PLANESIDE_EPSILON) // only on back *back = CopyBrush( brush ); return; } if ( d_back > -0.2 ) { // 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 ); } //free a possible winding if ( w ) { FreeWinding( w ); } return; } if ( WindingIsHuge( w ) ) { Log_Write( "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->original = s->original; cs->winding = cw[j]; cs->flags &= ~SFL_TESTED; } } // 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_MAP_BOUNDS || b[i]->maxs[j] > MAX_MAP_BOUNDS ) { Log_Write( "bogus brush after clip" ); break; } } if ( b[i]->numsides < 3 || j < 3 ) { FreeBrush( b[i] ); b[i] = NULL; } } if ( !( b[0] && b[1] ) ) { if ( !b[0] && !b[1] ) { Log_Write( "split removed brush\r\n" ); } else { Log_Write( "split not on both sides\r\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; //never use these sides as splitters cs->flags &= ~SFL_VISIBLE; cs->flags &= ~SFL_TESTED; 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; //Log_Write("tiny volume after clip"); } } if ( !b[0] && !b[1] ) { Log_Write( "two tiny brushes\r\n" ); } //end if } *front = b[0]; *back = b[1]; } //end of the function SplitBrush
/* ============ ProcessSubModel ============ */ void ProcessSubModel(int entityNum) { entity_t *e; tree_t *tree; bspBrush_t *b, *bc; node_t *node; e = &entities[entityNum]; e->firstDrawSurf = numMapDrawSurfs; BeginModel(e); PatchMapDrawSurfs(e); // just put all the brushes in an empty leaf // FIXME: patches? node = AllocNode(); node->planenum = PLANENUM_LEAF; for(b = e->brushes; b; b = b->next) { bc = CopyBrush(b); bc->next = node->brushlist; node->brushlist = bc; } tree = AllocTree(); tree->headnode = node; ClipSidesIntoTree(e, tree); // create drawsurfs for triangle models AddTriangleModel(e, qfalse); // subdivide each drawsurf as required by shader tesselation or fog if(!nosubdivide) { SubdivideDrawSurfs(e, tree); } // merge together all common shaders on the same plane and remove // all colinear points, so extra tjunctions won't be generated if(!nomerge) { MergeSides(e, tree); // !@# testing } // add in any vertexes required to fix tjunctions if(!notjunc) { FixTJunctions(e); } // allocate lightmaps for faces and patches AllocateLightmaps(e); // add references to the final drawsurfs in the apropriate clusters FilterDrawsurfsIntoTree(e, tree); EndModel(e, node); FreeTree(tree); }