brush_t *CopyBrush( brush_t *brush ){ brush_t *newBrush; size_t size; int i; /* copy brush */ size = (size_t)&( ( (brush_t*) 0 )->sides[ brush->numsides ] ); newBrush = AllocBrush( brush->numsides ); memcpy( newBrush, brush, size ); /* ydnar: nuke linked list */ newBrush->next = NULL; /* copy sides */ for ( i = 0; i < brush->numsides; i++ ) { if ( brush->sides[ i ].winding != NULL ) { newBrush->sides[ i ].winding = CopyWinding( brush->sides[ i ].winding ); } } /* return it */ return newBrush; }
//=========================================================================== // Creates a new axial brush // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs) { bspbrush_t *b; int i; vec3_t normal; vec_t dist; b = AllocBrush (6); b->numsides = 6; for (i=0 ; i<3 ; i++) { VectorClear (normal); normal[i] = 1; dist = maxs[i]; b->sides[i].planenum = FindFloatPlane (normal, dist); normal[i] = -1; dist = -mins[i]; b->sides[3+i].planenum = FindFloatPlane (normal, dist); } CreateBrushWindings (b); return b; } //end of the function BrushFromBounds
/* ================== BrushFromBounds Creates a new axial brush ================== */ bspbrush_t *BrushFromBounds (Vector& mins, Vector& maxs) { bspbrush_t *b; int i; Vector normal; vec_t dist; b = AllocBrush (6); b->numsides = 6; for (i=0 ; i<3 ; i++) { VectorClear (normal); normal[i] = 1; dist = maxs[i]; b->sides[i].planenum = g_MainMap->FindFloatPlane (normal, dist); normal[i] = -1; dist = -mins[i]; b->sides[3+i].planenum = g_MainMap->FindFloatPlane (normal, dist); } CreateBrushWindings (b); return b; }
/* ================= LoadMapFile loads a map file into a list of entities ================= */ void LoadMapFile( const char *filename, bool onlyLights ) { brush_t *b; int oldNumEntities, numMapBrushes; MsgDev( D_NOTE, "--- LoadMapFile ---\n" ); Msg( "Loading %s\n", filename ); mapfile = Com_OpenScript( filename, NULL, 0 ); if( !mapfile ) Sys_Break( "can't loading map file %s\n", filename ); if( onlyLights ) oldNumEntities = numEntities; else numEntities = 0; c_detail = 0; numMapDrawSurfs = 0; g_bBrushPrimit = BRUSH_UNKNOWN; // allocate a very large temporary brush for building the brushes as they are loaded buildBrush = AllocBrush( MAX_BUILD_SIDES ); while( ParseMapEntity( onlyLights )); Com_CloseScript( mapfile ); if( onlyLights ) { MsgDev( D_INFO, "%9d light entities\n", numEntities - oldNumEntities ); } else { ClearBounds( mapMins, mapMaxs ); for( b = entities[0].brushes; b; b = b->next ) { AddPointToBounds( b->mins, mapMins, mapMaxs ); AddPointToBounds( b->maxs, mapMins, mapMaxs ); } VectorSubtract( mapMaxs, mapMins, mapSize ); numMapBrushes = CountBrushList( entities[0].brushes ); if(( float )c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 ) MsgDev( D_WARN, "Over 90 percent structural map detected. Compile time may be adversely affected.\n" ); MsgDev( D_NOTE, "%9d total world brushes\n", numMapBrushes ); MsgDev( D_NOTE, "%9d detail brushes\n", c_detail ); MsgDev( D_NOTE, "%9d patches\n", numMapPatches ); MsgDev( D_NOTE, "%9d boxbevels\n", c_boxbevels ); MsgDev( D_NOTE, "%9d edgebevels\n", c_edgebevels ); MsgDev( D_NOTE, "%9d entities\n", numEntities ); MsgDev( D_NOTE, "%9d planes\n", nummapplanes ); MsgDev( D_NOTE, "%9d areaportals\n", c_areaportals ); MsgDev( D_INFO, "World size: %5.0f, %5.0f, %5.0f\n", mapSize[0], mapSize[1], mapSize[2] ); // write bogus map if( fakemap ) WriteBSPBrushMap( "fakemap.map", entities[0].brushes ); } }
/* ================ LoadDMapFile ================ */ bool LoadDMapFile( const char *filename ) { primitive_t *prim; idBounds mapBounds; int brushes, triSurfs; int i; int size; common->Printf( "--- LoadDMapFile ---\n" ); common->Printf( "loading %s\n", filename ); // load and parse the map file into canonical form dmapGlobals.dmapFile = new idMapFile(); if( !dmapGlobals.dmapFile->Parse( filename ) ) { delete dmapGlobals.dmapFile; dmapGlobals.dmapFile = NULL; common->Warning( "Couldn't load map file: '%s'", filename ); return false; } dmapGlobals.mapPlanes.Clear(); dmapGlobals.mapPlanes.SetGranularity( 1024 ); // process the canonical form into utility form dmapGlobals.num_entities = 0; c_numMapPatches = 0; c_areaportals = 0; size = dmapGlobals.dmapFile->GetNumEntities() * sizeof( dmapGlobals.uEntities[0] ); dmapGlobals.uEntities = ( uEntity_t * )Mem_Alloc( size ); memset( dmapGlobals.uEntities, 0, size ); // allocate a very large temporary brush for building // the brushes as they are loaded buildBrush = AllocBrush( MAX_BUILD_SIDES ); for( i = 0 ; i < dmapGlobals.dmapFile->GetNumEntities() ; i++ ) { ProcessMapEntity( dmapGlobals.dmapFile->GetEntity( i ) ); } CreateMapLights( dmapGlobals.dmapFile ); brushes = 0; triSurfs = 0; mapBounds.Clear(); for( prim = dmapGlobals.uEntities[0].primitives ; prim ; prim = prim->next ) { if( prim->brush ) { brushes++; mapBounds.AddBounds( prim->brush->bounds ); } else if( prim->tris ) { triSurfs++; } } common->Printf( "%5i total world brushes\n", brushes ); common->Printf( "%5i total world triSurfs\n", triSurfs ); common->Printf( "%5i patches\n", c_numMapPatches ); common->Printf( "%5i entities\n", dmapGlobals.num_entities ); common->Printf( "%5i planes\n", dmapGlobals.mapPlanes.Num() ); common->Printf( "%5i areaportals\n", c_areaportals ); common->Printf( "size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", mapBounds[0][0], mapBounds[0][1], mapBounds[0][2], mapBounds[1][0], mapBounds[1][1], mapBounds[1][2] ); return true; }
/* ================ idCollisionModelManagerLocal::ParseBrushes ================ */ void idCollisionModelManagerLocal::ParseBrushes( idLexer *src, cm_model_t *model ) { cm_brush_t *b; int i, numPlanes; idVec3 normal; idToken token; if( src->CheckTokenType( TT_NUMBER, 0, &token ) ) { model->brushBlock = static_cast<cm_brushBlock_t *>( Mem_Alloc( sizeof( cm_brushBlock_t ) + token.GetIntValue() ) ); model->brushBlock->bytesRemaining = token.GetIntValue(); model->brushBlock->next = ( reinterpret_cast<byte *>( model->brushBlock ) ) + sizeof( cm_brushBlock_t ); } src->ExpectTokenString( "{" ); while( !src->CheckTokenString( "}" ) ) { // parse brush numPlanes = src->ParseInt(); b = AllocBrush( model, numPlanes ); b->numPlanes = numPlanes; src->ExpectTokenString( "{" ); for( i = 0; i < b->numPlanes; i++ ) { src->Parse1DMatrix( 3, normal.ToFloatPtr() ); b->planes[i].SetNormal( normal ); b->planes[i].SetDist( src->ParseFloat() ); } src->ExpectTokenString( "}" ); src->Parse1DMatrix( 3, b->bounds[0].ToFloatPtr() ); src->Parse1DMatrix( 3, b->bounds[1].ToFloatPtr() ); src->ReadToken( &token ); if( token.type == TT_NUMBER ) { b->contents = token.GetIntValue(); // old .cm files use a single integer } else { b->contents = ContentsFromString( token ); } b->checkcount = 0; b->primitiveNum = 0; // filter brush into tree R_FilterBrushIntoTree( model, model->node, NULL, b ); } }
/** * @brief Duplicates the brush, the sides, and the windings * @sa AllocBrush */ bspbrush_t* CopyBrush (const bspbrush_t* brush) { bspbrush_t* newbrush; int i; const size_t size = offsetof(bspbrush_t, sides) + sizeof(((bspbrush_t*)0)->sides) * brush->numsides; newbrush = AllocBrush(brush->numsides); memcpy(newbrush, brush, size); for (i = 0; i < brush->numsides; i++) { const side_t* side = &brush->sides[i]; if (side->winding) newbrush->sides[i].winding = CopyWinding(side->winding); } return newbrush; }
/* ================== BrushFromBounds Creates a new axial brush ================== */ brush_t *BrushFromBounds (float minx, float miny, float minz, float maxx, float maxy, float maxz, shaderInfo_t *si) { brush_t *b; vec3_t mins, maxs; vec3_t normal; vec_t dist; int i; b = AllocBrush (6); b->entityNum = mapEntityNum; b->original = b; b->contentShader = si; b->compileFlags = si->compileFlags; b->contentFlags = si->contentFlags; b->opaque = qtrue; b->detail = qfalse; b->numsides = 6; VectorSet(mins, minx, miny, minz); VectorSet(maxs, maxx, maxy, maxz); for (i=0 ; i<3 ; i++) { VectorClear (normal); normal[i] = 1; dist = maxs[i]; b->sides[i].planenum = FindFloatPlane (normal, dist, 1, (vec3_t*) &maxs ); b->sides[i].shaderInfo = si; b->sides[i].surfaceFlags = si->surfaceFlags; b->sides[i].contentFlags = si->contentFlags; b->sides[i].compileFlags = si->compileFlags; b->sides[i].value = si->value; normal[i] = -1; dist = -mins[i]; b->sides[3+i].planenum = FindFloatPlane (normal, dist, 1, (vec3_t*) &mins ); b->sides[3+i].shaderInfo = si; b->sides[3+i].surfaceFlags = si->surfaceFlags; b->sides[3+i].contentFlags = si->contentFlags; b->sides[3+i].compileFlags = si->compileFlags; b->sides[3+i].value = si->value; } CreateBrushWindings (b); return b; }
/** * @brief Duplicates the brush, the sides, and the windings */ brush_t *CopyBrush(brush_t *brush) { brush_t *newbrush; size_t size; int32_t i; size = (size_t) & (((brush_t *) 0)->sides[brush->num_sides]); newbrush = AllocBrush(brush->num_sides); memcpy(newbrush, brush, size); for (i = 0; i < brush->num_sides; i++) { if (brush->sides[i].winding) { newbrush->sides[i].winding = CopyWinding(brush->sides[i].winding); } } return newbrush; }
//=========================================================================== // Duplicates the brush, the sides, and the windings // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bspbrush_t *CopyBrush (bspbrush_t *brush) { bspbrush_t *newbrush; size_t size; int i; size = sizeof(*newbrush) + sizeof(*brush->sides) * brush->numsides; newbrush = AllocBrush (brush->numsides); memcpy (newbrush, brush, size); for (i=0 ; i<brush->numsides ; i++) { if (brush->sides[i].winding) newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding); } return newbrush; } //end of the function CopyBrush
/* ================== CopyBrush Duplicates the brush, the sides, and the windings ================== */ bspbrush_t *CopyBrush (bspbrush_t *brush) { bspbrush_t *newbrush; int size; int i; size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]); newbrush = AllocBrush (brush->numsides); memcpy (newbrush, brush, size); for (i=0 ; i<brush->numsides ; i++) { if (brush->sides[i].winding) newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding); } return newbrush; }
/* ================== CopyBrush Duplicates the brush, the sides, and the windings ================== */ uBrush_t *CopyBrush (uBrush_t *brush) { uBrush_t *newbrush; int size; int i; size = BrushSizeForSides( brush->numsides ); newbrush = AllocBrush (brush->numsides); memcpy (newbrush, brush, size); for (i=0 ; i<brush->numsides ; i++) { if (brush->sides[i].winding) newbrush->sides[i].winding = brush->sides[i].winding->Copy(); } return newbrush; }
/* ================== BrushFromBounds Creates a new axial brush ================== */ uBrush_t *BrushFromBounds( const idBounds &bounds ) { uBrush_t *b; int i; idPlane plane; b = AllocBrush (6); b->numsides = 6; for (i=0 ; i<3 ; i++) { plane[0] = plane[1] = plane[2] = 0; plane[i] = 1; plane[3] = -bounds[1][i]; b->sides[i].planenum = FindFloatPlane( plane ); plane[i] = -1; plane[3] = bounds[0][i]; b->sides[3+i].planenum = FindFloatPlane( plane ); } CreateBrushWindings (b); return b; }
/** * @brief Creates a new axial brush */ static brush_t *BrushFromBounds(vec3_t mins, vec3_t maxs) { brush_t *b; int32_t i; vec3_t normal; vec_t dist; b = AllocBrush(6); b->num_sides = 6; for (i = 0; i < 3; i++) { VectorClear(normal); normal[i] = 1; dist = maxs[i]; b->sides[i].plane_num = FindPlane(normal, dist); normal[i] = -1; dist = -mins[i]; b->sides[3 + i].plane_num = FindPlane(normal, dist); } CreateBrushWindings(b); return b; }
/** * @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]; }
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]; }
/* ================ 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]; }
//=========================================================================== // 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
/* ================ 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]; }
void InsertModel(char *name, int frame, matrix_t transform, matrix_t nTransform, remap_t * remap, shaderInfo_t * celShader, int eNum, int castShadows, int recvShadows, int spawnFlags, float lightmapScale, int lightmapSampleSize, float shadeAngle) { int i, j, k, s, numSurfaces; matrix_t identity; picoModel_t *model; picoShader_t *shader; picoSurface_t *surface; shaderInfo_t *si; mapDrawSurface_t *ds; bspDrawVert_t *dv; char *picoShaderName; char shaderName[MAX_QPATH]; picoVec_t *xyz, *normal, *st; byte *color; picoIndex_t *indexes; remap_t *rm, *glob; double normalEpsilon_save; double distanceEpsilon_save; /* get model */ model = LoadModel(name, frame); if(model == NULL) return; /* handle null matrix */ if(transform == NULL) { MatrixIdentity(identity); transform = identity; } /* create transform matrix for normals */ #if 0 MatrixCopy(transform, nTransform); if(MatrixInverse(nTransform)) { Sys_FPrintf(SYS_VRB, "WARNING: Can't invert model transform matrix, using transpose instead\n"); MatrixTranspose(transform, nTransform); } #endif /* fix bogus lightmap scale */ if(lightmapScale <= 0.0f) lightmapScale = 1.0f; /* fix bogus shade angle */ if(shadeAngle <= 0.0f) shadeAngle = 0.0f; /* each surface on the model will become a new map drawsurface */ numSurfaces = PicoGetModelNumSurfaces(model); //% Sys_FPrintf( SYS_VRB, "Model %s has %d surfaces\n", name, numSurfaces ); for(s = 0; s < numSurfaces; s++) { /* get surface */ surface = PicoGetModelSurface(model, s); if(surface == NULL) continue; /* only handle triangle surfaces initially (fixme: support patches) */ if(PicoGetSurfaceType(surface) != PICO_TRIANGLES) continue; /* fix the surface's normals */ PicoFixSurfaceNormals(surface); /* allocate a surface (ydnar: gs mods) */ ds = AllocDrawSurface(SURFACE_TRIANGLES); ds->entityNum = eNum; ds->castShadows = castShadows; ds->recvShadows = recvShadows; /* get shader name */ shader = PicoGetSurfaceShader(surface); if(shader == NULL) picoShaderName = ""; else picoShaderName = PicoGetShaderName(shader); /* handle shader remapping */ glob = NULL; for(rm = remap; rm != NULL; rm = rm->next) { if(rm->from[0] == '*' && rm->from[1] == '\0') glob = rm; else if(!Q_stricmp(picoShaderName, rm->from)) { Sys_FPrintf(SYS_VRB, "Remapping %s to %s\n", picoShaderName, rm->to); picoShaderName = rm->to; glob = NULL; break; } } if(glob != NULL) { Sys_FPrintf(SYS_VRB, "Globbing %s to %s\n", picoShaderName, glob->to); picoShaderName = glob->to; } /* shader renaming for sof2 */ if(renameModelShaders) { strcpy(shaderName, picoShaderName); StripExtension(shaderName); if(spawnFlags & 1) strcat(shaderName, "_RMG_BSP"); else strcat(shaderName, "_BSP"); si = ShaderInfoForShader(shaderName); } else { si = ShaderInfoForShader(picoShaderName); // Tr3B: HACK to support the messy Doom 3 materials provided by .ASE files if(!si->explicitDef) { picoShaderName = PicoGetShaderMapName(shader); Q_strncpyz(shaderName, picoShaderName, sizeof(shaderName)); StripExtension(shaderName); i = 0; while(shaderName[i]) { if(shaderName[i] == '\\') shaderName[i] = '/'; i++; } if(strstr(shaderName, "base/")) { si = ShaderInfoForShader(strstr(shaderName, "base/") + strlen("base/")); Sys_FPrintf(SYS_WRN, "WARNING: Applied .ASE material loader HACK to '%s' -> '%s'\n", picoShaderName, si->shader); } } } /* set shader */ ds->shaderInfo = si; /* force to meta? */ if((si != NULL && si->forceMeta) || (spawnFlags & 4)) /* 3rd bit */ ds->type = SURFACE_FORCED_META; /* fix the surface's normals (jal: conditioned by shader info) */ //if(!(spawnFlags & 64) && (shadeAngle == 0.0f || ds->type != SURFACE_FORCED_META)) // PicoFixSurfaceNormals(surface); /* set sample size */ if(lightmapSampleSize > 0.0f) ds->sampleSize = lightmapSampleSize; /* set lightmap scale */ if(lightmapScale > 0.0f) ds->lightmapScale = lightmapScale; /* set shading angle */ if(shadeAngle > 0.0f) ds->shadeAngleDegrees = shadeAngle; /* set particulars */ ds->numVerts = PicoGetSurfaceNumVertexes(surface); ds->verts = safe_malloc(ds->numVerts * sizeof(ds->verts[0])); memset(ds->verts, 0, ds->numVerts * sizeof(ds->verts[0])); ds->numIndexes = PicoGetSurfaceNumIndexes(surface); ds->indexes = safe_malloc(ds->numIndexes * sizeof(ds->indexes[0])); memset(ds->indexes, 0, ds->numIndexes * sizeof(ds->indexes[0])); /* copy vertexes */ for(i = 0; i < ds->numVerts; i++) { /* get vertex */ dv = &ds->verts[i]; /* xyz and normal */ xyz = PicoGetSurfaceXYZ(surface, i); VectorCopy(xyz, dv->xyz); MatrixTransformPoint2(transform, dv->xyz); normal = PicoGetSurfaceNormal(surface, i); VectorCopy(normal, dv->normal); MatrixTransformNormal2(nTransform, dv->normal); VectorNormalize2(dv->normal, dv->normal); /* ydnar: tek-fu celshading support for flat shaded shit */ if(flat) { dv->st[0] = si->stFlat[0]; dv->st[1] = si->stFlat[1]; } /* ydnar: gs mods: added support for explicit shader texcoord generation */ else if(si->tcGen) { /* project the texture */ dv->st[0] = DotProduct(si->vecs[0], dv->xyz); dv->st[1] = DotProduct(si->vecs[1], dv->xyz); } /* normal texture coordinates */ else { st = PicoGetSurfaceST(surface, 0, i); dv->st[0] = st[0]; dv->st[1] = st[1]; } /* set lightmap/color bits */ color = PicoGetSurfaceColor(surface, 0, i); dv->paintColor[0] = color[0] / 255.0f; dv->paintColor[1] = color[1] / 255.0f; dv->paintColor[2] = color[2] / 255.0f; dv->paintColor[3] = color[3] / 255.0f; for(j = 0; j < MAX_LIGHTMAPS; j++) { dv->lightmap[j][0] = 0.0f; dv->lightmap[j][1] = 0.0f; dv->lightColor[j][0] = 255; dv->lightColor[j][1] = 255; dv->lightColor[j][2] = 255; dv->lightColor[j][3] = 255; } } /* copy indexes */ indexes = PicoGetSurfaceIndexes(surface, 0); for(i = 0; i < ds->numIndexes; i++) ds->indexes[i] = indexes[i]; /* set cel shader */ ds->celShader = celShader; /* ydnar: giant hack land: generate clipping brushes for model triangles */ if(si->clipModel || (spawnFlags & 2)) /* 2nd bit */ { vec3_t points[4], backs[3]; vec4_t plane, reverse, pa, pb, pc; /* temp hack */ if(!si->clipModel && (((si->compileFlags & C_TRANSLUCENT) && !(si->compileFlags & C_COLLISION)) || !(si->compileFlags & C_SOLID))) continue; /* walk triangle list */ for(i = 0; i < ds->numIndexes; i += 3) { /* overflow hack */ AUTOEXPAND_BY_REALLOC(mapplanes, (nummapplanes + 64) << 1, allocatedmapplanes, 1024); /* make points and back points */ for(j = 0; j < 3; j++) { /* get vertex */ dv = &ds->verts[ds->indexes[i + j]]; /* copy xyz */ VectorCopy(dv->xyz, points[j]); VectorCopy(dv->xyz, backs[j]); /* find nearest axial to normal and push back points opposite */ /* note: this doesn't work as well as simply using the plane of the triangle, below */ for(k = 0; k < 3; k++) { if(fabs(dv->normal[k]) >= fabs(dv->normal[(k + 1) % 3]) && fabs(dv->normal[k]) >= fabs(dv->normal[(k + 2) % 3])) { backs[j][k] += dv->normal[k] < 0.0f ? 64.0f : -64.0f; break; } } } VectorCopy(points[0], points[3]); // for cyclic usage /* make plane for triangle */ // div0: add some extra spawnflags: // 0: snap normals to axial planes for extrusion // 8: extrude with the original normals // 16: extrude only with up/down normals (ideal for terrain) // 24: extrude by distance zero (may need engine changes) if(PlaneFromPoints(plane, points[0], points[1], points[2], qtrue)) { vec3_t bestNormal; float backPlaneDistance = 2; if(spawnFlags & 8) // use a DOWN normal { if(spawnFlags & 16) { // 24: normal as is, and zero width (broken) VectorCopy(plane, bestNormal); } else { // 8: normal as is VectorCopy(plane, bestNormal); } } else { if(spawnFlags & 16) { // 16: UP/DOWN normal VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); } else { // 0: axial normal if(fabs(plane[0]) > fabs(plane[1])) // x>y if(fabs(plane[1]) > fabs(plane[2])) // x>y, y>z VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0); else // x>y, z>=y if(fabs(plane[0]) > fabs(plane[2])) // x>z, z>=y VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0); else // z>=x, x>y VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); else // y>=x if(fabs(plane[1]) > fabs(plane[2])) // y>z, y>=x VectorSet(bestNormal, 0, (plane[1] >= 0 ? 1 : -1), 0); else // z>=y, y>=x VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); } } /* build a brush */ buildBrush = AllocBrush(48); buildBrush->entityNum = mapEntityNum; buildBrush->original = buildBrush; buildBrush->contentShader = si; buildBrush->compileFlags = si->compileFlags; buildBrush->contentFlags = si->contentFlags; buildBrush->generatedClipBrush = qtrue; normalEpsilon_save = normalEpsilon; distanceEpsilon_save = distanceEpsilon; if(si->compileFlags & C_STRUCTURAL) // allow forced structural brushes here { buildBrush->detail = qfalse; // only allow EXACT matches when snapping for these (this is mostly for caulk brushes inside a model) if(normalEpsilon > 0) normalEpsilon = 0; if(distanceEpsilon > 0) distanceEpsilon = 0; } else buildBrush->detail = qtrue; /* regenerate back points */ for(j = 0; j < 3; j++) { /* get vertex */ dv = &ds->verts[ds->indexes[i + j]]; // shift by some units VectorMA(dv->xyz, -64.0f, bestNormal, backs[j]); // 64 prevents roundoff errors a bit } /* make back plane */ VectorScale(plane, -1.0f, reverse); reverse[3] = -plane[3]; if((spawnFlags & 24) != 24) reverse[3] += DotProduct(bestNormal, plane) * backPlaneDistance; // that's at least sqrt(1/3) backPlaneDistance, unless in DOWN mode; in DOWN mode, we are screwed anyway if we encounter a plane that's perpendicular to the xy plane) if(PlaneFromPoints(pa, points[2], points[1], backs[1], qtrue) && PlaneFromPoints(pb, points[1], points[0], backs[0], qtrue) && PlaneFromPoints(pc, points[0], points[2], backs[2], qtrue)) { /* set up brush sides */ buildBrush->numsides = 5; buildBrush->sides[0].shaderInfo = si; for(j = 1; j < buildBrush->numsides; j++) buildBrush->sides[j].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works buildBrush->sides[0].planenum = FindFloatPlane(plane, plane[3], 3, points); buildBrush->sides[1].planenum = FindFloatPlane(pa, pa[3], 2, &points[1]); // pa contains points[1] and points[2] buildBrush->sides[2].planenum = FindFloatPlane(pb, pb[3], 2, &points[0]); // pb contains points[0] and points[1] buildBrush->sides[3].planenum = FindFloatPlane(pc, pc[3], 2, &points[2]); // pc contains points[2] and points[0] (copied to points[3] buildBrush->sides[4].planenum = FindFloatPlane(reverse, reverse[3], 3, backs); } else { free(buildBrush); continue; } normalEpsilon = normalEpsilon_save; distanceEpsilon = distanceEpsilon_save; /* add to entity */ if(CreateBrushWindings(buildBrush)) { AddBrushBevels(); //% EmitBrushes( buildBrush, NULL, NULL ); buildBrush->next = entities[mapEntityNum].brushes; entities[mapEntityNum].brushes = buildBrush; entities[mapEntityNum].numBrushes++; } else free(buildBrush); } } } } }
/** * @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]; }
/* =============== LoadBrush Converts a mapbrush to a bsp brush =============== */ brush_t *LoadBrush (mbrush_t *mb, int brushnum, int hullnum) { brush_t *b; int contents; char *name; mface_t *f; // // check texture name for attributes // for (f = mb->faces;f;f = f->next) { name = miptex[texinfo[f->texinfo].miptex]; if (hullnum == 0) { // textures which don't show up in the drawing hull if (!Q_strcasecmp(name, "clip")) return NULL; if (!Q_strcasecmp(name, "common/nodraw")) return NULL; if (!Q_strcasecmp(name, "textures/common/nodraw")) return NULL; if (!Q_strcasecmp(name, "textures/common/clip")) return NULL; if (!Q_strcasecmp(name, "textures/common/full_clip")) return NULL; } if (!Q_strcasecmp(name, "textures/editor/visportal")) return NULL; } name = miptex[texinfo[mb->faces->texinfo].miptex]; // g-cont. without this check e1m3 won't compile if (name[0] == '!' && worldmodel ) // entities never use water merging { if (!Q_strncasecmp(name+1,"lava",4)) contents = CONTENTS_LAVA; else if (!Q_strncasecmp(name+1,"slime",5)) contents = CONTENTS_SLIME; else contents = CONTENTS_WATER; } else if (!Q_strncasecmp (name, "sky",3) && hullnum == 0) contents = CONTENTS_SKY; else contents = CONTENTS_SOLID; if (hullnum && contents != CONTENTS_SOLID && contents != CONTENTS_SKY) return NULL; // water brushes don't show up in clipping hulls // no seperate textures on clip hull // // create the faces // brush_faces = NULL; numbrushfaces = 0; for (f=mb->faces ; f ; f=f->next) { faces[numbrushfaces] = *f; if (hullnum) faces[numbrushfaces].texinfo = 0; numbrushfaces++; } if (hullnum) ExpandBrush (hullnum); CreateBrushFaces (); if (!brush_faces) { printf ("WARNING: couldn't create faces for brush %i in entity %i (incomplete brush?)\n", brushnum, (int)(CurrentEntity - entities)); return NULL; } // // create the brush // b = AllocBrush (); b->contents = contents; b->faces = brush_faces; VectorCopy (brush_mins, b->mins); VectorCopy (brush_maxs, b->maxs); // debugging code //printf("mapbrush\n"); //for (f=mb->faces ; f ; f=f->next) // printf("face %f %f %f %f \"%s\"\n", f->plane.normal[0], f->plane.normal[1], f->plane.normal[2], f->plane.dist, miptex[texinfo[f->texinfo].miptex]); //printf("bspbrush %i\n", numbrushfaces); //face_t *face; //for (face=b->faces ; face ; face=face->next) // printf("bspface %f %f %f %f\n", mapplanes[face->planenum].normal[0], mapplanes[face->planenum].normal[1], mapplanes[face->planenum].normal[2], mapplanes[face->planenum].dist); return b; }
//=========================================================================== // 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
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bspbrush_t *MakeBspBrushList(int startbrush, int endbrush, vec3_t clipmins, vec3_t clipmaxs) { mapbrush_t *mb; bspbrush_t *brushlist, *newbrush; int i, j; int c_faces; int c_brushes; int numsides; int vis; vec3_t normal; float dist; for (i=0 ; i<2 ; i++) { VectorClear (normal); normal[i] = 1; dist = clipmaxs[i]; maxplanenums[i] = FindFloatPlane(normal, dist); dist = clipmins[i]; minplanenums[i] = FindFloatPlane(normal, dist); } brushlist = NULL; c_faces = 0; c_brushes = 0; for (i=startbrush ; i<endbrush ; i++) { mb = &mapbrushes[i]; numsides = mb->numsides; if (!numsides) continue; // make sure the brush has at least one face showing vis = 0; for (j=0 ; j<numsides ; j++) if ((mb->original_sides[j].flags & SFL_VISIBLE) && mb->original_sides[j].winding) vis++; #if 0 if (!vis) continue; // no faces at all #endif // if the brush is outside the clip area, skip it for (j=0 ; j<3 ; j++) if (mb->mins[j] >= clipmaxs[j] || mb->maxs[j] <= clipmins[j]) break; if (j != 3) continue; // // make a copy of the brush // newbrush = AllocBrush (mb->numsides); newbrush->original = mb; newbrush->numsides = mb->numsides; memcpy (newbrush->sides, mb->original_sides, numsides*sizeof(side_t)); for (j=0 ; j<numsides ; j++) { if (newbrush->sides[j].winding) newbrush->sides[j].winding = CopyWinding (newbrush->sides[j].winding); if (newbrush->sides[j].surf & SURF_HINT) newbrush->sides[j].flags |= SFL_VISIBLE; // hints are always visible } VectorCopy (mb->mins, newbrush->mins); VectorCopy (mb->maxs, newbrush->maxs); // // carve off anything outside the clip box // newbrush = ClipBrushToBox (newbrush, clipmins, clipmaxs); if (!newbrush) continue; c_faces += vis; c_brushes++; newbrush->next = brushlist; brushlist = newbrush; } return brushlist; } //end of the function MakeBspBrushList
/* =============== LoadBrush Converts a mapbrush to a bsp brush =============== */ brush_t *LoadBrush (mbrush_t *mb, int hullnum) { brush_t *b; int contents; char *name; mface_t *f; // // check texture name for attributes // name = miptex[texinfo[mb->faces->texinfo].miptex]; if (!Q_strcasecmp(name, "clip") && hullnum == 0) return NULL; // "clip" brushes don't show up in the draw hull if (name[0] == '*' && worldmodel) // entities never use water merging { if (!Q_strncasecmp(name+1,"lava",4)) contents = CONTENTS_LAVA; else if (!Q_strncasecmp(name+1,"slime",5)) contents = CONTENTS_SLIME; else contents = CONTENTS_WATER; } else if (!Q_strncasecmp (name, "sky",3) && worldmodel && hullnum == 0) contents = CONTENTS_SKY; else contents = CONTENTS_SOLID; if (hullnum && contents != CONTENTS_SOLID && contents != CONTENTS_SKY) return NULL; // water brushes don't show up in clipping hulls // no seperate textures on clip hull // // create the faces // brush_faces = NULL; numbrushfaces = 0; for (f=mb->faces ; f ; f=f->next) { faces[numbrushfaces] = *f; if (hullnum) faces[numbrushfaces].texinfo = 0; numbrushfaces++; } CreateBrushFaces (); if (!brush_faces) { printf ("WARNING: couldn't create brush faces\n"); return NULL; } if (hullnum) { ExpandBrush (hullnum); CreateBrushFaces (); } // // create the brush // b = AllocBrush (); b->contents = contents; b->faces = brush_faces; VectorCopy (brush_mins, b->mins); VectorCopy (brush_maxs, b->maxs); return b; }
/* =============== LoadBrush Converts a mapbrush to a bsp brush =============== */ brush_t *LoadBrush (mbrush_t *mb, int hullnum) { brush_t *b; int contents, NoOfTex = 0, I, TexNo[3], MipTex1, MipTex2, Hull; char *name, Str[512]; mface_t *f; CurrLine = mb->Line; // // check texture name for attributes // name = miptex[MipTex1 = texinfo[mb->faces->texinfo].miptex]; if (!Q_strcasecmp(name, "clip") && hullnum == 0) return NULL; // "clip" brushes don't show up in the draw hull if (name[0] == '*' && worldmodel) // entities never use water merging { if (!Q_strncasecmp(name+1,"lava",4)) contents = CONTENTS_LAVA; else if (!Q_strncasecmp(name+1,"slime",5)) contents = CONTENTS_SLIME; else contents = CONTENTS_WATER; } else if (!options.SolidMap && !Q_strncasecmp (name, "sky",3) && worldmodel && hullnum == 0) contents = CONTENTS_SKY; else contents = CONTENTS_SOLID; if (hullnum && contents != CONTENTS_SOLID && contents != CONTENTS_SKY) return NULL; // water brushes don't show up in clipping hulls // no seperate textures on clip hull // // create the faces // brush_faces = NULL; numbrushfaces = 0; for (f=mb->faces ; f ; f=f->next) { faces[numbrushfaces] = *f; if (hullnum) faces[numbrushfaces].texinfo = 0; numbrushfaces++; if (numbrushfaces == MAX_FACES) Message (MSGERR, "LoadBrush: numbrushfaces == MAX_FACES (%d) on line %d", MAX_FACES, CurrLine); } CreateBrushFaces (); if (!brush_faces) { strcpy(Str, name); // Find max 3 extra unique texture names for (f = mb->faces; f; f = f->next) { MipTex2 = texinfo[f->texinfo].miptex; if (NoOfTex < 3 && MipTex2 != MipTex1) { for (I = 0; I < NoOfTex; ++I) { if (MipTex2 == TexNo[I]) break; } if (I == NoOfTex) TexNo[NoOfTex++] = MipTex2; } } for (I = 0; I < NoOfTex; ++I) { strcat(Str, " "); strcat(Str, miptex[TexNo[I]]); } Message (MSGWARN, "Couldn't create brush on line %d with %d faces, %s", CurrLine, numbrushfaces, Str); return NULL; } Hull = hullnum == 0 ? options.visiblehull : hullnum; if (Hull || options.HullExpansion[1] > 0) { ExpandBrush (Hull); FreeBrushFaces(brush_faces); CreateBrushFaces (); } // // create the brush // b = AllocBrush (); b->contents = contents; b->faces = brush_faces; VectorCopy (brush_mins, b->mins); VectorCopy (brush_maxs, b->maxs); return b; }