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); }
/* ============= 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 ); }
//=========================================================================== // 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
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== tree_t *ProcessWorldBrushes( int brush_start, int brush_end ) { bspbrush_t *brushes; tree_t *tree; node_t *node; vec3_t mins, maxs; //take the whole world mins[0] = map_mins[0] - 8; mins[1] = map_mins[1] - 8; mins[2] = map_mins[2] - 8; maxs[0] = map_maxs[0] + 8; maxs[1] = map_maxs[1] + 8; maxs[2] = map_maxs[2] + 8; //reset the brush bsp ResetBrushBSP(); // the makelist and chopbrushes could be cached between the passes... //create a list with brushes that are within the given mins/maxs //some brushes will be cut and only the part that falls within the //mins/maxs will be in the bush list brushes = MakeBspBrushList( brush_start, brush_end, mins, maxs ); // if ( !brushes ) { node = AllocNode(); node->planenum = PLANENUM_LEAF; node->contents = CONTENTS_SOLID; tree = Tree_Alloc(); tree->headnode = node; VectorCopy( mins, tree->mins ); VectorCopy( maxs, tree->maxs ); } //end if else { //Carves any intersecting solid brushes into the minimum number //of non-intersecting brushes. if ( !nocsg ) { brushes = ChopBrushes( brushes ); /* if (create_aas) { brushes = MergeBrushes(brushes); } //end if*/ } //end if //if the conversion is cancelled if ( cancelconversion ) { FreeBrushList( brushes ); return NULL; } //end if //create the actual bsp tree tree = BrushBSP( brushes, mins, maxs ); } //end else //return the tree return tree; } //end of the function ProcessWorldBrushes
/* ================ BuildTree_r ================ */ node_t *BuildTree_r (node_t *node, bspbrush_t *brushes) { node_t *newnode; side_t *bestside; int i; bspbrush_t *children[2]; if (numthreads == 1) c_nodes++; if (drawflag) DrawBrushList (brushes, node); // find the best plane to use as a splitter bestside = SelectSplitSide (brushes, node); if (!bestside) { // leaf node node->side = NULL; node->planenum = -1; LeafNode (node, brushes); return node; } // this is a splitplane node node->side = bestside; node->planenum = bestside->planenum & ~1; // always use front facing SplitBrushList (brushes, node, &children[0], &children[1]); FreeBrushList (brushes); // allocate children before recursing for (i=0 ; i<2 ; i++) { newnode = AllocNode (); newnode->parent = node; node->children[i] = newnode; } SplitBrush (node->volume, node->planenum, &node->children[0]->volume, &node->children[1]->volume); // recursively process children for (i=0 ; i<2 ; i++) { node->children[i] = BuildTree_r (node->children[i], children[i]); } return node; }
/* ============= 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 brushes FreeBrushList (node->brushlist); // free the node c_nodes--; Mem_Free (node); }
static node_t *BuildTree_r (node_t *node, bspbrush_t *brushes) { node_t *newnode; side_t *bestside; int i; bspbrush_t *children[2]; if (threadstate.numthreads == 1) c_nodes++; /* find the best plane to use as a splitter */ bestside = SelectSplitSide(brushes, node->volume); if (!bestside) { /* leaf node */ LeafNode(node, brushes); Verb_Printf(VERB_DUMP, "BuildTree_r: Created a leaf node.\n"); return node; } /* make sure the selected plane hasn't been used before. */ CheckPlaneAgainstParents(bestside->planenum, node); Verb_Printf(VERB_DUMP, "BuildTree_r: splitting along plane %i\n", (int)bestside->planenum); /* this is a splitplane node */ node->side = bestside; node->planenum = bestside->planenum & ~1; /* always use front facing */ SplitBrushList(brushes, node->planenum, &children[0], &children[1]); FreeBrushList(brushes); /* allocate children before recursing */ for (i = 0; i < 2; i++) { newnode = AllocNode(); newnode->parent = node; node->children[i] = newnode; } SplitBrush(node->volume, node->planenum, &node->children[0]->volume, &node->children[1]->volume); /* recursively process children */ for (i = 0; i < 2; i++) { node->children[i] = BuildTree_r(node->children[i], children[i]); } return node; }
//----------------------------------------------------------------------------- // Purpose: Build faces for the detail brushes and merge them into the BSP // Input : *worldtree - // brush_start - // brush_end - //----------------------------------------------------------------------------- face_t *MergeDetailTree( tree_t *worldtree, int brush_start, int brush_end ) { int start; bspbrush_t *detailbrushes = NULL; tree_t *detailtree = NULL; face_t *pFaces = NULL; face_t *pLeafFaceList = NULL; // Grab the list of detail brushes detailbrushes = MakeBspBrushList (brush_start, brush_end, map_mins, map_maxs, ONLY_DETAIL ); if (detailbrushes) { start = Plat_FloatTime(); Msg("Chop Details..."); // if there are detail brushes, chop them against each other if (!nocsg) detailbrushes = ChopBrushes (detailbrushes); Msg("done (%d)\n", (int)(Plat_FloatTime() - start) ); // Now mark the visible sides so we can eliminate all detail brush sides // that are covered by other detail brush sides // NOTE: This still leaves detail brush sides that are covered by the world. (these are removed in the merge operation) Msg("Find Visible Detail Sides..."); pFaces = ComputeVisibleBrushSides( detailbrushes ); TryMergeFaceList( &pFaces ); SubdivideFaceList( &pFaces ); Msg("done (%d)\n", (int)(Plat_FloatTime() - start) ); start = Plat_FloatTime(); Msg("Merging details..."); // Merge the detail solids and faces into the world tree // Merge all of the faces into the world tree pLeafFaceList = FilterFacesIntoTree( worldtree, pFaces ); FilterBrushesIntoTree( worldtree, detailbrushes ); FreeFaceList( pFaces ); FreeBrushList(detailbrushes); Msg("done (%d)\n", (int)(Plat_FloatTime() - start) ); } return pLeafFaceList; }
/* ============= 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); }
//=========================================================================== // returns a list with brushes created by splitting the given brush with // planes that go through the face edges and are orthogonal to the face plane // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bspbrush_t *Q1_SplitBrushWithFace(bspbrush_t *brush, q1_dface_t *face) { int i, edgenum, side, planenum, splits; float dist; q1_dplane_t plane; vec_t *v1, *v2; vec3_t normal, edgevec; bspbrush_t *front, *back, *brushlist; memcpy(&plane, &q1_dplanes[face->planenum], sizeof(q1_dplane_t)); //check on which side of the plane the face is if (face->side) { VectorNegate(plane.normal, plane.normal); plane.dist = -plane.dist; } //end if splits = 0; brushlist = NULL; for (i = 0; i < face->numedges; i++) { //get the first and second vertex of the edge edgenum = q1_dsurfedges[face->firstedge + i]; side = edgenum > 0; //if the face plane is flipped v1 = q1_dvertexes[q1_dedges[abs(edgenum)].v[side]].point; v2 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point; //create a plane through the edge vector, orthogonal to the face plane //and with the normal vector pointing out of the face VectorSubtract(v1, v2, edgevec); CrossProduct(edgevec, plane.normal, normal); VectorNormalize(normal); dist = DotProduct(normal, v1); // planenum = FindFloatPlane(normal, dist); //split the current brush SplitBrush(brush, planenum, &front, &back); //if there is a back brush just put it in the list if (back) { //copy the brush contents back->side = brush->side; // back->next = brushlist; brushlist = back; splits++; } //end if if (!front) { Log_Print("Q1_SplitBrushWithFace: no new brush\n"); FreeBrushList(brushlist); return NULL; } //end if //copy the brush contents front->side = brush->side; //continue splitting the front brush brush = front; } //end for if (!splits) { FreeBrush(front); return NULL; } //end if front->next = brushlist; brushlist = front; return brushlist; } //end of the function Q1_SplitBrushWithFace
//=========================================================================== // Carves any intersecting solid brushes into the minimum number // of non-intersecting brushes. // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bspbrush_t *ChopBrushes (bspbrush_t *head) { bspbrush_t *b1, *b2, *next; bspbrush_t *tail; bspbrush_t *keep; bspbrush_t *sub, *sub2; int c1, c2; int num_csg_iterations; Log_Print("-------- Brush CSG ---------\n"); Log_Print("%6d original brushes\n", CountBrushList (head)); num_csg_iterations = 0; qprintf("%6d output brushes", num_csg_iterations); #if 0 if (startbrush == 0) WriteBrushList ("before.gl", head, false); #endif keep = NULL; newlist: // find tail if (!head) return NULL; for (tail = head; tail->next; tail = tail->next) ; for (b1=head ; b1 ; b1=next) { next = b1->next; //if the conversion is cancelled if (cancelconversion) { b1->next = keep; keep = b1; continue; } //end if for (b2 = b1->next; b2; b2 = b2->next) { if (BrushesDisjoint (b1, b2)) continue; sub = NULL; sub2 = NULL; c1 = 999999; c2 = 999999; if (BrushGE (b2, b1)) { sub = SubtractBrush (b1, b2); if (sub == b1) { continue; // didn't really intersect } //end if if (!sub) { // b1 is swallowed by b2 head = CullList (b1, b1); goto newlist; } c1 = CountBrushList (sub); } if ( BrushGE (b1, b2) ) { sub2 = SubtractBrush (b2, b1); if (sub2 == b2) continue; // didn't really intersect if (!sub2) { // b2 is swallowed by b1 FreeBrushList (sub); head = CullList (b1, b2); goto newlist; } c2 = CountBrushList (sub2); } if (!sub && !sub2) continue; // neither one can bite // only accept if it didn't fragment // (commenting this out allows full fragmentation) if (c1 > 1 && c2 > 1) { if (sub2) FreeBrushList (sub2); if (sub) FreeBrushList (sub); continue; } if (c1 < c2) { if (sub2) FreeBrushList (sub2); tail = AddBrushListToTail (sub, tail); head = CullList (b1, b1); goto newlist; } //end if else { if (sub) FreeBrushList (sub); tail = AddBrushListToTail (sub2, tail); head = CullList (b1, b2); goto newlist; } //end else } //end for if (!b2) { // b1 is no longer intersecting anything, so keep it b1->next = keep; keep = b1; } //end if num_csg_iterations++; qprintf("\r%6d", num_csg_iterations); } //end for if (cancelconversion) return keep; // qprintf("\n"); Log_Write("%6d output brushes\r\n", num_csg_iterations); #if 0 { WriteBrushList ("after.gl", keep, false); WriteBrushMap ("after.map", keep); } #endif return keep; } //end of the function ChopBrushes
//thread function, gets nodes from the nodelist and processes them void BuildTreeThread( int threadid ) { node_t *newnode, *node; side_t *bestside; int i, totalmem; bspbrush_t *brushes; for ( node = NextNodeFromList(); node; ) { //if the nodelist isn't empty try to add another thread //if (NodeListSize() > 10) AddThread(BuildTreeThread); //display the number of nodes processed so far if ( numthreads == 1 ) { IncreaseNodeCounter(); } brushes = node->brushlist; if ( numthreads == 1 ) { totalmem = WindingMemory() + c_nodememory + c_brushmemory; if ( totalmem > c_peak_totalbspmemory ) { c_peak_totalbspmemory = totalmem; } //end if c_nodes++; } //endif if ( drawflag ) { DrawBrushList( brushes, node ); } //end if if ( cancelconversion ) { bestside = NULL; } //end if else { // find the best plane to use as a splitter bestside = SelectSplitSide( brushes, node ); } //end else //if there's no split side left if ( !bestside ) { //create a leaf out of the node LeafNode( node, brushes ); if ( node->contents & CONTENTS_SOLID ) { c_solidleafnodes++; } if ( create_aas ) { //free up memory!!! FreeBrushList( node->brushlist ); node->brushlist = NULL; } //end if //free the node volume brush (it is not used anymore) if ( node->volume ) { FreeBrush( node->volume ); node->volume = NULL; } //end if node = NextNodeFromList(); continue; } //end if // this is a splitplane node node->side = bestside; node->planenum = bestside->planenum & ~1; //always use front facing //allocate children for ( i = 0; i < 2; i++ ) { newnode = AllocNode(); newnode->parent = node; node->children[i] = newnode; } //end for //split the brush list in two for both children SplitBrushList( brushes, node, &node->children[0]->brushlist, &node->children[1]->brushlist ); CheckBrushLists( node->children[0]->brushlist, node->children[1]->brushlist ); //free the old brush list FreeBrushList( brushes ); node->brushlist = NULL; //split the volume brush of the node for the children SplitBrush( node->volume, node->planenum, &node->children[0]->volume, &node->children[1]->volume ); if ( !node->children[0]->volume || !node->children[1]->volume ) { Error( "child without volume brush" ); } //end if //free the volume brush if ( node->volume ) { FreeBrush( node->volume ); node->volume = NULL; } //end if //add both children to the node list //AddNodeToList(node->children[0]); AddNodeToList( node->children[1] ); node = node->children[0]; } //end while RemoveThread( threadid ); } //end of the function BuildTreeThread
node_t *BuildTree_r( node_t *node, bspbrush_t *brushes ) { node_t *newnode; side_t *bestside; int i, totalmem; bspbrush_t *children[2]; qprintf( "\r%6d", numrecurse ); numrecurse++; if ( numthreads == 1 ) { totalmem = WindingMemory() + c_nodememory + c_brushmemory; if ( totalmem > c_peak_totalbspmemory ) { c_peak_totalbspmemory = totalmem; } c_nodes++; } //endif if ( drawflag ) { DrawBrushList( brushes, node ); } // find the best plane to use as a splitter bestside = SelectSplitSide( brushes, node ); if ( !bestside ) { // leaf node node->side = NULL; node->planenum = -1; LeafNode( node, brushes ); if ( node->contents & CONTENTS_SOLID ) { c_solidleafnodes++; } if ( create_aas ) { //free up memory!!! FreeBrushList( node->brushlist ); node->brushlist = NULL; //free the node volume brush if ( node->volume ) { FreeBrush( node->volume ); node->volume = NULL; } //end if } //end if return node; } //end if // this is a splitplane node node->side = bestside; node->planenum = bestside->planenum & ~1; // always use front facing //split the brush list in two for both children SplitBrushList( brushes, node, &children[0], &children[1] ); //free the old brush list FreeBrushList( brushes ); // allocate children before recursing for ( i = 0; i < 2; i++ ) { newnode = AllocNode(); newnode->parent = node; node->children[i] = newnode; } //end for //split the volume brush of the node for the children SplitBrush( node->volume, node->planenum, &node->children[0]->volume, &node->children[1]->volume ); if ( create_aas ) { //free the volume brush if ( node->volume ) { FreeBrush( node->volume ); node->volume = NULL; } //end if } //end if // recursively process children for ( i = 0; i < 2; i++ ) { node->children[i] = BuildTree_r( node->children[i], children[i] ); } //end for return node; } //end of the function BuildTree_r
/* ================= ChopBrushes Carves any intersecting solid brushes into the minimum number of non-intersecting brushes. ================= */ bspbrush_t *ChopBrushes (bspbrush_t *head) { bspbrush_t *b1, *b2, *next; bspbrush_t *tail; bspbrush_t *keep; bspbrush_t *sub, *sub2; int c1, c2; Sys_FPrintf( SYS_VRB, "---- ChopBrushes ----\n"); Sys_FPrintf( SYS_VRB, "original brushes: %i\n", CountBrushList (head)); #if 0 if (startbrush == 0) WriteBrushList ("before.gl", head, false); #endif keep = NULL; newlist: // find tail if (!head) return NULL; for (tail=head ; tail->next ; tail=tail->next) ; for (b1=head ; b1 ; b1=next) { next = b1->next; for (b2=b1->next ; b2 ; b2 = b2->next) { if (BrushesDisjoint (b1, b2)) continue; sub = NULL; sub2 = NULL; c1 = 999999; c2 = 999999; if ( BrushGE (b2, b1) ) { sub = SubtractBrush (b1, b2); if (sub == b1) continue; // didn't really intersect if (!sub) { // b1 is swallowed by b2 head = CullList (b1, b1); goto newlist; } c1 = CountBrushList (sub); } if ( BrushGE (b1, b2) ) { sub2 = SubtractBrush (b2, b1); if (sub2 == b2) continue; // didn't really intersect if (!sub2) { // b2 is swallowed by b1 FreeBrushList (sub); head = CullList (b1, b2); goto newlist; } c2 = CountBrushList (sub2); } if (!sub && !sub2) continue; // neither one can bite // only accept if it didn't fragment // (commening this out allows full fragmentation) if (c1 > 1 && c2 > 1) { if (sub2) FreeBrushList (sub2); if (sub) FreeBrushList (sub); continue; } if (c1 < c2) { if (sub2) FreeBrushList (sub2); tail = AddBrushListToTail (sub, tail); head = CullList (b1, b1); goto newlist; } else { if (sub) FreeBrushList (sub); tail = AddBrushListToTail (sub2, tail); head = CullList (b1, b2); goto newlist; } } if (!b2) { // b1 is no longer intersecting anything, so keep it b1->next = keep; keep = b1; } } Sys_FPrintf( SYS_VRB, "output brushes: %i\n", CountBrushList (keep)); #if 0 { WriteBrushList ("after.gl", keep, false); WriteBrushMap ("after.map", keep); } #endif return keep; }