//#pragma warning(disable: 4018) // '<' : signed/unsigned mismatch #include "bsp5.h" // FaceSide // ChooseMidPlaneFromList // ChoosePlaneFromList // SelectPartition // CalcSurfaceInfo // DivideSurface // SplitNodeSurfaces // RankForContents // ContentsForRank // FreeLeafSurfs // LinkLeafFaces // MakeNodePortal // SplitNodePortals // CalcNodeBounds // CopyFacesToNode // BuildBspTree_r // SolidBSP // Each node or leaf will have a set of portals that completely enclose // the volume of the node and pass into an adjacent node. #ifdef HLBSP_FAST_SELECTPARTITION #include #endif int g_maxnode_size = DEFAULT_MAXNODE_SIZE; static bool g_reportProgress = false; static int g_numProcessed = 0; static int g_numReported = 0; static void ResetStatus(bool report_progress) { g_reportProgress = report_progress; g_numProcessed = g_numReported = 0; } static void UpdateStatus(void) { if(g_reportProgress) { ++g_numProcessed; if((g_numProcessed / 500) > g_numReported) { g_numReported = (g_numProcessed / 500); Log("%d...",g_numProcessed); } } } // ===================================================================================== // FaceSide // For BSP hueristic // ===================================================================================== static int FaceSide(face_t* in, const dplane_t* const split #ifdef HLBSP_AVOIDEPSILONSPLIT , double *epsilonsplit = NULL #endif ) { #ifdef HLBSP_AVOIDEPSILONSPLIT const vec_t epsilonmin = 0.002, epsilonmax = 0.2; vec_t d_front, d_back; #else int frontcount, backcount; #endif vec_t dot; int i; vec_t* p; #ifdef HLBSP_AVOIDEPSILONSPLIT d_front = d_back = 0; #else frontcount = backcount = 0; #endif // axial planes are fast if (split->type <= last_axial) { vec_t splitGtEp = split->dist + ON_EPSILON; // Invariant moved out of loop vec_t splitLtEp = split->dist - ON_EPSILON; // Invariant moved out of loop for (i = 0, p = in->pts[0] + split->type; i < in->numpoints; i++, p += 3) { #ifdef HLBSP_AVOIDEPSILONSPLIT dot = *p - split->dist; if (dot > d_front) d_front = dot; if (dot < d_back) d_back = dot; #else if (*p > splitGtEp) { if (backcount) { return SIDE_ON; } frontcount = 1; } else if (*p < splitLtEp) { if (frontcount) { return SIDE_ON; } backcount = 1; } #endif } } else { // sloping planes take longer for (i = 0, p = in->pts[0]; i < in->numpoints; i++, p += 3) { dot = DotProduct(p, split->normal); dot -= split->dist; #ifdef HLBSP_AVOIDEPSILONSPLIT if (dot > d_front) d_front = dot; if (dot < d_back) d_back = dot; #else if (dot > ON_EPSILON) { if (backcount) { return SIDE_ON; } frontcount = 1; } else if (dot < -ON_EPSILON) { if (frontcount) { return SIDE_ON; } backcount = 1; } #endif } } #ifdef HLBSP_AVOIDEPSILONSPLIT if (d_front <= ON_EPSILON) { if (d_front > epsilonmin || d_back > -epsilonmax) { if (epsilonsplit) (*epsilonsplit)++; } return SIDE_BACK; } if (d_back >= -ON_EPSILON) { if (d_back < -epsilonmin || d_front < epsilonmax) { if (epsilonsplit) (*epsilonsplit)++; } return SIDE_FRONT; } if (d_front < epsilonmax || d_back > -epsilonmax) { if (epsilonsplit) (*epsilonsplit)++; } return SIDE_ON; #else if (!frontcount) { return SIDE_BACK; } if (!backcount) { return SIDE_FRONT; } return SIDE_ON; #endif } #ifdef HLBSP_FAST_SELECTPARTITION // organize all surfaces into a tree structure to accelerate intersection test // can reduce more than 90% compile time for very complicated maps typedef struct surfacetreenode_s { int size; // can be zero, which invalidates mins and maxs #ifdef HLCSG_HLBSP_SOLIDHINT int size_discardable; #endif vec3_t mins; vec3_t maxs; bool isleaf; // node surfacetreenode_s *children[2]; std::vector< face_t * > *nodefaces; #ifdef HLCSG_HLBSP_SOLIDHINT int nodefaces_discardablesize; #endif // leaf std::vector< face_t * > *leaffaces; } surfacetreenode_t; typedef struct { bool dontbuild; vec_t epsilon; // if a face is not epsilon far from the splitting plane, put it in result.middle surfacetreenode_t *headnode; struct { int frontsize; int backsize; std::vector< face_t * > *middle; // may contains coplanar faces and discardable(SOLIDHINT) faces } result; // "public" } surfacetree_t; void BuildSurfaceTree_r (surfacetree_t *tree, surfacetreenode_t *node) { node->size = node->leaffaces->size (); #ifdef HLCSG_HLBSP_SOLIDHINT node->size_discardable = 0; #endif if (node->size == 0) { node->isleaf = true; return; } VectorFill (node->mins, BOGUS_RANGE); VectorFill (node->maxs, -BOGUS_RANGE); for (std::vector< face_t * >::iterator i = node->leaffaces->begin (); i != node->leaffaces->end (); ++i) { face_t *f = *i; for (int x = 0; x < f->numpoints; x++) { VectorCompareMinimum (node->mins, f->pts[x], node->mins); VectorCompareMaximum (node->maxs, f->pts[x], node->maxs); } #ifdef HLCSG_HLBSP_SOLIDHINT if (f->facestyle == face_discardable) { node->size_discardable++; } #endif } int bestaxis = -1; { vec_t bestdelta = 0; for (int k = 0; k < 3; k++) { if (node->maxs[k] - node->mins[k] > bestdelta + ON_EPSILON) { bestaxis = k; bestdelta = node->maxs[k] - node->mins[k]; } } } if (node->size <= 5 || tree->dontbuild == true || bestaxis == -1) { node->isleaf = true; return; } node->isleaf = false; vec_t dist, dist1, dist2; dist = (node->mins[bestaxis] + node->maxs[bestaxis]) / 2; dist1 = (3 * node->mins[bestaxis] + node->maxs[bestaxis]) / 4; dist2 = (node->mins[bestaxis] + 3 * node->maxs[bestaxis]) / 4; // Each child node is at most 3/4 the size of the parent node. // Most faces should be passed to a child node, faces left in the parent node are the ones whose dimensions are large enough to be comparable to the dimension of the parent node. node->nodefaces = new std::vector< face_t * >; #ifdef HLCSG_HLBSP_SOLIDHINT node->nodefaces_discardablesize = 0; #endif node->children[0] = (surfacetreenode_t *)malloc (sizeof (surfacetreenode_t)); node->children[0]->leaffaces = new std::vector< face_t * >; node->children[1] = (surfacetreenode_t *)malloc (sizeof (surfacetreenode_t)); node->children[1]->leaffaces = new std::vector< face_t * >; for (std::vector< face_t * >::iterator i = node->leaffaces->begin (); i != node->leaffaces->end (); ++i) { face_t *f = *i; vec_t low = BOGUS_RANGE; vec_t high = -BOGUS_RANGE; for (int x = 0; x < f->numpoints; x++) { low = qmin (low, f->pts[x][bestaxis]); high = qmax (high, f->pts[x][bestaxis]); } if (low < dist1 + ON_EPSILON && high > dist2 - ON_EPSILON) { node->nodefaces->push_back (f); #ifdef HLCSG_HLBSP_SOLIDHINT if (f->facestyle == face_discardable) { node->nodefaces_discardablesize++; } #endif } else if (low >= dist1 && high <= dist2) { if ((low + high) / 2 > dist) { node->children[0]->leaffaces->push_back (f); } else { node->children[1]->leaffaces->push_back (f); } } else if (low >= dist1) { node->children[0]->leaffaces->push_back (f); } else if (high <= dist2) { node->children[1]->leaffaces->push_back (f); } } if (node->children[0]->leaffaces->size () == node->leaffaces->size () || node->children[1]->leaffaces->size () == node->leaffaces->size ()) { Warning ("BuildSurfaceTree_r: didn't split node with bound (%f,%f,%f)-(%f,%f,%f)", node->mins[0], node->mins[1], node->mins[2], node->maxs[0], node->maxs[1], node->maxs[2]); delete node->children[0]->leaffaces; delete node->children[1]->leaffaces; free (node->children[0]); free (node->children[1]); delete node->nodefaces; node->isleaf = true; return; } delete node->leaffaces; BuildSurfaceTree_r (tree, node->children[0]); BuildSurfaceTree_r (tree, node->children[1]); } surfacetree_t *BuildSurfaceTree (surface_t *surfaces, vec_t epsilon) { surfacetree_t *tree; tree = (surfacetree_t *)malloc (sizeof (surfacetree_t)); tree->epsilon = epsilon; tree->result.middle = new std::vector< face_t * >; tree->headnode = (surfacetreenode_t *)malloc (sizeof (surfacetreenode_t)); tree->headnode->leaffaces = new std::vector< face_t * >; { surface_t *p2; face_t *f; for (p2 = surfaces; p2; p2 = p2->next) { if (p2->onnode) { continue; } for (f = p2->faces; f; f = f->next) { tree->headnode->leaffaces->push_back (f); } } } tree->dontbuild = tree->headnode->leaffaces->size () < 20; BuildSurfaceTree_r (tree, tree->headnode); if (tree->dontbuild) { *tree->result.middle = *tree->headnode->leaffaces; tree->result.backsize = 0; tree->result.frontsize = 0; } return tree; } void TestSurfaceTree_r (surfacetree_t *tree, const surfacetreenode_t *node, const dplane_t *split) { if (node->size == 0) { return; } vec_t low, high; low = high = -split->dist; for (int k = 0; k < 3; k++) { if (split->normal[k] >= 0) { high += split->normal[k] * node->maxs[k]; low += split->normal[k] * node->mins[k]; } else { high += split->normal[k] * node->mins[k]; low += split->normal[k] * node->maxs[k]; } } if (low > tree->epsilon) { tree->result.frontsize += node->size; #ifdef HLCSG_HLBSP_SOLIDHINT tree->result.frontsize -= node->size_discardable; #endif return; } if (high < -tree->epsilon) { tree->result.backsize += node->size; #ifdef HLCSG_HLBSP_SOLIDHINT tree->result.backsize -= node->size_discardable; #endif return; } if (node->isleaf) { for (std::vector< face_t * >::iterator i = node->leaffaces->begin (); i != node->leaffaces->end (); ++i) { tree->result.middle->push_back (*i); } } else { for (std::vector< face_t * >::iterator i = node->nodefaces->begin (); i != node->nodefaces->end (); ++i) { tree->result.middle->push_back (*i); } TestSurfaceTree_r (tree, node->children[0], split); TestSurfaceTree_r (tree, node->children[1], split); } } void TestSurfaceTree (surfacetree_t *tree, const dplane_t *split) { if (tree->dontbuild) { return; } tree->result.middle->clear (); tree->result.backsize = 0; tree->result.frontsize = 0; TestSurfaceTree_r (tree, tree->headnode, split); } void DeleteSurfaceTree_r (surfacetreenode_t *node) { if (node->isleaf) { delete node->leaffaces; } else { DeleteSurfaceTree_r (node->children[0]); free (node->children[0]); DeleteSurfaceTree_r (node->children[1]); free (node->children[1]); delete node->nodefaces; } } void DeleteSurfaceTree (surfacetree_t *tree) { DeleteSurfaceTree_r (tree->headnode); free (tree->headnode); delete tree->result.middle; free (tree); } #endif // ===================================================================================== // ChooseMidPlaneFromList // When there are a huge number of planes, just choose one closest // to the middle. // ===================================================================================== static surface_t* ChooseMidPlaneFromList(surface_t* surfaces, const vec3_t mins, const vec3_t maxs #ifdef ZHLT_DETAILBRUSH , int detaillevel #endif ) { int j, l; surface_t* p; surface_t* bestsurface; vec_t bestvalue; vec_t value; vec_t dist; dplane_t* plane; #ifdef HLBSP_CHOOSEMIDPLANE surfacetree_t* surfacetree; std::vector< face_t * >::iterator it; face_t* f; surfacetree = BuildSurfaceTree (surfaces, ON_EPSILON); #endif // // pick the plane that splits the least // #ifdef HLBSP_CHOOSEMIDPLANE bestvalue = 9e30; #else #ifdef ZHLT_LARGERANGE bestvalue = 6.0f * BOGUS_RANGE * BOGUS_RANGE; #else bestvalue = 6 * 8192 * 8192; #endif #endif bestsurface = NULL; for (p = surfaces; p; p = p->next) { if (p->onnode) { continue; } #ifdef ZHLT_DETAILBRUSH if (p->detaillevel != detaillevel) { continue; } #endif plane = &g_dplanes[p->planenum]; // check for axis aligned surfaces l = plane->type; if (l > last_axial) { continue; } // // calculate the split metric along axis l, smaller values are better // value = 0; dist = plane->dist * plane->normal[l]; #ifdef HLBSP_MAXNODESIZE_SKYBOX if (maxs[l] - dist < ON_EPSILON || dist - mins[l] < ON_EPSILON) continue; #endif #ifdef HLBSP_ChooseMidPlane_FIX if (maxs[l] - dist < g_maxnode_size/2.0 - ON_EPSILON || dist - mins[l] < g_maxnode_size/2.0 - ON_EPSILON) continue; #endif #ifdef HLBSP_CHOOSEMIDPLANE double crosscount = 0; double frontcount = 0; double backcount = 0; double coplanarcount = 0; TestSurfaceTree (surfacetree, plane); frontcount += surfacetree->result.frontsize; backcount += surfacetree->result.backsize; for (it = surfacetree->result.middle->begin (); it != surfacetree->result.middle->end (); ++it) { f = *it; #ifdef HLCSG_HLBSP_SOLIDHINT if (f->facestyle == face_discardable) { continue; } #endif if (f->planenum == p->planenum || f->planenum == (p->planenum ^ 1)) { coplanarcount++; continue; } switch (FaceSide (f, plane)) { case SIDE_FRONT: frontcount++; break; case SIDE_BACK: backcount++; break; case SIDE_ON: crosscount++; break; } } double frontsize = frontcount + 0.5 * coplanarcount + 0.5 * crosscount; double frontfrac = (maxs[l] - dist) / (maxs[l] - mins[l]); double backsize = backcount + 0.5 * coplanarcount + 0.5 * crosscount; double backfrac = (dist - mins[l]) / (maxs[l] - mins[l]); value = crosscount + 0.1 * (frontsize * (log (frontfrac) / log (2.0)) + backsize * (log (backfrac) / log (2.0))); // the first part is how the split will increase the number of faces // the second part is how the split will increase the average depth of the bsp tree #else for (j = 0; j < 3; j++) { if (j == l) { value += (maxs[l] - dist) * (maxs[l] - dist); value += (dist - mins[l]) * (dist - mins[l]); } else { value += 2 * (maxs[j] - mins[j]) * (maxs[j] - mins[j]); } } #endif if (value > bestvalue) { continue; } // // currently the best! // bestvalue = value; bestsurface = p; } #ifdef HLBSP_CHOOSEMIDPLANE DeleteSurfaceTree (surfacetree); #endif if (!bestsurface) { #ifdef HLBSP_ChooseMidPlane_FIX return NULL; #else for (p = surfaces; p; p = p->next) { if (!p->onnode) { #ifdef ZHLT_DETAILBRUSH if (p->detaillevel != detaillevel) { continue; } #endif return p; // first valid surface } } Error("ChooseMidPlaneFromList: no valid planes"); #endif } return bestsurface; } // ===================================================================================== // ChoosePlaneFromList // Choose the plane that splits the least faces // ===================================================================================== #ifdef HLBSP_ChoosePlane_VL static surface_t* ChoosePlaneFromList(surface_t* surfaces, const vec3_t mins, const vec3_t maxs #ifdef ZHLT_DETAILBRUSH // mins and maxs are invalid when detaillevel > 0 , int detaillevel #endif ) { surface_t* p; surface_t* p2; surface_t* bestsurface; vec_t bestvalue; vec_t value; dplane_t* plane; face_t* f; #ifdef HLBSP_FAST_SELECTPARTITION double planecount; double totalsplit; double avesplit; double (*tmpvalue)[2]; surfacetree_t* surfacetree; std::vector< face_t * >::iterator it; planecount = 0; totalsplit = 0; tmpvalue = (double (*)[2])malloc (g_numplanes * sizeof (double [2])); surfacetree = BuildSurfaceTree (surfaces, ON_EPSILON); #endif #ifndef HLBSP_FAST_SELECTPARTITION double avesplit; double planecount; { planecount = 0; double totalsplit = 0; for (p = surfaces; p; p = p->next) { if (p->onnode) { continue; } #ifdef ZHLT_DETAILBRUSH if (p->detaillevel != detaillevel) { continue; } #endif planecount++; plane = &g_dplanes[p->planenum]; for (p2 = surfaces; p2; p2 = p2->next) { if (p2->onnode || p2 == p) { continue; } for (f = p2->faces; f; f = f->next) { #ifdef HLCSG_HLBSP_SOLIDHINT if (f->facestyle == face_discardable) { continue; } #endif if (FaceSide (f, plane) == SIDE_ON) { totalsplit++; } } } } avesplit = (double)totalsplit / (double)planecount; } #endif // // pick the plane that splits the least // bestvalue = 9e30; bestsurface = NULL; for (p = surfaces; p; p = p->next) { if (p->onnode) { continue; } #ifdef ZHLT_DETAILBRUSH if (p->detaillevel != detaillevel) { continue; } #endif #ifdef HLBSP_FAST_SELECTPARTITION planecount++; #endif double crosscount = 0; // use double here because we need to perform "crosscount++" double frontcount = 0; double backcount = 0; double coplanarcount = 0; #ifdef HLBSP_AVOIDEPSILONSPLIT double epsilonsplit = 0; #endif plane = &g_dplanes[p->planenum]; for (f = p->faces; f; f = f->next) { #ifdef HLCSG_HLBSP_SOLIDHINT if (f->facestyle == face_discardable) { continue; } #endif coplanarcount++; } #ifdef HLBSP_FAST_SELECTPARTITION TestSurfaceTree (surfacetree, plane); { frontcount += surfacetree->result.frontsize; backcount += surfacetree->result.backsize; for (it = surfacetree->result.middle->begin (); it != surfacetree->result.middle->end (); ++it) { f = *it; if (f->planenum == p->planenum || f->planenum == (p->planenum ^ 1)) { continue; } #else for (p2 = surfaces; p2; p2 = p2->next) { if (p2->onnode || p2 == p) { continue; } for (f = p2->faces; f; f = f->next) { #endif #ifdef HLCSG_HLBSP_SOLIDHINT if (f->facestyle == face_discardable) { #ifdef HLBSP_AVOIDEPSILONSPLIT FaceSide (f, plane, &epsilonsplit); #endif continue; } #endif switch (FaceSide(f, plane #ifdef HLBSP_AVOIDEPSILONSPLIT , &epsilonsplit #endif )) { case SIDE_FRONT: frontcount++; break; case SIDE_BACK: backcount++; break; case SIDE_ON: #ifdef HLBSP_FAST_SELECTPARTITION totalsplit++; #endif crosscount++; break; } } } value = crosscount - sqrt (coplanarcount); // Not optimized. --vluzacn #ifdef HLCSG_HLBSP_SOLIDHINT if (coplanarcount == 0) { crosscount += 1; } #endif #ifdef HLBSP_BALANCE // This is the most efficient code among what I have ever tested: // (1) BSP file is small, despite possibility of slowing down vis and rad (but still faster than the original non BSP balancing method). // (2) Factors need not adjust across various maps. double frac = (coplanarcount / 2 + crosscount / 2 + frontcount) / (coplanarcount + frontcount + backcount + crosscount); double ent = (0.0001 < frac && frac < 0.9999)? (- frac * log (frac) / log (2.0) - (1 - frac) * log (1 - frac) / log (2.0)): 0.0; // the formula tends to 0 when frac=0,1 #ifdef HLBSP_FAST_SELECTPARTITION tmpvalue[p->planenum][1] = crosscount * (1 - ent); #else value += crosscount * avesplit * (1 - ent); #endif #endif #ifdef HLBSP_AVOIDEPSILONSPLIT value += epsilonsplit * 10000; #endif #ifdef HLBSP_FAST_SELECTPARTITION tmpvalue[p->planenum][0] = value; #else if (value < bestvalue) { // // currently the best! // bestvalue = value; bestsurface = p; } #endif } #ifdef HLBSP_FAST_SELECTPARTITION avesplit = totalsplit / planecount; for (p = surfaces; p; p = p->next) { if (p->onnode) { continue; } #ifdef ZHLT_DETAILBRUSH if (p->detaillevel != detaillevel) { continue; } #endif value = tmpvalue[p->planenum][0] + avesplit * tmpvalue[p->planenum][1]; if (value < bestvalue) { bestvalue = value; bestsurface = p; } } #endif if (!bestsurface) Error("ChoosePlaneFromList: no valid planes"); #ifdef HLBSP_FAST_SELECTPARTITION free (tmpvalue); DeleteSurfaceTree (surfacetree); #endif return bestsurface; } #else static surface_t* ChoosePlaneFromList(surface_t* surfaces, const vec3_t mins, const vec3_t maxs #ifdef ZHLT_DETAILBRUSH // mins and maxs are invalid when detaillevel > 0 , int detaillevel #endif ) { int j; int k; int l; surface_t* p; surface_t* p2; surface_t* bestsurface; vec_t bestvalue; vec_t bestdistribution; vec_t value; vec_t dist; dplane_t* plane; face_t* f; // // pick the plane that splits the least // #define UNDESIREABLE_HINT_FACTOR 10000 #define WORST_VALUE 100000000 bestvalue = WORST_VALUE; bestsurface = NULL; bestdistribution = 9e30; for (p = surfaces; p; p = p->next) { if (p->onnode) { continue; } #ifdef ZHLT_DETAILBRUSH if (p->detaillevel != detaillevel) { continue; } #endif #ifdef ZHLT_DETAIL if (g_bDetailBrushes) { // AJM: cycle though all faces, and make sure none of them are detail // if any of them are, this surface isnt to cause a bsp split for (face_t* f = p->faces; f; f = f->next) { if (f->contents == CONTENTS_DETAIL) { //Log("ChoosePlaneFromList::got a detial surface, skipping...\n"); continue; // wrong. --vluzacn } } } #endif plane = &g_dplanes[p->planenum]; k = 0; for (p2 = surfaces; p2; p2 = p2->next) { if (p2 == p) { continue; } if (p2->onnode) { continue; } for (f = p2->faces; f; f = f->next) { // Give this face (a hint brush fragment) a large 'undesireable' value, only split when we have to) if (f->facestyle == face_hint) { k += UNDESIREABLE_HINT_FACTOR; hlassert(k < WORST_VALUE); if (k >= WORST_VALUE) { Warning("::ChoosePlaneFromList() surface fragmentation undesireability exceeded WORST_VALUE"); k = WORST_VALUE - 1; } } if (FaceSide(f, plane) == SIDE_ON) { k++; if (k >= bestvalue) { break; } } } if (k > bestvalue) { break; } } if (k > bestvalue) { continue; } // if equal numbers, axial planes win, then decide on spatial subdivision if (k < bestvalue || (k == bestvalue && (plane->type <= last_axial))) { // check for axis aligned surfaces l = plane->type; if (l <= last_axial) { // axial aligned // // calculate the split metric along axis l // value = 0; for (j = 0; j < 3; j++) { if (j == l) { dist = plane->dist * plane->normal[l]; value += (maxs[l] - dist) * (maxs[l] - dist); value += (dist - mins[l]) * (dist - mins[l]); } else { value += 2 * (maxs[j] - mins[j]) * (maxs[j] - mins[j]); } } if (value > bestdistribution && k == bestvalue) { continue; } bestdistribution = value; } // // currently the best! // bestvalue = k; bestsurface = p; } } return bestsurface; } #endif // ===================================================================================== // SelectPartition // Selects a surface from a linked list of surfaces to split the group on // returns NULL if the surface list can not be divided any more (a leaf) // ===================================================================================== #ifdef ZHLT_DETAILBRUSH int CalcSplitDetaillevel (const node_t *node) { int bestdetaillevel = -1; surface_t *s; face_t *f; for (s = node->surfaces; s; s = s->next) { if (s->onnode) { continue; } #ifdef HLCSG_HLBSP_SOLIDHINT for (f = s->faces; f; f = f->next) { if (f->facestyle == face_discardable) { continue; } if (bestdetaillevel == -1 || f->detaillevel < bestdetaillevel) { bestdetaillevel = f->detaillevel; } } #else if (bestdetaillevel == -1 || s->detaillevel < bestdetaillevel) { bestdetaillevel = s->detaillevel; } #endif } return bestdetaillevel; } #endif static surface_t* SelectPartition(surface_t* surfaces, const node_t* const node, const bool usemidsplit #ifdef ZHLT_DETAILBRUSH , int splitdetaillevel #endif #ifdef HLBSP_MAXNODESIZE_SKYBOX , vec3_t validmins, vec3_t validmaxs #endif ) { #ifdef ZHLT_DETAILBRUSH if (splitdetaillevel == -1) { return NULL; } // now we MUST choose a surface of this detail level #else int i; surface_t* p; surface_t* bestsurface; // // count surface choices // i = 0; bestsurface = NULL; for (p = surfaces; p; p = p->next) { if (!p->onnode) { #ifdef ZHLT_DETAIL if (g_bDetailBrushes) { // AJM: cycle though all faces, and make sure none of them are detail // if any of them are, this surface isnt to cause a bsp split for (face_t* f = p->faces; f; f = f->next) { if (f->contents == CONTENTS_DETAIL) { //Log("SelectPartition::got a detial surface, skipping...\n"); continue; } } } #endif #ifdef HLCSG_HLBSP_SOLIDHINT face_t *f; for (f = p->faces; f; f = f->next) { #ifdef ZHLT_DETAILBRUSH if (f->detaillevel != splitdetaillevel) { continue; } #endif if (f->facestyle != face_discardable) { break; } } if (!f) { continue; // this surface is discardable // if all surfaces are discardable, this will become a leaf node } #endif #ifdef ZHLT_DETAILBRUSH if (p->detaillevel != splitdetaillevel) { continue; } #endif i++; bestsurface = p; } } if (i == 0) { return NULL; // this is a leafnode } #ifndef HLCSG_HLBSP_SOLIDHINT // although there is only one undiscardable surface, maybe discardable surfaces should split first. if (i == 1) { return bestsurface; // this is a final split } #endif #endif #ifdef HLBSP_ChooseMidPlane_FIX if (usemidsplit) { surface_t *s = ChooseMidPlaneFromList(surfaces, #ifdef HLBSP_MAXNODESIZE_SKYBOX validmins, validmaxs #else node->mins, node->maxs #endif #ifdef ZHLT_DETAILBRUSH , splitdetaillevel #endif ); if (s != NULL) return s; } return ChoosePlaneFromList(surfaces, node->mins, node->maxs #ifdef ZHLT_DETAILBRUSH , splitdetaillevel #endif ); #else if (usemidsplit) { // do fast way for clipping hull return ChooseMidPlaneFromList(surfaces, #ifdef HLBSP_MAXNODESIZE_SKYBOX validmins, validmaxs #else node->mins, node->maxs #endif #ifdef ZHLT_DETAILBRUSH , splitdetaillevel #endif ); } else { // do slow way to save poly splits for drawing hull return ChoosePlaneFromList(surfaces, node->mins, node->maxs #ifdef ZHLT_DETAILBRUSH , splitdetaillevel #endif ); } #endif } // ===================================================================================== // CalcSurfaceInfo // Calculates the bounding box // ===================================================================================== static void CalcSurfaceInfo(surface_t* surf) { int i; int j; face_t* f; hlassume(surf->faces != NULL, assume_ValidPointer); // "CalcSurfaceInfo() surface without a face" // // calculate a bounding box // for (i = 0; i < 3; i++) { surf->mins[i] = 99999; surf->maxs[i] = -99999; } #ifdef ZHLT_DETAILBRUSH surf->detaillevel = -1; #endif for (f = surf->faces; f; f = f->next) { if (f->contents >= 0) { Error("Bad contents"); } for (i = 0; i < f->numpoints; i++) { for (j = 0; j < 3; j++) { if (f->pts[i][j] < surf->mins[j]) { surf->mins[j] = f->pts[i][j]; } if (f->pts[i][j] > surf->maxs[j]) { surf->maxs[j] = f->pts[i][j]; } } } #ifdef ZHLT_DETAILBRUSH if (surf->detaillevel == -1 || f->detaillevel < surf->detaillevel) { surf->detaillevel = f->detaillevel; } #endif } } #ifdef ZHLT_DETAILBRUSH #ifdef HLCSG_HLBSP_SOLIDHINT void FixDetaillevelForDiscardable (node_t *node, int detaillevel) { // when we move on to the next detaillevel, some discardable faces of previous detail level remain not on node (because they are discardable). remove them now surface_t *s, **psnext; face_t *f, **pfnext; for (psnext = &node->surfaces; s = *psnext, s != NULL; ) { if (s->onnode) { psnext = &s->next; continue; } hlassume (s->faces, assume_ValidPointer); for (pfnext = &s->faces; f = *pfnext, f != NULL; ) { if (detaillevel == -1 || f->detaillevel < detaillevel) { *pfnext = f->next; FreeFace (f); } else { pfnext = &f->next; } } if (!s->faces) { *psnext = s->next; FreeSurface (s); } else { psnext = &s->next; CalcSurfaceInfo (s); hlassume (!(detaillevel == -1 || s->detaillevel < detaillevel), assume_first); } } } #endif #endif // ===================================================================================== // DivideSurface // ===================================================================================== static void DivideSurface(surface_t* in, const dplane_t* const split, surface_t** front, surface_t** back) { face_t* facet; face_t* next; face_t* frontlist; face_t* backlist; face_t* frontfrag; face_t* backfrag; surface_t* news; dplane_t* inplane; inplane = &g_dplanes[in->planenum]; // parallel case is easy if (inplane->normal[0] == split->normal[0] && inplane->normal[1] == split->normal[1] && inplane->normal[2] == split->normal[2]) { if (inplane->dist > split->dist) { *front = in; *back = NULL; } else if (inplane->dist < split->dist) { *front = NULL; *back = in; } else { // split the surface into front and back frontlist = NULL; backlist = NULL; for (facet = in->faces; facet; facet = next) { next = facet->next; if (facet->planenum & 1) { facet->next = backlist; backlist = facet; } else { facet->next = frontlist; frontlist = facet; } } goto makesurfs; } return; } // do a real split. may still end up entirely on one side // OPTIMIZE: use bounding box for fast test frontlist = NULL; backlist = NULL; for (facet = in->faces; facet; facet = next) { next = facet->next; SplitFace(facet, split, &frontfrag, &backfrag); if (frontfrag) { frontfrag->next = frontlist; frontlist = frontfrag; } if (backfrag) { backfrag->next = backlist; backlist = backfrag; } } // if nothing actually got split, just move the in plane makesurfs: #ifdef HLBSP_REMOVECOLINEARPOINTS if (frontlist == NULL && backlist == NULL) { *front = NULL; *back = NULL; return; } #endif if (frontlist == NULL) { *front = NULL; *back = in; in->faces = backlist; return; } if (backlist == NULL) { *front = in; *back = NULL; in->faces = frontlist; return; } // stuff got split, so allocate one new surface and reuse in news = AllocSurface(); *news = *in; news->faces = backlist; *back = news; in->faces = frontlist; *front = in; // recalc bboxes and flags CalcSurfaceInfo(news); CalcSurfaceInfo(in); } // ===================================================================================== // SplitNodeSurfaces // ===================================================================================== static void SplitNodeSurfaces(surface_t* surfaces, const node_t* const node) { surface_t* p; surface_t* next; surface_t* frontlist; surface_t* backlist; surface_t* frontfrag; surface_t* backfrag; dplane_t* splitplane; splitplane = &g_dplanes[node->planenum]; frontlist = NULL; backlist = NULL; for (p = surfaces; p; p = next) { next = p->next; DivideSurface(p, splitplane, &frontfrag, &backfrag); if (frontfrag) { if (!frontfrag->faces) { Error("surface with no faces"); } frontfrag->next = frontlist; frontlist = frontfrag; } if (backfrag) { if (!backfrag->faces) { Error("surface with no faces"); } backfrag->next = backlist; backlist = backfrag; } } node->children[0]->surfaces = frontlist; node->children[1]->surfaces = backlist; } #ifdef ZHLT_DETAILBRUSH static void SplitNodeBrushes (brush_t *brushes, const node_t *node) { brush_t *frontlist, *frontfrag; brush_t *backlist, *backfrag; brush_t *b, *next; const dplane_t *splitplane; frontlist = NULL; backlist = NULL; splitplane = &g_dplanes[node->planenum]; for (b = brushes; b; b = next) { next = b->next; SplitBrush (b, splitplane, &frontfrag, &backfrag); if (frontfrag) { frontfrag->next = frontlist; frontlist = frontfrag; } if (backfrag) { backfrag->next = backlist; backlist = backfrag; } } node->children[0]->detailbrushes = frontlist; node->children[1]->detailbrushes = backlist; } #endif // ===================================================================================== // RankForContents // ===================================================================================== static int RankForContents(const int contents) { //Log("SolidBSP::RankForContents - contents type is %i ",contents); switch (contents) { #ifdef ZHLT_NULLTEX // AJM #ifndef HLCSG_HLBSP_CONTENTSNULL_FIX case CONTENTS_NULL: //Log("(null)\n"); //return 13; return -2; #endif #endif case CONTENTS_EMPTY: //Log("(empty)\n"); return 0; case CONTENTS_WATER: //Log("(water)\n"); return 1; case CONTENTS_TRANSLUCENT: //Log("(traslucent)\n"); return 2; case CONTENTS_CURRENT_0: //Log("(current_0)\n"); return 3; case CONTENTS_CURRENT_90: //Log("(current_90)\n"); return 4; case CONTENTS_CURRENT_180: //Log("(current_180)\n"); return 5; case CONTENTS_CURRENT_270: //Log("(current_270)\n"); return 6; case CONTENTS_CURRENT_UP: //Log("(current_up)\n"); return 7; case CONTENTS_CURRENT_DOWN: //Log("(current_down)\n"); return 8; case CONTENTS_SLIME: //Log("(slime)\n"); return 9; case CONTENTS_LAVA: //Log("(lava)\n"); return 10; case CONTENTS_SKY: //Log("(sky)\n"); return 11; case CONTENTS_SOLID: //Log("(solid)\n"); return 12; #ifdef ZHLT_DETAIL case CONTENTS_DETAIL: return 13; //Log("(detail)\n"); #endif default: hlassert(false); Error("RankForContents: bad contents %i", contents); } return -1; } // ===================================================================================== // ContentsForRank // ===================================================================================== static int ContentsForRank(const int rank) { switch (rank) { #ifdef ZHLT_NULLTEX // AJM #ifndef HLCSG_HLBSP_CONTENTSNULL_FIX case -2: return CONTENTS_NULL; // has at leat one face with null #endif #endif case -1: #ifdef HLCSG_HLBSP_ALLOWEMPTYENTITY return CONTENTS_EMPTY; #else return CONTENTS_SOLID; // no faces at all #endif case 0: return CONTENTS_EMPTY; case 1: return CONTENTS_WATER; case 2: return CONTENTS_TRANSLUCENT; case 3: return CONTENTS_CURRENT_0; case 4: return CONTENTS_CURRENT_90; case 5: return CONTENTS_CURRENT_180; case 6: return CONTENTS_CURRENT_270; case 7: return CONTENTS_CURRENT_UP; case 8: return CONTENTS_CURRENT_DOWN; case 9: return CONTENTS_SLIME; case 10: return CONTENTS_LAVA; case 11: return CONTENTS_SKY; case 12: return CONTENTS_SOLID; #ifdef ZHLT_DETAIL // AJM case 13: return CONTENTS_DETAIL; #endif default: hlassert(false); Error("ContentsForRank: bad rank %i", rank); } return -1; } // ===================================================================================== // FreeLeafSurfs // ===================================================================================== static void FreeLeafSurfs(node_t* leaf) { surface_t* surf; surface_t* snext; face_t* f; face_t* fnext; for (surf = leaf->surfaces; surf; surf = snext) { snext = surf->next; for (f = surf->faces; f; f = fnext) { fnext = f->next; FreeFace(f); } FreeSurface(surf); } leaf->surfaces = NULL; } #ifdef ZHLT_DETAILBRUSH static void FreeLeafBrushes (node_t *leaf) { brush_t *b, *next; for (b = leaf->detailbrushes; b; b = next) { next = b->next; FreeBrush (b); } leaf->detailbrushes = NULL; } #endif // ===================================================================================== // LinkLeafFaces // Determines the contents of the leaf and creates the final list of original faces // that have some fragment inside this leaf // ===================================================================================== #ifdef HLBSP_MAX_LEAF_FACES #define MAX_LEAF_FACES 16384 #else #define MAX_LEAF_FACES 1024 #endif #ifdef HLBSP_WARNMIXEDCONTENTS const char* ContentsToString(int contents) { switch (contents) { case CONTENTS_EMPTY: return "EMPTY"; case CONTENTS_SOLID: return "SOLID"; case CONTENTS_WATER: return "WATER"; case CONTENTS_SLIME: return "SLIME"; case CONTENTS_LAVA: return "LAVA"; case CONTENTS_SKY: return "SKY"; case CONTENTS_CURRENT_0: return "CURRENT_0"; case CONTENTS_CURRENT_90: return "CURRENT_90"; case CONTENTS_CURRENT_180: return "CURRENT_180"; case CONTENTS_CURRENT_270: return "CURRENT_270"; case CONTENTS_CURRENT_UP: return "CURRENT_UP"; case CONTENTS_CURRENT_DOWN: return "CURRENT_DOWN"; case CONTENTS_TRANSLUCENT: return "TRANSLUCENT"; default: return "UNKNOWN"; } } #endif static void LinkLeafFaces(surface_t* planelist, node_t* leafnode) { face_t* f; surface_t* surf; int rank, r; #ifndef ZHLT_DETAILBRUSH int nummarkfaces; face_t* markfaces[MAX_LEAF_FACES]; leafnode->faces = NULL; leafnode->planenum = -1; #endif rank = -1; for (surf = planelist; surf; surf = surf->next) { #ifdef ZHLT_DETAILBRUSH if (!surf->onnode) { continue; } #endif for (f = surf->faces; f; f = f->next) { if ((f->contents == CONTENTS_HINT)) { f->contents = CONTENTS_EMPTY; } #ifdef ZHLT_DETAILBRUSH if (f->detaillevel) { continue; } #endif r = RankForContents(f->contents); if (r > rank) { rank = r; } } } #ifdef HLBSP_WARNMIXEDCONTENTS for (surf = planelist; surf; surf = surf->next) { #ifdef ZHLT_DETAILBRUSH if (!surf->onnode) { continue; } #endif for (f = surf->faces; f; f = f->next) { #ifdef ZHLT_DETAILBRUSH if (f->detaillevel) { continue; } #endif r = RankForContents(f->contents); if (r != rank) break; } if (f) break; } if (surf) { entity_t *ent = EntityForModel (g_nummodels - 1); if (g_nummodels - 1 != 0 && ent == &g_entities[0]) { ent = NULL; } Warning ("Ambiguous leafnode content ( %s and %s ) at (%.0f,%.0f,%.0f)-(%.0f,%.0f,%.0f) in hull %d of model %d (entity: classname \"%s\", origin \"%s\", targetname \"%s\")", ContentsToString (ContentsForRank(r)), ContentsToString (ContentsForRank(rank)), leafnode->mins[0], leafnode->mins[1], leafnode->mins[2], leafnode->maxs[0], leafnode->maxs[1], leafnode->maxs[2], g_hullnum, g_nummodels - 1, (ent? ValueForKey (ent, "classname"): "unknown"), (ent? ValueForKey (ent, "origin"): "unknown"), (ent? ValueForKey (ent, "targetname"): "unknown")); for (surface_t *surf2 = planelist; surf2; surf2 = surf2->next) { for (face_t *f2 = surf2->faces; f2; f2 = f2->next) { Developer (DEVELOPER_LEVEL_SPAM, "content = %d plane = %d normal = (%g,%g,%g)\n", f2->contents, f2->planenum, g_dplanes[f2->planenum].normal[0], g_dplanes[f2->planenum].normal[1], g_dplanes[f2->planenum].normal[2]); for (int i = 0; i < f2->numpoints; i++) { Developer (DEVELOPER_LEVEL_SPAM, "(%g,%g,%g)\n", f2->pts[i][0], f2->pts[i][1], f2->pts[i][2]); } } } } #endif leafnode->contents = ContentsForRank(rank); #ifndef ZHLT_DETAILBRUSH if (leafnode->contents != CONTENTS_SOLID) { nummarkfaces = 0; for (surf = leafnode->surfaces; surf; surf = surf->next) { for (f = surf->faces; f; f = f->next) { #ifdef HLCSG_HLBSP_SOLIDHINT if (f->original == NULL) { continue; } #endif hlassume(nummarkfaces < MAX_LEAF_FACES, assume_MAX_LEAF_FACES); markfaces[nummarkfaces++] = f->original; } } markfaces[nummarkfaces] = NULL; // end marker nummarkfaces++; leafnode->markfaces = (face_t**)malloc(nummarkfaces * sizeof(*leafnode->markfaces)); memcpy(leafnode->markfaces, markfaces, nummarkfaces * sizeof(*leafnode->markfaces)); } FreeLeafSurfs(leafnode); leafnode->surfaces = NULL; #endif } #ifdef ZHLT_DETAILBRUSH static void MakeLeaf (node_t *leafnode) { int nummarkfaces; face_t *markfaces[MAX_LEAF_FACES + 1]; surface_t *surf; face_t *f; leafnode->planenum = -1; leafnode->iscontentsdetail = leafnode->detailbrushes != NULL; FreeLeafBrushes (leafnode); leafnode->detailbrushes = NULL; #ifdef HLBSP_DETAILBRUSH_CULL if (leafnode->boundsbrush) { FreeBrush (leafnode->boundsbrush); } leafnode->boundsbrush = NULL; #endif if (!(leafnode->isportalleaf && leafnode->contents == CONTENTS_SOLID)) { nummarkfaces = 0; for (surf = leafnode->surfaces; surf; surf = surf->next) { if (!surf->onnode) { continue; } #ifdef HLCSG_HLBSP_SOLIDHINT if (!surf->onnode) { continue; } #endif for (f = surf->faces; f; f = f->next) { if (f->original == NULL) { // because it is not on node or its content is solid continue; } #ifdef HLCSG_HLBSP_SOLIDHINT if (f->original == NULL) { continue; } #endif hlassume(nummarkfaces < MAX_LEAF_FACES, assume_MAX_LEAF_FACES); markfaces[nummarkfaces++] = f->original; } } markfaces[nummarkfaces] = NULL; // end marker nummarkfaces++; leafnode->markfaces = (face_t**)malloc(nummarkfaces * sizeof(*leafnode->markfaces)); memcpy(leafnode->markfaces, markfaces, nummarkfaces * sizeof(*leafnode->markfaces)); } FreeLeafSurfs(leafnode); leafnode->surfaces = NULL; } #endif // ===================================================================================== // MakeNodePortal // Create the new portal by taking the full plane winding for the cutting plane and // clipping it by all of the planes from the other portals. // Each portal tracks the node that created it, so unused nodes can be removed later. // ===================================================================================== static void MakeNodePortal(node_t* node) { portal_t* new_portal; portal_t* p; dplane_t* plane; dplane_t clipplane; Winding * w; int side = 0; plane = &g_dplanes[node->planenum]; w = new Winding(*plane); new_portal = AllocPortal(); new_portal->plane = *plane; new_portal->onnode = node; for (p = node->portals; p; p = p->next[side]) { clipplane = p->plane; if (p->nodes[0] == node) { side = 0; } else if (p->nodes[1] == node) { clipplane.dist = -clipplane.dist; VectorSubtract(vec3_origin, clipplane.normal, clipplane.normal); side = 1; } else { Error("MakeNodePortal: mislinked portal"); } w->Clip(clipplane, true); #ifdef ZHLT_WINDING_RemoveColinearPoints_VL if (w->m_NumPoints == 0) #else if (!w) #endif { #ifdef ZHLT_WINDING_RemoveColinearPoints_VL Developer (DEVELOPER_LEVEL_WARNING, #else Warning( #endif "MakeNodePortal:new portal was clipped away from node@(%.0f,%.0f,%.0f)-(%.0f,%.0f,%.0f)", node->mins[0], node->mins[1], node->mins[2], node->maxs[0], node->maxs[1], node->maxs[2]); FreePortal(new_portal); return; } } new_portal->winding = w; AddPortalToNodes(new_portal, node->children[0], node->children[1]); } // ===================================================================================== // SplitNodePortals // Move or split the portals that bound node so that the node's children have portals instead of node. // ===================================================================================== static void SplitNodePortals(node_t *node) { portal_t* p; portal_t* next_portal; portal_t* new_portal; node_t* f; node_t* b; node_t* other_node; int side = 0; dplane_t* plane; Winding* frontwinding; Winding* backwinding; plane = &g_dplanes[node->planenum]; f = node->children[0]; b = node->children[1]; for (p = node->portals; p; p = next_portal) { if (p->nodes[0] == node) { side = 0; } else if (p->nodes[1] == node) { side = 1; } else { Error("SplitNodePortals: mislinked portal"); } next_portal = p->next[side]; other_node = p->nodes[!side]; RemovePortalFromNode(p, p->nodes[0]); RemovePortalFromNode(p, p->nodes[1]); // cut the portal into two portals, one on each side of the cut plane p->winding->Divide(*plane, &frontwinding, &backwinding); #ifdef ZHLT_WINDING_RemoveColinearPoints_VL if (!frontwinding && !backwinding) { continue; } #endif if (!frontwinding) { if (side == 0) { AddPortalToNodes(p, b, other_node); } else { AddPortalToNodes(p, other_node, b); } continue; } if (!backwinding) { if (side == 0) { AddPortalToNodes(p, f, other_node); } else { AddPortalToNodes(p, other_node, f); } continue; } // the winding is split new_portal = AllocPortal(); *new_portal = *p; new_portal->winding = backwinding; delete p->winding; p->winding = frontwinding; if (side == 0) { AddPortalToNodes(p, f, other_node); AddPortalToNodes(new_portal, b, other_node); } else { AddPortalToNodes(p, other_node, f); AddPortalToNodes(new_portal, other_node, b); } } node->portals = NULL; } // ===================================================================================== // CalcNodeBounds // Determines the boundaries of a node by minmaxing all the portal points, whcih // completely enclose the node. // Returns true if the node should be midsplit.(very large) // ===================================================================================== static bool CalcNodeBounds(node_t* node #ifdef HLBSP_MAXNODESIZE_SKYBOX , vec3_t validmins, vec3_t validmaxs #endif ) { int i; int j; vec_t v; portal_t* p; portal_t* next_portal; int side = 0; #ifdef ZHLT_DETAILBRUSH if (node->isdetail) { return false; } #endif #ifdef ZHLT_LARGERANGE node->mins[0] = node->mins[1] = node->mins[2] = BOGUS_RANGE; node->maxs[0] = node->maxs[1] = node->maxs[2] = -BOGUS_RANGE; #else node->mins[0] = node->mins[1] = node->mins[2] = 9999; node->maxs[0] = node->maxs[1] = node->maxs[2] = -9999; #endif for (p = node->portals; p; p = next_portal) { if (p->nodes[0] == node) { side = 0; } else if (p->nodes[1] == node) { side = 1; } else { Error("CalcNodeBounds: mislinked portal"); } next_portal = p->next[side]; for (i = 0; i < p->winding->m_NumPoints; i++) { for (j = 0; j < 3; j++) { v = p->winding->m_Points[i][j]; if (v < node->mins[j]) { node->mins[j] = v; } if (v > node->maxs[j]) { node->maxs[j] = v; } } } } #ifdef ZHLT_DETAILBRUSH if (node->isportalleaf) { return false; } #endif #ifdef HLBSP_MAXNODESIZE_SKYBOX for (i = 0; i < 3; i++) { validmins[i] = qmax (node->mins[i], -(ENGINE_ENTITY_RANGE + g_maxnode_size)); validmaxs[i] = qmin (node->maxs[i], ENGINE_ENTITY_RANGE + g_maxnode_size); } for (i = 0; i < 3; i++) { if (validmaxs[i] - validmins[i] <= ON_EPSILON) { return false; } } for (i = 0; i < 3; i++) { if (validmaxs[i] - validmins[i] > g_maxnode_size + ON_EPSILON) { return true; } } #else for (i = 0; i < 3; i++) { if (node->maxs[i] - node->mins[i] > g_maxnode_size) { return true; } } #endif return false; } // ===================================================================================== // CopyFacesToNode // Do a final merge attempt, then subdivide the faces to surface cache size if needed. // These are final faces that will be drawable in the game. // Copies of these faces are further chopped up into the leafs, but they will reference these originals. // ===================================================================================== static void CopyFacesToNode(node_t* node, surface_t* surf) { face_t** prevptr; face_t* f; face_t* newf; // merge as much as possible MergePlaneFaces(surf); // subdivide large faces prevptr = &surf->faces; while (1) { f = *prevptr; if (!f) { break; } SubdivideFace(f, prevptr); f = *prevptr; prevptr = &f->next; } // copy the faces to the node, and consider them the originals node->surfaces = NULL; node->faces = NULL; for (f = surf->faces; f; f = f->next) { #ifdef HLCSG_HLBSP_SOLIDHINT if (f->facestyle == face_discardable) { continue; } #endif if (f->contents != CONTENTS_SOLID) { newf = AllocFace(); *newf = *f; f->original = newf; newf->next = node->faces; node->faces = newf; } } } // ===================================================================================== // BuildBspTree_r // ===================================================================================== static void BuildBspTree_r(node_t* node) { surface_t* split; bool midsplit; surface_t* allsurfs; #ifdef HLBSP_MAXNODESIZE_SKYBOX vec3_t validmins, validmaxs; #endif midsplit = CalcNodeBounds(node #ifdef HLBSP_MAXNODESIZE_SKYBOX , validmins, validmaxs #endif ); #ifdef HLBSP_DETAILBRUSH_CULL if (node->boundsbrush) { CalcBrushBounds (node->boundsbrush, node->loosemins, node->loosemaxs); } else { VectorFill (node->loosemins, BOGUS_RANGE); VectorFill (node->loosemaxs, -BOGUS_RANGE); } #endif #ifdef ZHLT_DETAILBRUSH int splitdetaillevel = CalcSplitDetaillevel (node); #ifdef HLCSG_HLBSP_SOLIDHINT FixDetaillevelForDiscardable (node, splitdetaillevel); #endif #endif split = SelectPartition(node->surfaces, node, midsplit #ifdef ZHLT_DETAILBRUSH , splitdetaillevel #endif #ifdef HLBSP_MAXNODESIZE_SKYBOX , validmins, validmaxs #endif ); #ifdef ZHLT_DETAILBRUSH if (!node->isdetail && (!split || split->detaillevel > 0)) { node->isportalleaf = true; LinkLeafFaces (node->surfaces, node); // set contents if (node->contents == CONTENTS_SOLID) { split = NULL; } } else { node->isportalleaf = false; } #endif if (!split) { // this is a leaf node #ifdef ZHLT_DETAILBRUSH MakeLeaf (node); #else node->planenum = PLANENUM_LEAF; LinkLeafFaces(node->surfaces, node); #endif return; } // these are final polygons split->onnode = node; // can't use again allsurfs = node->surfaces; node->planenum = split->planenum; node->faces = NULL; CopyFacesToNode(node, split); node->children[0] = AllocNode(); node->children[1] = AllocNode(); #ifdef ZHLT_DETAILBRUSH node->children[0]->isdetail = split->detaillevel > 0; node->children[1]->isdetail = split->detaillevel > 0; #endif // split all the polysurfaces into front and back lists SplitNodeSurfaces(allsurfs, node); #ifdef ZHLT_DETAILBRUSH SplitNodeBrushes (node->detailbrushes, node); #ifdef HLBSP_DETAILBRUSH_CULL if (node->boundsbrush) { for (int k = 0; k < 2; k++) { dplane_t p; brush_t *copy, *front, *back; if (k == 0) { // front child VectorCopy (g_dplanes[split->planenum].normal, p.normal); p.dist = g_dplanes[split->planenum].dist - BOUNDS_EXPANSION; } else { // back child VecSubtractVector (0, g_dplanes[split->planenum].normal, p.normal); p.dist = -g_dplanes[split->planenum].dist - BOUNDS_EXPANSION; } copy = NewBrushFromBrush (node->boundsbrush); SplitBrush (copy, &p, &front, &back); if (back) { FreeBrush (back); } if (!front) { Warning ("BuildBspTree_r: bounds was clipped away at (%f,%f,%f)-(%f,%f,%f).", node->loosemins[0], node->loosemins[1], node->loosemins[2], node->loosemaxs[0], node->loosemaxs[1], node->loosemaxs[2]); } node->children[k]->boundsbrush = front; } FreeBrush (node->boundsbrush); } node->boundsbrush = NULL; #endif #endif #ifdef ZHLT_DETAILBRUSH if (!split->detaillevel) { MakeNodePortal (node); SplitNodePortals (node); } #else // create the portal that seperates the two children MakeNodePortal(node); // carve the portals on the boundaries of the node SplitNodePortals(node); #endif // recursively do the children BuildBspTree_r(node->children[0]); BuildBspTree_r(node->children[1]); UpdateStatus(); } // ===================================================================================== // SolidBSP // Takes a chain of surfaces plus a split type, and returns a bsp tree with faces // off the nodes. // The original surface chain will be completely freed. // ===================================================================================== node_t* SolidBSP(const surfchain_t* const surfhead, #ifdef ZHLT_DETAILBRUSH brush_t *detailbrushes, #endif bool report_progress) { node_t* headnode; ResetStatus(report_progress); double start_time = I_FloatTime(); if(report_progress) { Log("SolidBSP [hull %d] ",g_hullnum); } else { Verbose("----- SolidBSP -----\n"); } headnode = AllocNode(); headnode->surfaces = surfhead->surfaces; #ifdef ZHLT_DETAILBRUSH headnode->detailbrushes = detailbrushes; headnode->isdetail = false; #ifdef HLBSP_DETAILBRUSH_CULL vec3_t brushmins, brushmaxs; VectorAddVec (surfhead->mins, -SIDESPACE, brushmins); VectorAddVec (surfhead->maxs, SIDESPACE, brushmaxs); headnode->boundsbrush = BrushFromBox (brushmins, brushmaxs); #endif #endif #ifndef HLCSG_HLBSP_ALLOWEMPTYENTITY if (!surfhead->surfaces) { // nothing at all to build #ifdef ZHLT_DETAILBRUSH headnode->isportalleaf = true; headnode->iscontentsdetail = (headnode->detailbrushes != NULL); FreeLeafBrushes (headnode->detailbrushes); #endif headnode->planenum = -1; headnode->contents = CONTENTS_EMPTY; return headnode; } #endif // generate six portals that enclose the entire world MakeHeadnodePortals(headnode, surfhead->mins, surfhead->maxs); // recursively partition everything BuildBspTree_r(headnode); double end_time = I_FloatTime(); if(report_progress) { Log("%d (%.2f seconds)\n",++g_numProcessed,(end_time - start_time)); } return headnode; }