//=========================================================================== // Returns a single brush made up by the intersection of the // two provided brushes, or NULL if they are disjoint. // // The originals are undisturbed. // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bspbrush_t *IntersectBrush( bspbrush_t *a, bspbrush_t *b ) { int i; bspbrush_t *front, *back; bspbrush_t *in; in = a; for ( i = 0 ; i < b->numsides && in ; i++ ) { SplitBrush( in, b->sides[i].planenum, &front, &back ); // SplitBrush2(in, b->sides[i].planenum, &front, &back); if ( in != a ) { FreeBrush( in ); } if ( front ) { FreeBrush( front ); } in = back; } //end for if ( in == a ) { return NULL; } in->next = NULL; return in; } //end of the function IntersectBrush
//----------------------------------------------------------------------------- // Purpose: Recursively filter a brush through the tree // Input : *node - // *brush - //----------------------------------------------------------------------------- void MergeBrush_r( node_t *node, bspbrush_t *brush ) { if ( node->planenum == PLANENUM_LEAF ) { if ( node->contents & CONTENTS_SOLID ) { FreeBrush( brush ); } else { AddBrushToLeaf( node, brush ); } return; } bspbrush_t *front, *back; SplitBrush( brush, node->planenum, &front, &back ); FreeBrush( brush ); if ( front ) { MergeBrush_r( node->children[0], front ); } if ( back ) { MergeBrush_r( node->children[1], back ); } }
//=========================================================================== // Returns a list of brushes that remain after B is subtracted from A. // May by empty if A is contained inside B. // The originals are undisturbed. // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bspbrush_t *SubtractBrush (bspbrush_t *a, bspbrush_t *b) { // a - b = out (list) int i; bspbrush_t *front, *back; bspbrush_t *out, *in; in = a; out = NULL; for (i = 0; i < b->numsides && in; i++) { SplitBrush2(in, b->sides[i].planenum, &front, &back); if (in != a) FreeBrush(in); if (front) { // add to list front->next = out; out = front; } //end if in = back; } //end for if (in) { FreeBrush (in); } //end if else { // didn't really intersect FreeBrushList (out); return a; } //end else return out; } //end of the function SubtractBrush
/* ================ FreeDMapFile ================ */ void FreeDMapFile( void ) { int i, j; FreeBrush( buildBrush ); buildBrush = NULL; // free the entities and brushes for( i = 0; i < dmapGlobals.num_entities; i++ ) { uEntity_t *ent; primitive_t *prim, *nextPrim; ent = &dmapGlobals.uEntities[i]; FreeTree( ent->tree ); // free primitives for( prim = ent->primitives; prim; prim = nextPrim ) { nextPrim = prim->next; if( prim->brush ) { FreeBrush( prim->brush ); } if( prim->tris ) { FreeTriList( prim->tris ); } Mem_Free( prim ); } // free area surfaces if( ent->areas ) { for( j = 0; j < ent->numAreas; j++ ) { uArea_t *area; area = &ent->areas[j]; FreeOptimizeGroupList( area->groups ); } Mem_Free( ent->areas ); } } Mem_Free( dmapGlobals.uEntities ); dmapGlobals.num_entities = 0; // free the map lights for( i = 0; i < dmapGlobals.mapLights.Num(); i++ ) { R_FreeLightDefDerivedData( &dmapGlobals.mapLights[i]->def ); } dmapGlobals.mapLights.DeleteContents( true ); }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bspbrush_t *MergeBrushes(bspbrush_t *brushlist) { int nummerges, merged; bspbrush_t *b1, *b2, *tail, *newbrush, *newbrushlist; bspbrush_t *lastb2; if (!brushlist) return NULL; qprintf("%5d brushes merged", nummerges = 0); do { for (tail = brushlist; tail; tail = tail->next) { if (!tail->next) break; } //end for merged = 0; newbrushlist = NULL; for (b1 = brushlist; b1; b1 = brushlist) { lastb2 = b1; for (b2 = b1->next; b2; b2 = b2->next) { //if the brushes don't have the same contents if (b1->original->contents != b2->original->contents || b1->original->expansionbbox != b2->original->expansionbbox) newbrush = NULL; else newbrush = TryMergeBrushes(b1, b2); if (newbrush) { tail->next = newbrush; lastb2->next = b2->next; brushlist = brushlist->next; FreeBrush(b1); FreeBrush(b2); for (tail = brushlist; tail; tail = tail->next) { if (!tail->next) break; } //end for merged++; qprintf("\r%5d", nummerges++); break; } //end if lastb2 = b2; } //end for //if b1 can't be merged with any of the other brushes if (!b2) { brushlist = brushlist->next; //keep b1 b1->next = newbrushlist; newbrushlist = b1; } //end else } //end for brushlist = newbrushlist; } while(merged); qprintf("\n"); return newbrushlist; } //end of the function MergeBrushes
/* ============ PruneNodes_r ============ */ void PruneNodes_r (node_t *node) { bspbrush_t *b, *next; // portal_t *p, *nextp; // int s; if (node->planenum == PLANENUM_LEAF) return; PruneNodes_r (node->children[0]); PruneNodes_r (node->children[1]); if ( (node->children[0]->contents & CONTENTS_SOLID) && (node->children[1]->contents & CONTENTS_SOLID) ) { if (node->faces) Error ("node->faces seperating CONTENTS_SOLID"); if (node->children[0]->faces || node->children[1]->faces) Error ("!node->faces with children"); // FIXME: free stuff node->pruned = true; node->planenum = PLANENUM_LEAF; node->contents = CONTENTS_SOLID; node->detail_seperator = false; if (node->brushlist) Error ("PruneNodes: node->brushlist"); // combine brush lists node->brushlist = node->children[1]->brushlist; for (b=node->children[0]->brushlist ; b ; b=next) { next = b->next; b->next = node->brushlist; node->brushlist = b; } node->children[0]->brushlist = NULL; node->children[1]->brushlist = NULL; if(node->children[0]->volume != NULL) { FreeBrush(node->children[0]->volume); node->children[0]->volume = NULL; } if(node->children[1]->volume != NULL) { FreeBrush(node->children[1]->volume); node->children[1]->volume = NULL; } c_pruned++; } }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Tree_Free_r( node_t *node ) { // face_t *f, *nextf; bspbrush_t *brush, *nextbrush; //free children if ( node->planenum != PLANENUM_LEAF ) { Tree_Free_r( node->children[0] ); Tree_Free_r( node->children[1] ); } //end if //free bspbrushes // FreeBrushList (node->brushlist); for ( brush = node->brushlist; brush; brush = nextbrush ) { nextbrush = brush->next; #ifdef ME freedtreemem += MemorySize( brush ); #endif //ME FreeBrush( brush ); } //end for node->brushlist = NULL; /* NOTE: only used when creating Q2 bsp // free faces for (f = node->faces; f; f = nextf) { nextf = f->next; #ifdef ME if (f->w) freedtreemem += MemorySize(f->w); freedtreemem += sizeof(face_t); #endif //ME FreeFace(f); } //end for */ // free the node if ( node->volume ) { #ifdef ME freedtreemem += MemorySize( node->volume ); #endif //ME FreeBrush( node->volume ); } //end if if ( numthreads == 1 ) { c_nodes--; } #ifdef ME freedtreemem += MemorySize( node ); #endif //ME FreeMemory( node ); } //end of the function Tree_Free_r
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean CheckPlaneAgainstVolume (int pnum, node_t *node) { bspbrush_t *front, *back; qboolean good; SplitBrush (node->volume, pnum, &front, &back); good = (front && back); if (front) FreeBrush (front); if (back) FreeBrush (back); return good; } //end of the function CheckPlaneAgaintsVolume
/* ==================== FilterBrushIntoTree_r ==================== */ int FilterBrushIntoTree_r( uBrush_t *b, node_t *node ) { uBrush_t *front, *back; int c; if( !b ) { return 0; } // add it to the leaf list if( node->planenum == PLANENUM_LEAF ) { b->next = node->brushlist; node->brushlist = b; // classify the leaf by the structural brush if( b->opaque ) { node->opaque = true; } return 1; } // split it by the node plane SplitBrush( b, node->planenum, &front, &back ); FreeBrush( b ); c = 0; c += FilterBrushIntoTree_r( front, node->children[0] ); c += FilterBrushIntoTree_r( back, node->children[1] ); return c; }
static bool CheckPlaneAgainstVolume (uint16_t pnum, const bspbrush_t* volume) { bspbrush_t* front, *back; bool good; SplitBrush(volume, pnum, &front, &back); good = (front && back); if (front) FreeBrush(front); if (back) FreeBrush(back); return good; }
/* ============= FreeTree_r ============= */ void FreeTree_r( node_t *node ){ face_t *f, *nextf; // free children if ( node->planenum != PLANENUM_LEAF ) { FreeTree_r( node->children[0] ); FreeTree_r( node->children[1] ); } // free bspbrushes FreeBrushList( node->brushlist ); // free faces for ( f = node->faces ; f ; f = nextf ) { nextf = f->next; FreeFace( f ); } // free the node if ( node->volume ) { FreeBrush( node->volume ); } if ( numthreads == 1 ) { c_nodes--; } free( node ); }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Q1_CreateMapBrushes(entity_t *mapent, int modelnum) { bspbrush_t *brushlist, *brush, *nextbrush; int i; //create brushes from the model BSP tree brushlist = Q1_CreateBrushesFromBSP(modelnum); //texture the brushes and split them when necesary brushlist = Q1_TextureBrushes(brushlist, modelnum); //fix the contents textures of all brushes Q1_FixContentsTextures(brushlist); // if (!nobrushmerge) { brushlist = Q1_MergeBrushes(brushlist, modelnum); //brushlist = Q1_MergeBrushes(brushlist, modelnum); } //end if // if (!modelnum) qprintf("converting brushes to map brushes\n"); if (!modelnum) qprintf("%5d brushes", i = 0); for (brush = brushlist; brush; brush = nextbrush) { nextbrush = brush->next; Q1_BSPBrushToMapBrush(brush, mapent); brush->next = NULL; FreeBrush(brush); if (!modelnum) qprintf("\r%5d", ++i); } //end for if (!modelnum) qprintf("\n"); } //end of the function Q1_CreateMapBrushes
static void FreeTree_r (node_t *node) { face_t *f, *nextf; /* free children */ if (node->planenum != PLANENUM_LEAF) { FreeTree_r(node->children[0]); FreeTree_r(node->children[1]); } /* free bspbrushes */ FreeBrushList(node->brushlist); /* free faces */ for (f = node->faces; f; f = nextf) { nextf = f->next; FreeFace(f); } /* free the node */ if (node->volume) FreeBrush(node->volume); if (threadstate.numthreads == 1) c_nodes--; Mem_Free(node); }
static _Bool CheckPlaneAgainstVolume(int32_t pnum, node_t *node) { brush_t *front, *back; _Bool good; SplitBrush(node->volume, pnum, &front, &back); good = (front && back); if (front) { FreeBrush(front); } if (back) { FreeBrush(back); } return good; }
/** * @sa AllocBrush * @sa CountBrushList */ void FreeBrushList (bspbrush_t* brushes) { bspbrush_t* next; for (; brushes; brushes = next) { next = brushes->next; FreeBrush(brushes); } }
/* ================ FreeBrushList ================ */ void FreeBrushList( uBrush_t *brushes ) { uBrush_t *next; for( /**/; brushes; brushes = next ) { next = brushes->next; FreeBrush( brushes ); } }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void FreeBrushList( bspbrush_t *brushes ) { bspbrush_t *next; for ( ; brushes; brushes = next ) { next = brushes->next; FreeBrush( brushes ); } //end for } //end of the function FreeBrushList
//=========================================================================== // Any planes shared with the box edge will be set to no texinfo // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bspbrush_t *ClipBrushToBox(bspbrush_t *brush, vec3_t clipmins, vec3_t clipmaxs) { int i, j; bspbrush_t *front, *back; int p; for (j=0 ; j<2 ; j++) { if (brush->maxs[j] > clipmaxs[j]) { SplitBrush (brush, maxplanenums[j], &front, &back); if (front) FreeBrush (front); brush = back; if (!brush) return NULL; } if (brush->mins[j] < clipmins[j]) { SplitBrush (brush, minplanenums[j], &front, &back); if (back) FreeBrush (back); brush = front; if (!brush) return NULL; } } // remove any colinear faces for (i=0 ; i<brush->numsides ; i++) { p = brush->sides[i].planenum & ~1; if (p == maxplanenums[0] || p == maxplanenums[1] || p == minplanenums[0] || p == minplanenums[1]) { brush->sides[i].texinfo = TEXINFO_NODE; brush->sides[i].flags &= ~SFL_VISIBLE; } } return brush; } //end of the function ClipBrushToBox
/* ===================== FreeBrushsetBrushes ===================== */ void FreeBrushsetBrushes(void) { brush_t *pBrush, *pNext; for (pBrush = brushset->brushes; pBrush; pBrush = pNext) { pNext = pBrush->next; FreeBrushFaces(pBrush->faces); FreeBrush(pBrush); } }
void FreeBrushList( brush_t *brushes ){ brush_t *next; /* walk brush list */ for ( ; brushes != NULL; brushes = next ) { next = brushes->next; FreeBrush( brushes ); } }
/* ==================== FreeBrushList ==================== */ void FreeBrushList( cbspbrush_t *list ) { cbspbrush_t *next; if ( !list ) return; for ( ; list ;list = next ) { next = list->next; FreeBrush( list ); } }
/* ==================== FilterBrushIntoTree_r ==================== */ int FilterBrushIntoTree_r(bspBrush_t * b, node_t * node) { bspBrush_t *front, *back; int c; if(!b) { return 0; } // add it to the leaf list if(node->planenum == PLANENUM_LEAF) { b->next = node->brushlist; node->brushlist = b; // classify the leaf by the structural brush if(!b->detail) { if(b->opaque) { node->opaque = qtrue; node->areaportal = qfalse; } else if(b->contents & CONTENTS_AREAPORTAL) { if(!node->opaque) { node->areaportal = qtrue; } } } return 1; } // split it by the node plane SplitBrush(b, node->planenum, &front, &back); FreeBrush(b); c = 0; c += FilterBrushIntoTree_r(front, node->children[0]); c += FilterBrushIntoTree_r(back, node->children[1]); return c; }
int FilterBrushIntoTree_r( brush_t *b, node_t *node ) { brush_t *front, *back; int c; /* dummy check */ if( b == NULL ) return 0; /* add it to the leaf list */ if( node->planenum == PLANENUM_LEAF ) { /* something somewhere is hammering brushlist */ b->next = node->brushlist; node->brushlist = b; /* classify the leaf by the structural brush */ if( !b->detail ) { if( b->opaque ) { node->opaque = qtrue; node->areaportal = qfalse; } else if( b->compileFlags & C_AREAPORTAL ) { if( !node->opaque ) node->areaportal = qtrue; } } return 1; } /* split it by the node plane */ c = b->numsides; SplitBrush( b, node->planenum, &front, &back ); FreeBrush( b ); c = 0; c += FilterBrushIntoTree_r( front, node->children[ 0 ] ); c += FilterBrushIntoTree_r( back, node->children[ 1 ] ); return c; }
//=========================================================================== // Builds a new list that doesn't hold the given brush // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bspbrush_t *CullList( bspbrush_t *list, bspbrush_t *skip1 ) { bspbrush_t *newlist; bspbrush_t *next; newlist = NULL; for ( ; list ; list = next ) { next = list->next; if ( list == skip1 ) { FreeBrush( list ); continue; } list->next = newlist; newlist = list; } return newlist; } //end of the function CullList
/* ============= FreeTree_r ============= */ void FreeTree_r (node_t *node) { // free children if (node->planenum != PLANENUM_LEAF) { FreeTree_r (node->children[0]); FreeTree_r (node->children[1]); } // free bspbrushes FreeBrushList (node->brushlist); // free the node if (node->volume) FreeBrush (node->volume); if (numthreads == 1) c_nodes--; free (node); }
/** * @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]; }
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]; }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bspbrush_t *Q1_TextureBrushes(bspbrush_t *brushlist, int modelnum) { float area, largestarea; int i, n, texinfonum, sn, numbrushes, ofs; int bestfacenum, sidenodenum; side_t *side; q1_dmiptexlump_t *miptexlump; q1_miptex_t *miptex; bspbrush_t *brush, *nextbrush, *prevbrush, *newbrushes, *brushlistend; vec_t defaultvec[4] = {1, 0, 0, 0}; if (!modelnum) qprintf("texturing brushes\n"); if (!modelnum) qprintf("%5d brushes", numbrushes = 0); //get a pointer to the last brush in the list for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next) { if (!brushlistend->next) break; } //end for //there's no previous brush when at the start of the list prevbrush = NULL; //go over the brush list for (brush = brushlist; brush; brush = nextbrush) { nextbrush = brush->next; //find a texinfo for every brush side for (sn = 0; sn < brush->numsides; sn++) { side = &brush->sides[sn]; // if (side->flags & SFL_TEXTURED) continue; //number of the node that created this brush side sidenodenum = side->surf; //see midwinding in Q1_SplitBrush //no face found yet bestfacenum = -1; //minimum face size largestarea = 1; //if optimizing the texture placement and not going for the //least number of brushes if (!lessbrushes) { for (i = 0; i < q1_numfaces; i++) { //the face must be in the same plane as the node plane that created //this brush side if (q1_dfaces[i].planenum == q1_dnodes[sidenodenum].planenum) { //get the area the face and the brush side overlap area = Q1_FaceOnWinding(&q1_dfaces[i], side->winding); //if this face overlaps the brush side winding more than previous faces if (area > largestarea) { //if there already was a face for texturing this brush side with //a different texture if (bestfacenum >= 0 && (q1_dfaces[bestfacenum].texinfo != q1_dfaces[i].texinfo)) { //split the brush to fit the texture newbrushes = Q1_SplitBrushWithFace(brush, &q1_dfaces[i]); //if new brushes where created if (newbrushes) { //remove the current brush from the list if (prevbrush) prevbrush->next = brush->next; else brushlist = brush->next; if (brushlistend == brush) { brushlistend = prevbrush; nextbrush = newbrushes; } //end if //add the new brushes to the end of the list if (brushlistend) brushlistend->next = newbrushes; else brushlist = newbrushes; //free the current brush FreeBrush(brush); //don't forget about the prevbrush pointer at the bottom of //the outer loop brush = prevbrush; //find the end of the list for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next) { if (!brushlistend->next) break; } //end for break; } //end if else { Log_Write("brush %d: no real texture split", numbrushes); } //end else } //end if else { //best face for texturing this brush side bestfacenum = i; } //end else } //end if } //end if } //end for //if the brush was split the original brush is removed //and we just continue with the next one in the list if (i < q1_numfaces) break; } //end if else { //find the face with the largest overlap with this brush side //for texturing the brush side for (i = 0; i < q1_numfaces; i++) { //the face must be in the same plane as the node plane that created //this brush side if (q1_dfaces[i].planenum == q1_dnodes[sidenodenum].planenum) { //get the area the face and the brush side overlap area = Q1_FaceOnWinding(&q1_dfaces[i], side->winding); //if this face overlaps the brush side winding more than previous faces if (area > largestarea) { largestarea = area; bestfacenum = i; } //end if } //end if } //end for } //end else //if a face was found for texturing this brush side if (bestfacenum >= 0) { //set the MAP texinfo values texinfonum = q1_dfaces[bestfacenum].texinfo; for (n = 0; n < 4; n++) { map_texinfo[texinfonum].vecs[0][n] = q1_texinfo[texinfonum].vecs[0][n]; map_texinfo[texinfonum].vecs[1][n] = q1_texinfo[texinfonum].vecs[1][n]; } //end for //make sure the two vectors aren't of zero length otherwise use the default //vector to prevent a divide by zero in the map writing if (VectorLength(map_texinfo[texinfonum].vecs[0]) < 0.01) memcpy(map_texinfo[texinfonum].vecs[0], defaultvec, sizeof(defaultvec)); if (VectorLength(map_texinfo[texinfonum].vecs[1]) < 0.01) memcpy(map_texinfo[texinfonum].vecs[1], defaultvec, sizeof(defaultvec)); // map_texinfo[texinfonum].flags = q1_texinfo[texinfonum].flags; map_texinfo[texinfonum].value = 0; //Q1 and HL texinfos don't have a value //the mip texture miptexlump = (q1_dmiptexlump_t *) q1_dtexdata; ofs = miptexlump->dataofs[q1_texinfo[texinfonum].miptex]; if ( ofs > q1_texdatasize ) { ofs = miptexlump->dataofs[0]; } miptex = (q1_miptex_t *)((byte *)miptexlump + ofs); //get the mip texture name strcpy(map_texinfo[texinfonum].texture, miptex->name); //no animations in Quake1 and Half-Life mip textures map_texinfo[texinfonum].nexttexinfo = -1; //store the texinfo number side->texinfo = texinfonum; // if (texinfonum > map_numtexinfo) map_numtexinfo = texinfonum; //this side is textured side->flags |= SFL_TEXTURED; } //end if else { //no texture for this side side->texinfo = TEXINFO_NODE; //this side is textured side->flags |= SFL_TEXTURED; } //end if } //end for // if (!modelnum && prevbrush != brush) qprintf("\r%5d", ++numbrushes); //previous brush in the list prevbrush = brush; } //end for if (!modelnum) qprintf("\n"); //return the new list with brushes return brushlist; } //end of the function Q1_TextureBrushes