From 6bf20c78f5b69d40bcc4931df93d29198435ab67 Mon Sep 17 00:00:00 2001 From: zakk Date: Fri, 26 Aug 2005 17:39:27 +0000 Subject: newlines fixed git-svn-id: svn://svn.icculus.org/quake3/trunk@6 edf5b092-35ff-0310-97b2-ce42778d08ea --- code/bspc/brushbsp.c | 3742 +++++++++++++++++++++++++------------------------- 1 file changed, 1871 insertions(+), 1871 deletions(-) (limited to 'code/bspc/brushbsp.c') diff --git a/code/bspc/brushbsp.c b/code/bspc/brushbsp.c index 22cb0dd..d8accec 100755 --- a/code/bspc/brushbsp.c +++ b/code/bspc/brushbsp.c @@ -1,1871 +1,1871 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Foobar; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "qbsp.h" -#include "l_mem.h" -#include "../botlib/aasfile.h" -#include "aas_store.h" -#include "aas_cfg.h" - -#include - -/* -each side has a count of the other sides it splits - -the best split will be the one that minimizes the total split counts -of all remaining sides - -precalc side on plane table - -evaluate split side -{ -cost = 0 -for all sides - for all sides - get - if side splits side and splitside is on same child - cost++; -} -*/ - -int c_nodes; -int c_nonvis; -int c_active_brushes; -int c_solidleafnodes; -int c_totalsides; -int c_brushmemory; -int c_peak_brushmemory; -int c_nodememory; -int c_peak_totalbspmemory; - -// if a brush just barely pokes onto the other side, -// let it slide by without chopping -#define PLANESIDE_EPSILON 0.001 -//0.1 - -//#ifdef DEBUG -typedef struct cname_s -{ - int value; - char *name; -} cname_t; - -cname_t contentnames[] = -{ - {CONTENTS_SOLID,"CONTENTS_SOLID"}, - {CONTENTS_WINDOW,"CONTENTS_WINDOW"}, - {CONTENTS_AUX,"CONTENTS_AUX"}, - {CONTENTS_LAVA,"CONTENTS_LAVA"}, - {CONTENTS_SLIME,"CONTENTS_SLIME"}, - {CONTENTS_WATER,"CONTENTS_WATER"}, - {CONTENTS_MIST,"CONTENTS_MIST"}, - {LAST_VISIBLE_CONTENTS,"LAST_VISIBLE_CONTENTS"}, - - {CONTENTS_AREAPORTAL,"CONTENTS_AREAPORTAL"}, - {CONTENTS_PLAYERCLIP,"CONTENTS_PLAYERCLIP"}, - {CONTENTS_MONSTERCLIP,"CONTENTS_MONSTERCLIP"}, - {CONTENTS_CURRENT_0,"CONTENTS_CURRENT_0"}, - {CONTENTS_CURRENT_90,"CONTENTS_CURRENT_90"}, - {CONTENTS_CURRENT_180,"CONTENTS_CURRENT_180"}, - {CONTENTS_CURRENT_270,"CONTENTS_CURRENT_270"}, - {CONTENTS_CURRENT_UP,"CONTENTS_CURRENT_UP"}, - {CONTENTS_CURRENT_DOWN,"CONTENTS_CURRENT_DOWN"}, - {CONTENTS_ORIGIN,"CONTENTS_ORIGIN"}, - {CONTENTS_MONSTER,"CONTENTS_MONSTER"}, - {CONTENTS_DEADMONSTER,"CONTENTS_DEADMONSTER"}, - {CONTENTS_DETAIL,"CONTENTS_DETAIL"}, - {CONTENTS_Q2TRANSLUCENT,"CONTENTS_TRANSLUCENT"}, - {CONTENTS_LADDER,"CONTENTS_LADDER"}, - {0, 0} -}; - -void PrintContents(int contents) -{ - int i; - - for (i = 0; contentnames[i].value; i++) - { - if (contents & contentnames[i].value) - { - Log_Write("%s,", contentnames[i].name); - } //end if - } //end for -} //end of the function PrintContents - -//#endif DEBUG - -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void ResetBrushBSP(void) -{ - c_nodes = 0; - c_nonvis = 0; - c_active_brushes = 0; - c_solidleafnodes = 0; - c_totalsides = 0; - c_brushmemory = 0; - c_peak_brushmemory = 0; - c_nodememory = 0; - c_peak_totalbspmemory = 0; -} //end of the function ResetBrushBSP -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FindBrushInTree (node_t *node, int brushnum) -{ - bspbrush_t *b; - - if (node->planenum == PLANENUM_LEAF) - { - for (b=node->brushlist ; b ; b=b->next) - if (b->original->brushnum == brushnum) - Log_Print ("here\n"); - return; - } - FindBrushInTree(node->children[0], brushnum); - FindBrushInTree(node->children[1], brushnum); -} //end of the function FindBrushInTree -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void DrawBrushList (bspbrush_t *brush, node_t *node) -{ - int i; - side_t *s; - - GLS_BeginScene (); - for ( ; brush ; brush=brush->next) - { - for (i=0 ; inumsides ; i++) - { - s = &brush->sides[i]; - if (!s->winding) - continue; - if (s->texinfo == TEXINFO_NODE) - GLS_Winding (s->winding, 1); - else if (!(s->flags & SFL_VISIBLE)) - GLS_Winding (s->winding, 2); - else - GLS_Winding (s->winding, 0); - } - } - GLS_EndScene (); -} //end of the function DrawBrushList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis) -{ - int i; - side_t *s; - FILE *f; - - qprintf ("writing %s\n", name); - f = SafeOpenWrite (name); - - for ( ; brush ; brush=brush->next) - { - for (i=0 ; inumsides ; i++) - { - s = &brush->sides[i]; - if (!s->winding) - continue; - if (onlyvis && !(s->flags & SFL_VISIBLE)) - continue; - OutputWinding (brush->sides[i].winding, f); - } - } - - fclose (f); -} //end of the function WriteBrushList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void PrintBrush (bspbrush_t *brush) -{ - int i; - - printf ("brush: %p\n", brush); - for (i=0;inumsides ; i++) - { - pw(brush->sides[i].winding); - printf ("\n"); - } //end for -} //end of the function PrintBrush -//=========================================================================== -// Sets the mins/maxs based on the windings -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BoundBrush (bspbrush_t *brush) -{ - int i, j; - winding_t *w; - - ClearBounds (brush->mins, brush->maxs); - for (i=0 ; inumsides ; i++) - { - w = brush->sides[i].winding; - if (!w) - continue; - for (j=0 ; jnumpoints ; j++) - AddPointToBounds (w->p[j], brush->mins, brush->maxs); - } -} //end of the function BoundBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void CreateBrushWindings (bspbrush_t *brush) -{ - int i, j; - winding_t *w; - side_t *side; - plane_t *plane; - - for (i=0 ; inumsides ; i++) - { - side = &brush->sides[i]; - plane = &mapplanes[side->planenum]; - w = BaseWindingForPlane (plane->normal, plane->dist); - for (j=0 ; jnumsides && w; j++) - { - if (i == j) - continue; - if (brush->sides[j].flags & SFL_BEVEL) - continue; - plane = &mapplanes[brush->sides[j].planenum^1]; - ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON); - } - - side->winding = w; - } - - BoundBrush (brush); -} //end of the function CreateBrushWindings -//=========================================================================== -// Creates a new axial brush -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs) -{ - bspbrush_t *b; - int i; - vec3_t normal; - vec_t dist; - - b = AllocBrush (6); - b->numsides = 6; - for (i=0 ; i<3 ; i++) - { - VectorClear (normal); - normal[i] = 1; - dist = maxs[i]; - b->sides[i].planenum = FindFloatPlane (normal, dist); - - normal[i] = -1; - dist = -mins[i]; - b->sides[3+i].planenum = FindFloatPlane (normal, dist); - } - - CreateBrushWindings (b); - - return b; -} //end of the function BrushFromBounds -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BrushOutOfBounds(bspbrush_t *brush, vec3_t mins, vec3_t maxs, float epsilon) -{ - int i, j, n; - winding_t *w; - side_t *side; - - for (i = 0; i < brush->numsides; i++) - { - side = &brush->sides[i]; - w = side->winding; - for (j = 0; j < w->numpoints; j++) - { - for (n = 0; n < 3; n++) - { - if (w->p[j][n] < (mins[n] + epsilon) || w->p[j][n] > (maxs[n] - epsilon)) return true; - } //end for - } //end for - } //end for - return false; -} //end of the function BrushOutOfBounds -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -vec_t BrushVolume (bspbrush_t *brush) -{ - int i; - winding_t *w; - vec3_t corner; - vec_t d, area, volume; - plane_t *plane; - - if (!brush) return 0; - - // grab the first valid point as the corner - w = NULL; - for (i = 0; i < brush->numsides; i++) - { - w = brush->sides[i].winding; - if (w) break; - } //end for - if (!w) return 0; - VectorCopy (w->p[0], corner); - - // make tetrahedrons to all other faces - volume = 0; - for ( ; i < brush->numsides; i++) - { - w = brush->sides[i].winding; - if (!w) continue; - plane = &mapplanes[brush->sides[i].planenum]; - d = -(DotProduct (corner, plane->normal) - plane->dist); - area = WindingArea(w); - volume += d * area; - } //end for - - volume /= 3; - return volume; -} //end of the function BrushVolume -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int CountBrushList (bspbrush_t *brushes) -{ - int c; - - c = 0; - for ( ; brushes; brushes = brushes->next) c++; - return c; -} //end of the function CountBrushList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -node_t *AllocNode (void) -{ - node_t *node; - - node = GetMemory(sizeof(*node)); - memset (node, 0, sizeof(*node)); - if (numthreads == 1) - { - c_nodememory += MemorySize(node); - } //end if - return node; -} //end of the function AllocNode -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *AllocBrush (int numsides) -{ - bspbrush_t *bb; - int c; - - c = (int)&(((bspbrush_t *)0)->sides[numsides]); - bb = GetMemory(c); - memset (bb, 0, c); - if (numthreads == 1) - { - c_active_brushes++; - c_brushmemory += MemorySize(bb); - if (c_brushmemory > c_peak_brushmemory) - c_peak_brushmemory = c_brushmemory; - } //end if - return bb; -} //end of the function AllocBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void FreeBrush (bspbrush_t *brushes) -{ - int i; - - for (i=0 ; inumsides ; i++) - if (brushes->sides[i].winding) - FreeWinding(brushes->sides[i].winding); - if (numthreads == 1) - { - c_active_brushes--; - c_brushmemory -= MemorySize(brushes); - if (c_brushmemory < 0) c_brushmemory = 0; - } //end if - FreeMemory(brushes); -} //end of the function FreeBrush -//=========================================================================== -// -// 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 -//=========================================================================== -// Duplicates the brush, the sides, and the windings -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -bspbrush_t *CopyBrush (bspbrush_t *brush) -{ - bspbrush_t *newbrush; - int size; - int i; - - size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]); - - newbrush = AllocBrush (brush->numsides); - memcpy (newbrush, brush, size); - - for (i=0 ; inumsides ; i++) - { - if (brush->sides[i].winding) - newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding); - } - - return newbrush; -} //end of the function CopyBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -node_t *PointInLeaf (node_t *node, vec3_t point) -{ - vec_t d; - plane_t *plane; - - while (node->planenum != PLANENUM_LEAF) - { - plane = &mapplanes[node->planenum]; - d = DotProduct (point, plane->normal) - plane->dist; - if (d > 0) - node = node->children[0]; - else - node = node->children[1]; - } - - return node; -} //end of the function PointInLeaf -//=========================================================================== -// Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#if 0 -int BoxOnPlaneSide (vec3_t mins, vec3_t maxs, plane_t *plane) -{ - int side; - int i; - vec3_t corners[2]; - vec_t dist1, dist2; - - // axial planes are easy - if (plane->type < 3) - { - side = 0; - if (maxs[plane->type] > plane->dist+PLANESIDE_EPSILON) - side |= PSIDE_FRONT; - if (mins[plane->type] < plane->dist-PLANESIDE_EPSILON) - side |= PSIDE_BACK; - return side; - } - - // create the proper leading and trailing verts for the box - - for (i=0 ; i<3 ; i++) - { - if (plane->normal[i] < 0) - { - corners[0][i] = mins[i]; - corners[1][i] = maxs[i]; - } - else - { - corners[1][i] = mins[i]; - corners[0][i] = maxs[i]; - } - } - - dist1 = DotProduct (plane->normal, corners[0]) - plane->dist; - dist2 = DotProduct (plane->normal, corners[1]) - plane->dist; - side = 0; - if (dist1 >= PLANESIDE_EPSILON) - side = PSIDE_FRONT; - if (dist2 < PLANESIDE_EPSILON) - side |= PSIDE_BACK; - - return side; -} -#else -int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, plane_t *p) -{ - float dist1, dist2; - int sides; - - // axial planes are easy - if (p->type < 3) - { - sides = 0; - if (emaxs[p->type] > p->dist+PLANESIDE_EPSILON) sides |= PSIDE_FRONT; - if (emins[p->type] < p->dist-PLANESIDE_EPSILON) sides |= PSIDE_BACK; - return sides; - } //end if - -// general case - switch (p->signbits) - { - case 0: - dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - break; - case 1: - dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - break; - case 2: - dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - break; - case 3: - dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - break; - case 4: - dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - break; - case 5: - dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; - dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; - break; - case 6: - dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - break; - case 7: - dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; - dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; - break; - default: - dist1 = dist2 = 0; // shut up compiler -// assert( 0 ); - break; - } - - sides = 0; - if (dist1 - p->dist >= PLANESIDE_EPSILON) sides = PSIDE_FRONT; - if (dist2 - p->dist < PLANESIDE_EPSILON) sides |= PSIDE_BACK; - -// assert(sides != 0); - - return sides; -} -#endif -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int QuickTestBrushToPlanenum (bspbrush_t *brush, int planenum, int *numsplits) -{ - int i, num; - plane_t *plane; - int s; - - *numsplits = 0; - - plane = &mapplanes[planenum]; - -#ifdef ME - //fast axial cases - if (plane->type < 3) - { - if (plane->dist + PLANESIDE_EPSILON < brush->mins[plane->type]) - return PSIDE_FRONT; - if (plane->dist - PLANESIDE_EPSILON > brush->maxs[plane->type]) - return PSIDE_BACK; - } //end if -#endif //ME*/ - - // if the brush actually uses the planenum, - // we can tell the side for sure - for (i = 0; i < brush->numsides; i++) - { - num = brush->sides[i].planenum; - if (num >= MAX_MAPFILE_PLANES) - Error ("bad planenum"); - if (num == planenum) - return PSIDE_BACK|PSIDE_FACING; - if (num == (planenum ^ 1) ) - return PSIDE_FRONT|PSIDE_FACING; - - } - - // box on plane side - s = BoxOnPlaneSide (brush->mins, brush->maxs, plane); - - // if both sides, count the visible faces split - if (s == PSIDE_BOTH) - { - *numsplits += 3; - } - - return s; -} //end of the function QuickTestBrushToPlanenum -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int TestBrushToPlanenum (bspbrush_t *brush, int planenum, - int *numsplits, qboolean *hintsplit, int *epsilonbrush) -{ - int i, j, num; - plane_t *plane; - int s = 0; - winding_t *w; - vec_t d, d_front, d_back; - int front, back; - int type; - float dist; - - *numsplits = 0; - *hintsplit = false; - - plane = &mapplanes[planenum]; - -#ifdef ME - //fast axial cases - type = plane->type; - if (type < 3) - { - dist = plane->dist; - if (dist + PLANESIDE_EPSILON < brush->mins[type]) return PSIDE_FRONT; - if (dist - PLANESIDE_EPSILON > brush->maxs[type]) return PSIDE_BACK; - if (brush->mins[type] < dist - PLANESIDE_EPSILON && - brush->maxs[type] > dist + PLANESIDE_EPSILON) s = PSIDE_BOTH; - } //end if - - if (s != PSIDE_BOTH) -#endif //ME - { - // if the brush actually uses the planenum, - // we can tell the side for sure - for (i = 0; i < brush->numsides; i++) - { - num = brush->sides[i].planenum; - if (num >= MAX_MAPFILE_PLANES) Error ("bad planenum"); - if (num == planenum) - { - //we don't need to test this side plane again - brush->sides[i].flags |= SFL_TESTED; - return PSIDE_BACK|PSIDE_FACING; - } //end if - if (num == (planenum ^ 1) ) - { - //we don't need to test this side plane again - brush->sides[i].flags |= SFL_TESTED; - return PSIDE_FRONT|PSIDE_FACING; - } //end if - } //end for - - // box on plane side - s = BoxOnPlaneSide (brush->mins, brush->maxs, plane); - - if (s != PSIDE_BOTH) return s; - } //end if - - // if both sides, count the visible faces split - d_front = d_back = 0; - - for (i = 0; i < brush->numsides; i++) - { - if (brush->sides[i].texinfo == TEXINFO_NODE) - continue; // on node, don't worry about splits - if (!(brush->sides[i].flags & SFL_VISIBLE)) - continue; // we don't care about non-visible - w = brush->sides[i].winding; - if (!w) continue; - front = back = 0; - for (j = 0; j < w->numpoints; j++) - { - d = DotProduct(w->p[j], plane->normal) - plane->dist; - if (d > d_front) d_front = d; - if (d < d_back) d_back = d; - if (d > 0.1) // PLANESIDE_EPSILON) - front = 1; - if (d < -0.1) // PLANESIDE_EPSILON) - back = 1; - } //end for - if (front && back) - { - if ( !(brush->sides[i].surf & SURF_SKIP) ) - { - (*numsplits)++; - if (brush->sides[i].surf & SURF_HINT) - { - *hintsplit = true; - } //end if - } //end if - } //end if - } //end for - - if ( (d_front > 0.0 && d_front < 1.0) - || (d_back < 0.0 && d_back > -1.0) ) - (*epsilonbrush)++; - -#if 0 - if (*numsplits == 0) - { // didn't really need to be split - if (front) s = PSIDE_FRONT; - else if (back) s = PSIDE_BACK; - else s = 0; - } -#endif - - return s; -} //end of the function TestBrushToPlanenum -//=========================================================================== -// Returns true if the winding would be crunched out of -// existance by the vertex snapping. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -#define EDGE_LENGTH 0.2 -qboolean WindingIsTiny (winding_t *w) -{ -#if 0 - if (WindingArea (w) < 1) - return true; - return false; -#else - int i, j; - vec_t len; - vec3_t delta; - int edges; - - edges = 0; - for (i=0 ; inumpoints ; i++) - { - j = i == w->numpoints - 1 ? 0 : i+1; - VectorSubtract (w->p[j], w->p[i], delta); - len = VectorLength (delta); - if (len > EDGE_LENGTH) - { - if (++edges == 3) - return false; - } - } - return true; -#endif -} //end of the function WindingIsTiny -//=========================================================================== -// Returns true if the winding still has one of the points -// from basewinding for plane -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -qboolean WindingIsHuge (winding_t *w) -{ - int i, j; - - for (i=0 ; inumpoints ; i++) - { - for (j=0 ; j<3 ; j++) - if (w->p[i][j] < -BOGUS_RANGE+1 || w->p[i][j] > BOGUS_RANGE-1) - return true; - } - return false; -} //end of the function WindingIsHuge -//=========================================================================== -// creates a leaf out of the given nodes with the given brushes -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void LeafNode(node_t *node, bspbrush_t *brushes) -{ - bspbrush_t *b; - int i; - - node->side = NULL; - node->planenum = PLANENUM_LEAF; - node->contents = 0; - - for (b = brushes; b; b = b->next) - { - // if the brush is solid and all of its sides are on nodes, - // it eats everything - if (b->original->contents & CONTENTS_SOLID) - { - for (i=0 ; inumsides ; i++) - if (b->sides[i].texinfo != TEXINFO_NODE) - break; - if (i == b->numsides) - { - node->contents = CONTENTS_SOLID; - break; - } //end if - } //end if - node->contents |= b->original->contents; - } //end for - - if (create_aas) - { - node->expansionbboxes = 0; - node->contents = 0; - for (b = brushes; b; b = b->next) - { - node->expansionbboxes |= b->original->expansionbbox; - node->contents |= b->original->contents; - if (b->original->modelnum) - node->modelnum = b->original->modelnum; - } //end for - if (node->contents & CONTENTS_SOLID) - { - if (node->expansionbboxes != cfg.allpresencetypes) - { - node->contents &= ~CONTENTS_SOLID; - } //end if - } //end if - } //end if - - node->brushlist = brushes; -} //end of the function LeafNode -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void CheckPlaneAgainstParents (int pnum, node_t *node) -{ - node_t *p; - - for (p = node->parent; p; p = p->parent) - { - if (p->planenum == pnum) Error("Tried parent"); - } //end for -} //end of the function CheckPlaneAgainstParants -//=========================================================================== -// -// 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 -//=========================================================================== -// Using a hueristic, choses one of the sides out of the brushlist -// to partition the brushes with. -// Returns NULL if there are no valid planes to split with.. -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -side_t *SelectSplitSide (bspbrush_t *brushes, node_t *node) -{ - int value, bestvalue; - bspbrush_t *brush, *test; - side_t *side, *bestside; - int i, pass, numpasses; - int pnum; - int s; - int front, back, both, facing, splits; - int bsplits; - int bestsplits; - int epsilonbrush; - qboolean hintsplit = false; - - bestside = NULL; - bestvalue = -99999; - bestsplits = 0; - - // the search order goes: visible-structural, visible-detail, - // nonvisible-structural, nonvisible-detail. - // If any valid plane is available in a pass, no further - // passes will be tried. - numpasses = 2; - for (pass = 0; pass < numpasses; pass++) - { - for (brush = brushes; brush; brush = brush->next) - { - // only check detail the second pass -// if ( (pass & 1) && !(brush->original->contents & CONTENTS_DETAIL) ) -// continue; -// if ( !(pass & 1) && (brush->original->contents & CONTENTS_DETAIL) ) -// continue; - for (i = 0; i < brush->numsides; i++) - { - side = brush->sides + i; -// if (side->flags & SFL_BEVEL) -// continue; // never use a bevel as a spliter - if (!side->winding) - continue; // nothing visible, so it can't split - if (side->texinfo == TEXINFO_NODE) - continue; // allready a node splitter - if (side->flags & SFL_TESTED) - continue; // we allready have metrics for this plane -// if (side->surf & SURF_SKIP) -// continue; // skip surfaces are never chosen - -// if (!(side->flags & SFL_VISIBLE) && (pass < 2)) -// continue; // only check visible faces on first pass - - if ((side->flags & SFL_CURVE) && (pass < 1)) - continue; // only check curves the second pass - - pnum = side->planenum; - pnum &= ~1; // allways use positive facing plane - - CheckPlaneAgainstParents (pnum, node); - - if (!CheckPlaneAgainstVolume (pnum, node)) - continue; // would produce a tiny volume - - front = 0; - back = 0; - both = 0; - facing = 0; - splits = 0; - epsilonbrush = 0; - - //inner loop: optimize - for (test = brushes; test; test = test->next) - { - s = TestBrushToPlanenum (test, pnum, &bsplits, &hintsplit, &epsilonbrush); - - splits += bsplits; -// if (bsplits && (s&PSIDE_FACING) ) -// Error ("PSIDE_FACING with splits"); - - test->testside = s; - // - if (s & PSIDE_FACING) facing++; - if (s & PSIDE_FRONT) front++; - if (s & PSIDE_BACK) back++; - if (s == PSIDE_BOTH) both++; - } //end for - - // give a value estimate for using this plane - value = 5*facing - 5*splits - abs(front-back); -// value = -5*splits; -// value = 5*facing - 5*splits; - if (mapplanes[pnum].type < 3) - value+=5; // axial is better - - value -= epsilonbrush * 1000; // avoid! - - // never split a hint side except with another hint - if (hintsplit && !(side->surf & SURF_HINT) ) - value = -9999999; - - // save off the side test so we don't need - // to recalculate it when we actually seperate - // the brushes - if (value > bestvalue) - { - bestvalue = value; - bestside = side; - bestsplits = splits; - for (test = brushes; test ; test = test->next) - test->side = test->testside; - } //end if - } //end for - } //end for (brush = brushes; - - // if we found a good plane, don't bother trying any - // other passes - if (bestside) - { - if (pass > 1) - { - if (numthreads == 1) c_nonvis++; - } - if (pass > 0) node->detail_seperator = true; // not needed for vis - break; - } //end if - } //end for (pass = 0; - - // - // clear all the tested flags we set - // - for (brush = brushes ; brush ; brush=brush->next) - { - for (i = 0; i < brush->numsides; i++) - { - brush->sides[i].flags &= ~SFL_TESTED; - } //end for - } //end for - - return bestside; -} //end of the function SelectSplitSide -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane) -{ - int i, j; - winding_t *w; - vec_t d, max; - int side; - - max = 0; - side = PSIDE_FRONT; - for (i=0 ; inumsides ; i++) - { - w = brush->sides[i].winding; - if (!w) - continue; - for (j=0 ; jnumpoints ; j++) - { - d = DotProduct (w->p[j], plane->normal) - plane->dist; - if (d > max) - { - max = d; - side = PSIDE_FRONT; - } - if (-d > max) - { - max = -d; - side = PSIDE_BACK; - } - } - } - return side; -} //end of the function BrushMostlyOnSide -//=========================================================================== -// Generates two new brushes, leaving the original -// unchanged -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void SplitBrush (bspbrush_t *brush, int planenum, - bspbrush_t **front, bspbrush_t **back) -{ - bspbrush_t *b[2]; - int i, j; - winding_t *w, *cw[2], *midwinding; - plane_t *plane, *plane2; - side_t *s, *cs; - float d, d_front, d_back; - - *front = *back = NULL; - plane = &mapplanes[planenum]; - - // check all points - d_front = d_back = 0; - for (i=0 ; inumsides ; i++) - { - w = brush->sides[i].winding; - if (!w) - continue; - for (j=0 ; jnumpoints ; j++) - { - d = DotProduct (w->p[j], plane->normal) - plane->dist; - if (d > 0 && d > d_front) - d_front = d; - if (d < 0 && d < d_back) - d_back = d; - } - } - - if (d_front < 0.2) // PLANESIDE_EPSILON) - { // only on back - *back = CopyBrush (brush); - return; - } - if (d_back > -0.2) // PLANESIDE_EPSILON) - { // only on front - *front = CopyBrush (brush); - return; - } - - // create a new winding from the split plane - - w = BaseWindingForPlane (plane->normal, plane->dist); - for (i=0 ; inumsides && w ; i++) - { - plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; - ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON); - } - - if (!w || WindingIsTiny(w)) - { // the brush isn't really split - int side; - - side = BrushMostlyOnSide (brush, plane); - if (side == PSIDE_FRONT) - *front = CopyBrush (brush); - if (side == PSIDE_BACK) - *back = CopyBrush (brush); - //free a possible winding - if (w) FreeWinding(w); - return; - } - - if (WindingIsHuge (w)) - { - Log_Write("WARNING: huge winding\n"); - } - - midwinding = w; - - // split it for real - - for (i=0 ; i<2 ; i++) - { - b[i] = AllocBrush (brush->numsides+1); - b[i]->original = brush->original; - } - - // split all the current windings - - for (i=0 ; inumsides ; i++) - { - s = &brush->sides[i]; - w = s->winding; - if (!w) - continue; - ClipWindingEpsilon (w, plane->normal, plane->dist, - 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); - for (j=0 ; j<2 ; j++) - { - if (!cw[j]) - continue; -#if 0 - if (WindingIsTiny (cw[j])) - { - FreeWinding (cw[j]); - continue; - } -#endif - cs = &b[j]->sides[b[j]->numsides]; - b[j]->numsides++; - *cs = *s; -// cs->planenum = s->planenum; -// cs->texinfo = s->texinfo; -// cs->original = s->original; - cs->winding = cw[j]; - cs->flags &= ~SFL_TESTED; - } - } - - - // see if we have valid polygons on both sides - - for (i=0 ; i<2 ; i++) - { - BoundBrush (b[i]); - for (j=0 ; j<3 ; j++) - { - if (b[i]->mins[j] < -MAX_MAP_BOUNDS || b[i]->maxs[j] > MAX_MAP_BOUNDS) - { - Log_Write("bogus brush after clip"); - break; - } - } - - if (b[i]->numsides < 3 || j < 3) - { - FreeBrush (b[i]); - b[i] = NULL; - } - } - - if ( !(b[0] && b[1]) ) - { - if (!b[0] && !b[1]) - Log_Write("split removed brush\r\n"); - else - Log_Write("split not on both sides\r\n"); - if (b[0]) - { - FreeBrush (b[0]); - *front = CopyBrush (brush); - } - if (b[1]) - { - FreeBrush (b[1]); - *back = CopyBrush (brush); - } - return; - } - - // add the midwinding to both sides - for (i=0 ; i<2 ; i++) - { - cs = &b[i]->sides[b[i]->numsides]; - b[i]->numsides++; - - cs->planenum = planenum^i^1; - cs->texinfo = TEXINFO_NODE; //never use these sides as splitters - cs->flags &= ~SFL_VISIBLE; - cs->flags &= ~SFL_TESTED; - if (i==0) - cs->winding = CopyWinding (midwinding); - else - cs->winding = midwinding; - } - -{ - vec_t v1; - int i; - - for (i = 0; i < 2; i++) - { - v1 = BrushVolume (b[i]); - if (v1 < 1.0) - { - FreeBrush(b[i]); - b[i] = NULL; - //Log_Write("tiny volume after clip"); - } - } - if (!b[0] && !b[1]) - { - Log_Write("two tiny brushes\r\n"); - } //end if -} - - *front = b[0]; - *back = b[1]; -} //end of the function SplitBrush -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void SplitBrushList (bspbrush_t *brushes, - node_t *node, bspbrush_t **front, bspbrush_t **back) -{ - bspbrush_t *brush, *newbrush, *newbrush2; - side_t *side; - int sides; - int i; - - *front = *back = NULL; - - for (brush = brushes; brush; brush = brush->next) - { - sides = brush->side; - - if (sides == PSIDE_BOTH) - { // split into two brushes - SplitBrush (brush, node->planenum, &newbrush, &newbrush2); - if (newbrush) - { - newbrush->next = *front; - *front = newbrush; - } //end if - if (newbrush2) - { - newbrush2->next = *back; - *back = newbrush2; - } //end if - continue; - } //end if - - newbrush = CopyBrush (brush); - - // if the planenum is actualy a part of the brush - // find the plane and flag it as used so it won't be tried - // as a splitter again - if (sides & PSIDE_FACING) - { - for (i=0 ; inumsides ; i++) - { - side = newbrush->sides + i; - if ( (side->planenum& ~1) == node->planenum) - side->texinfo = TEXINFO_NODE; - } //end for - } //end if - if (sides & PSIDE_FRONT) - { - newbrush->next = *front; - *front = newbrush; - continue; - } //end if - if (sides & PSIDE_BACK) - { - newbrush->next = *back; - *back = newbrush; - continue; - } //end if - } //end for -} //end of the function SplitBrushList -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void CheckBrushLists(bspbrush_t *brushlist1, bspbrush_t *brushlist2) -{ - bspbrush_t *brush1, *brush2; - - for (brush1 = brushlist1; brush1; brush1 = brush1->next) - { - for (brush2 = brushlist2; brush2; brush2 = brush2->next) - { - assert(brush1 != brush2); - } //end for - } //end for -} //end of the function CheckBrushLists -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -int numrecurse = 0; - -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 -//=========================================================================== -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -node_t *firstnode; //first node in the list -node_t *lastnode; //last node in the list -int nodelistsize; //number of nodes in the list -int use_nodequeue = 0; //use nodequeue, otherwise a node stack is used -int numwaiting = 0; - -void (*AddNodeToList)(node_t *node); - -//add the node to the front of the node list -//(effectively using a node stack) -void AddNodeToStack(node_t *node) -{ - ThreadLock(); - - node->next = firstnode; - firstnode = node; - if (!lastnode) lastnode = node; - nodelistsize++; - - ThreadUnlock(); - // - ThreadSemaphoreIncrease(1); -} //end of the function AddNodeToStack -//add the node to the end of the node list -//(effectively using a node queue) -void AddNodeToQueue(node_t *node) -{ - ThreadLock(); - - node->next = NULL; - if (lastnode) lastnode->next = node; - else firstnode = node; - lastnode = node; - nodelistsize++; - - ThreadUnlock(); - // - ThreadSemaphoreIncrease(1); -} //end of the function AddNodeToQueue -//get the first node from the front of the node list -node_t *NextNodeFromList(void) -{ - node_t *node; - - ThreadLock(); - numwaiting++; - if (!firstnode) - { - if (numwaiting >= GetNumThreads()) ThreadSemaphoreIncrease(GetNumThreads()); - } //end if - ThreadUnlock(); - - ThreadSemaphoreWait(); - - ThreadLock(); - - numwaiting--; - - node = firstnode; - if (firstnode) - { - firstnode = firstnode->next; - nodelistsize--; - } //end if - if (!firstnode) lastnode = NULL; - - ThreadUnlock(); - - return node; -} //end of the function NextNodeFromList -//returns the size of the node list -int NodeListSize(void) -{ - int size; - - ThreadLock(); - size = nodelistsize; - ThreadUnlock(); - - return size; -} //end of the function NodeListSize -// -void IncreaseNodeCounter(void) -{ - ThreadLock(); - //if (verbose) printf("\r%6d", numrecurse++); - qprintf("\r%6d", numrecurse++); - //qprintf("\r%6d %d, %5d ", numrecurse++, GetNumThreads(), nodelistsize); - ThreadUnlock(); -} //end of the function IncreaseNodeCounter -//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 -//=========================================================================== -// build the bsp tree using a node list -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -void BuildTree(tree_t *tree) -{ - int i; - - firstnode = NULL; - lastnode = NULL; - //use a node queue or node stack - if (use_nodequeue) AddNodeToList = AddNodeToQueue; - else AddNodeToList = AddNodeToStack; - //setup thread locking - ThreadSetupLock(); - ThreadSetupSemaphore(); - numwaiting = 0; - // - Log_Print("%6d threads max\n", numthreads); - if (use_nodequeue) Log_Print("breadth first bsp building\n"); - else Log_Print("depth first bsp building\n"); - qprintf("%6d splits", 0); - //add the first node to the list - AddNodeToList(tree->headnode); - //start the threads - for (i = 0; i < numthreads; i++) - AddThread(BuildTreeThread); - //wait for all added threads to be finished - WaitForAllThreadsFinished(); - //shutdown the thread locking - ThreadShutdownLock(); - ThreadShutdownSemaphore(); -} //end of the function BuildTree -//=========================================================================== -// The incoming brush list will be freed before exiting -// -// Parameter: - -// Returns: - -// Changes Globals: - -//=========================================================================== -tree_t *BrushBSP(bspbrush_t *brushlist, vec3_t mins, vec3_t maxs) -{ - int i, c_faces, c_nonvisfaces, c_brushes; - bspbrush_t *b; - node_t *node; - tree_t *tree; - vec_t volume; -// vec3_t point; - - Log_Print("-------- Brush BSP ---------\n"); - - tree = Tree_Alloc(); - - c_faces = 0; - c_nonvisfaces = 0; - c_brushes = 0; - c_totalsides = 0; - for (b = brushlist; b; b = b->next) - { - c_brushes++; - - volume = BrushVolume(b); - if (volume < microvolume) - { - Log_Print("WARNING: entity %i, brush %i: microbrush\n", - b->original->entitynum, b->original->brushnum); - } //end if - - for (i=0 ; inumsides ; i++) - { - if (b->sides[i].flags & SFL_BEVEL) - continue; - if (!b->sides[i].winding) - continue; - if (b->sides[i].texinfo == TEXINFO_NODE) - continue; - if (b->sides[i].flags & SFL_VISIBLE) - { - c_faces++; - } //end if - else - { - c_nonvisfaces++; - //if (create_aas) b->sides[i].texinfo = TEXINFO_NODE; - } //end if - } //end for - c_totalsides += b->numsides; - - AddPointToBounds (b->mins, tree->mins, tree->maxs); - AddPointToBounds (b->maxs, tree->mins, tree->maxs); - } //end for - - Log_Print("%6i brushes\n", c_brushes); - Log_Print("%6i visible faces\n", c_faces); - Log_Print("%6i nonvisible faces\n", c_nonvisfaces); - Log_Print("%6i total sides\n", c_totalsides); - - c_active_brushes = c_brushes; - c_nodememory = 0; - c_brushmemory = 0; - c_peak_brushmemory = 0; - - c_nodes = 0; - c_nonvis = 0; - node = AllocNode (); - - //volume of first node (head node) - node->volume = BrushFromBounds (mins, maxs); - // - tree->headnode = node; - //just get some statistics and the mins/maxs of the node - numrecurse = 0; -// qprintf("%6d splits", numrecurse); - - tree->headnode->brushlist = brushlist; - BuildTree(tree); - - //build the bsp tree with the start node from the brushlist -// node = BuildTree_r(node, brushlist); - - //if the conversion is cancelled - if (cancelconversion) return tree; - - qprintf("\n"); - Log_Write("%6d splits\r\n", numrecurse); -// Log_Print("%6i visible nodes\n", c_nodes/2 - c_nonvis); -// Log_Print("%6i nonvis nodes\n", c_nonvis); -// Log_Print("%6i leaves\n", (c_nodes+1)/2); -// Log_Print("%6i solid leaf nodes\n", c_solidleafnodes); -// Log_Print("%6i active brushes\n", c_active_brushes); - if (numthreads == 1) - { -// Log_Print("%6i KB of node memory\n", c_nodememory >> 10); -// Log_Print("%6i KB of brush memory\n", c_brushmemory >> 10); -// Log_Print("%6i KB of peak brush memory\n", c_peak_brushmemory >> 10); -// Log_Print("%6i KB of winding memory\n", WindingMemory() >> 10); -// Log_Print("%6i KB of peak winding memory\n", WindingPeakMemory() >> 10); - Log_Print("%6i KB of peak total bsp memory\n", c_peak_totalbspmemory >> 10); - } //end if - - /* - point[0] = 1485; - point[1] = 956.125; - point[2] = 352.125; - node = PointInLeaf(tree->headnode, point); - if (node->planenum != PLANENUM_LEAF) - { - Log_Print("node not a leaf\n"); - } //end if - Log_Print("at %f %f %f:\n", point[0], point[1], point[2]); - PrintContents(node->contents); - Log_Print("node->expansionbboxes = %d\n", node->expansionbboxes); - //*/ - return tree; -} //end of the function BrushBSP - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "qbsp.h" +#include "l_mem.h" +#include "../botlib/aasfile.h" +#include "aas_store.h" +#include "aas_cfg.h" + +#include + +/* +each side has a count of the other sides it splits + +the best split will be the one that minimizes the total split counts +of all remaining sides + +precalc side on plane table + +evaluate split side +{ +cost = 0 +for all sides + for all sides + get + if side splits side and splitside is on same child + cost++; +} +*/ + +int c_nodes; +int c_nonvis; +int c_active_brushes; +int c_solidleafnodes; +int c_totalsides; +int c_brushmemory; +int c_peak_brushmemory; +int c_nodememory; +int c_peak_totalbspmemory; + +// if a brush just barely pokes onto the other side, +// let it slide by without chopping +#define PLANESIDE_EPSILON 0.001 +//0.1 + +//#ifdef DEBUG +typedef struct cname_s +{ + int value; + char *name; +} cname_t; + +cname_t contentnames[] = +{ + {CONTENTS_SOLID,"CONTENTS_SOLID"}, + {CONTENTS_WINDOW,"CONTENTS_WINDOW"}, + {CONTENTS_AUX,"CONTENTS_AUX"}, + {CONTENTS_LAVA,"CONTENTS_LAVA"}, + {CONTENTS_SLIME,"CONTENTS_SLIME"}, + {CONTENTS_WATER,"CONTENTS_WATER"}, + {CONTENTS_MIST,"CONTENTS_MIST"}, + {LAST_VISIBLE_CONTENTS,"LAST_VISIBLE_CONTENTS"}, + + {CONTENTS_AREAPORTAL,"CONTENTS_AREAPORTAL"}, + {CONTENTS_PLAYERCLIP,"CONTENTS_PLAYERCLIP"}, + {CONTENTS_MONSTERCLIP,"CONTENTS_MONSTERCLIP"}, + {CONTENTS_CURRENT_0,"CONTENTS_CURRENT_0"}, + {CONTENTS_CURRENT_90,"CONTENTS_CURRENT_90"}, + {CONTENTS_CURRENT_180,"CONTENTS_CURRENT_180"}, + {CONTENTS_CURRENT_270,"CONTENTS_CURRENT_270"}, + {CONTENTS_CURRENT_UP,"CONTENTS_CURRENT_UP"}, + {CONTENTS_CURRENT_DOWN,"CONTENTS_CURRENT_DOWN"}, + {CONTENTS_ORIGIN,"CONTENTS_ORIGIN"}, + {CONTENTS_MONSTER,"CONTENTS_MONSTER"}, + {CONTENTS_DEADMONSTER,"CONTENTS_DEADMONSTER"}, + {CONTENTS_DETAIL,"CONTENTS_DETAIL"}, + {CONTENTS_Q2TRANSLUCENT,"CONTENTS_TRANSLUCENT"}, + {CONTENTS_LADDER,"CONTENTS_LADDER"}, + {0, 0} +}; + +void PrintContents(int contents) +{ + int i; + + for (i = 0; contentnames[i].value; i++) + { + if (contents & contentnames[i].value) + { + Log_Write("%s,", contentnames[i].name); + } //end if + } //end for +} //end of the function PrintContents + +//#endif DEBUG + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ResetBrushBSP(void) +{ + c_nodes = 0; + c_nonvis = 0; + c_active_brushes = 0; + c_solidleafnodes = 0; + c_totalsides = 0; + c_brushmemory = 0; + c_peak_brushmemory = 0; + c_nodememory = 0; + c_peak_totalbspmemory = 0; +} //end of the function ResetBrushBSP +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FindBrushInTree (node_t *node, int brushnum) +{ + bspbrush_t *b; + + if (node->planenum == PLANENUM_LEAF) + { + for (b=node->brushlist ; b ; b=b->next) + if (b->original->brushnum == brushnum) + Log_Print ("here\n"); + return; + } + FindBrushInTree(node->children[0], brushnum); + FindBrushInTree(node->children[1], brushnum); +} //end of the function FindBrushInTree +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void DrawBrushList (bspbrush_t *brush, node_t *node) +{ + int i; + side_t *s; + + GLS_BeginScene (); + for ( ; brush ; brush=brush->next) + { + for (i=0 ; inumsides ; i++) + { + s = &brush->sides[i]; + if (!s->winding) + continue; + if (s->texinfo == TEXINFO_NODE) + GLS_Winding (s->winding, 1); + else if (!(s->flags & SFL_VISIBLE)) + GLS_Winding (s->winding, 2); + else + GLS_Winding (s->winding, 0); + } + } + GLS_EndScene (); +} //end of the function DrawBrushList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis) +{ + int i; + side_t *s; + FILE *f; + + qprintf ("writing %s\n", name); + f = SafeOpenWrite (name); + + for ( ; brush ; brush=brush->next) + { + for (i=0 ; inumsides ; i++) + { + s = &brush->sides[i]; + if (!s->winding) + continue; + if (onlyvis && !(s->flags & SFL_VISIBLE)) + continue; + OutputWinding (brush->sides[i].winding, f); + } + } + + fclose (f); +} //end of the function WriteBrushList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintBrush (bspbrush_t *brush) +{ + int i; + + printf ("brush: %p\n", brush); + for (i=0;inumsides ; i++) + { + pw(brush->sides[i].winding); + printf ("\n"); + } //end for +} //end of the function PrintBrush +//=========================================================================== +// Sets the mins/maxs based on the windings +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BoundBrush (bspbrush_t *brush) +{ + int i, j; + winding_t *w; + + ClearBounds (brush->mins, brush->maxs); + for (i=0 ; inumsides ; i++) + { + w = brush->sides[i].winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; j++) + AddPointToBounds (w->p[j], brush->mins, brush->maxs); + } +} //end of the function BoundBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CreateBrushWindings (bspbrush_t *brush) +{ + int i, j; + winding_t *w; + side_t *side; + plane_t *plane; + + for (i=0 ; inumsides ; i++) + { + side = &brush->sides[i]; + plane = &mapplanes[side->planenum]; + w = BaseWindingForPlane (plane->normal, plane->dist); + for (j=0 ; jnumsides && w; j++) + { + if (i == j) + continue; + if (brush->sides[j].flags & SFL_BEVEL) + continue; + plane = &mapplanes[brush->sides[j].planenum^1]; + ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON); + } + + side->winding = w; + } + + BoundBrush (brush); +} //end of the function CreateBrushWindings +//=========================================================================== +// Creates a new axial brush +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs) +{ + bspbrush_t *b; + int i; + vec3_t normal; + vec_t dist; + + b = AllocBrush (6); + b->numsides = 6; + for (i=0 ; i<3 ; i++) + { + VectorClear (normal); + normal[i] = 1; + dist = maxs[i]; + b->sides[i].planenum = FindFloatPlane (normal, dist); + + normal[i] = -1; + dist = -mins[i]; + b->sides[3+i].planenum = FindFloatPlane (normal, dist); + } + + CreateBrushWindings (b); + + return b; +} //end of the function BrushFromBounds +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BrushOutOfBounds(bspbrush_t *brush, vec3_t mins, vec3_t maxs, float epsilon) +{ + int i, j, n; + winding_t *w; + side_t *side; + + for (i = 0; i < brush->numsides; i++) + { + side = &brush->sides[i]; + w = side->winding; + for (j = 0; j < w->numpoints; j++) + { + for (n = 0; n < 3; n++) + { + if (w->p[j][n] < (mins[n] + epsilon) || w->p[j][n] > (maxs[n] - epsilon)) return true; + } //end for + } //end for + } //end for + return false; +} //end of the function BrushOutOfBounds +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +vec_t BrushVolume (bspbrush_t *brush) +{ + int i; + winding_t *w; + vec3_t corner; + vec_t d, area, volume; + plane_t *plane; + + if (!brush) return 0; + + // grab the first valid point as the corner + w = NULL; + for (i = 0; i < brush->numsides; i++) + { + w = brush->sides[i].winding; + if (w) break; + } //end for + if (!w) return 0; + VectorCopy (w->p[0], corner); + + // make tetrahedrons to all other faces + volume = 0; + for ( ; i < brush->numsides; i++) + { + w = brush->sides[i].winding; + if (!w) continue; + plane = &mapplanes[brush->sides[i].planenum]; + d = -(DotProduct (corner, plane->normal) - plane->dist); + area = WindingArea(w); + volume += d * area; + } //end for + + volume /= 3; + return volume; +} //end of the function BrushVolume +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int CountBrushList (bspbrush_t *brushes) +{ + int c; + + c = 0; + for ( ; brushes; brushes = brushes->next) c++; + return c; +} //end of the function CountBrushList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +node_t *AllocNode (void) +{ + node_t *node; + + node = GetMemory(sizeof(*node)); + memset (node, 0, sizeof(*node)); + if (numthreads == 1) + { + c_nodememory += MemorySize(node); + } //end if + return node; +} //end of the function AllocNode +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *AllocBrush (int numsides) +{ + bspbrush_t *bb; + int c; + + c = (int)&(((bspbrush_t *)0)->sides[numsides]); + bb = GetMemory(c); + memset (bb, 0, c); + if (numthreads == 1) + { + c_active_brushes++; + c_brushmemory += MemorySize(bb); + if (c_brushmemory > c_peak_brushmemory) + c_peak_brushmemory = c_brushmemory; + } //end if + return bb; +} //end of the function AllocBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeBrush (bspbrush_t *brushes) +{ + int i; + + for (i=0 ; inumsides ; i++) + if (brushes->sides[i].winding) + FreeWinding(brushes->sides[i].winding); + if (numthreads == 1) + { + c_active_brushes--; + c_brushmemory -= MemorySize(brushes); + if (c_brushmemory < 0) c_brushmemory = 0; + } //end if + FreeMemory(brushes); +} //end of the function FreeBrush +//=========================================================================== +// +// 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 +//=========================================================================== +// Duplicates the brush, the sides, and the windings +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *CopyBrush (bspbrush_t *brush) +{ + bspbrush_t *newbrush; + int size; + int i; + + size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]); + + newbrush = AllocBrush (brush->numsides); + memcpy (newbrush, brush, size); + + for (i=0 ; inumsides ; i++) + { + if (brush->sides[i].winding) + newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding); + } + + return newbrush; +} //end of the function CopyBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +node_t *PointInLeaf (node_t *node, vec3_t point) +{ + vec_t d; + plane_t *plane; + + while (node->planenum != PLANENUM_LEAF) + { + plane = &mapplanes[node->planenum]; + d = DotProduct (point, plane->normal) - plane->dist; + if (d > 0) + node = node->children[0]; + else + node = node->children[1]; + } + + return node; +} //end of the function PointInLeaf +//=========================================================================== +// Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#if 0 +int BoxOnPlaneSide (vec3_t mins, vec3_t maxs, plane_t *plane) +{ + int side; + int i; + vec3_t corners[2]; + vec_t dist1, dist2; + + // axial planes are easy + if (plane->type < 3) + { + side = 0; + if (maxs[plane->type] > plane->dist+PLANESIDE_EPSILON) + side |= PSIDE_FRONT; + if (mins[plane->type] < plane->dist-PLANESIDE_EPSILON) + side |= PSIDE_BACK; + return side; + } + + // create the proper leading and trailing verts for the box + + for (i=0 ; i<3 ; i++) + { + if (plane->normal[i] < 0) + { + corners[0][i] = mins[i]; + corners[1][i] = maxs[i]; + } + else + { + corners[1][i] = mins[i]; + corners[0][i] = maxs[i]; + } + } + + dist1 = DotProduct (plane->normal, corners[0]) - plane->dist; + dist2 = DotProduct (plane->normal, corners[1]) - plane->dist; + side = 0; + if (dist1 >= PLANESIDE_EPSILON) + side = PSIDE_FRONT; + if (dist2 < PLANESIDE_EPSILON) + side |= PSIDE_BACK; + + return side; +} +#else +int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, plane_t *p) +{ + float dist1, dist2; + int sides; + + // axial planes are easy + if (p->type < 3) + { + sides = 0; + if (emaxs[p->type] > p->dist+PLANESIDE_EPSILON) sides |= PSIDE_FRONT; + if (emins[p->type] < p->dist-PLANESIDE_EPSILON) sides |= PSIDE_BACK; + return sides; + } //end if + +// general case + switch (p->signbits) + { + case 0: + dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; + dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; + break; + case 1: + dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; + dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; + break; + case 2: + dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; + dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; + break; + case 3: + dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; + dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; + break; + case 4: + dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; + dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; + break; + case 5: + dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; + dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; + break; + case 6: + dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; + dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; + break; + case 7: + dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; + dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; + break; + default: + dist1 = dist2 = 0; // shut up compiler +// assert( 0 ); + break; + } + + sides = 0; + if (dist1 - p->dist >= PLANESIDE_EPSILON) sides = PSIDE_FRONT; + if (dist2 - p->dist < PLANESIDE_EPSILON) sides |= PSIDE_BACK; + +// assert(sides != 0); + + return sides; +} +#endif +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int QuickTestBrushToPlanenum (bspbrush_t *brush, int planenum, int *numsplits) +{ + int i, num; + plane_t *plane; + int s; + + *numsplits = 0; + + plane = &mapplanes[planenum]; + +#ifdef ME + //fast axial cases + if (plane->type < 3) + { + if (plane->dist + PLANESIDE_EPSILON < brush->mins[plane->type]) + return PSIDE_FRONT; + if (plane->dist - PLANESIDE_EPSILON > brush->maxs[plane->type]) + return PSIDE_BACK; + } //end if +#endif //ME*/ + + // if the brush actually uses the planenum, + // we can tell the side for sure + for (i = 0; i < brush->numsides; i++) + { + num = brush->sides[i].planenum; + if (num >= MAX_MAPFILE_PLANES) + Error ("bad planenum"); + if (num == planenum) + return PSIDE_BACK|PSIDE_FACING; + if (num == (planenum ^ 1) ) + return PSIDE_FRONT|PSIDE_FACING; + + } + + // box on plane side + s = BoxOnPlaneSide (brush->mins, brush->maxs, plane); + + // if both sides, count the visible faces split + if (s == PSIDE_BOTH) + { + *numsplits += 3; + } + + return s; +} //end of the function QuickTestBrushToPlanenum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int TestBrushToPlanenum (bspbrush_t *brush, int planenum, + int *numsplits, qboolean *hintsplit, int *epsilonbrush) +{ + int i, j, num; + plane_t *plane; + int s = 0; + winding_t *w; + vec_t d, d_front, d_back; + int front, back; + int type; + float dist; + + *numsplits = 0; + *hintsplit = false; + + plane = &mapplanes[planenum]; + +#ifdef ME + //fast axial cases + type = plane->type; + if (type < 3) + { + dist = plane->dist; + if (dist + PLANESIDE_EPSILON < brush->mins[type]) return PSIDE_FRONT; + if (dist - PLANESIDE_EPSILON > brush->maxs[type]) return PSIDE_BACK; + if (brush->mins[type] < dist - PLANESIDE_EPSILON && + brush->maxs[type] > dist + PLANESIDE_EPSILON) s = PSIDE_BOTH; + } //end if + + if (s != PSIDE_BOTH) +#endif //ME + { + // if the brush actually uses the planenum, + // we can tell the side for sure + for (i = 0; i < brush->numsides; i++) + { + num = brush->sides[i].planenum; + if (num >= MAX_MAPFILE_PLANES) Error ("bad planenum"); + if (num == planenum) + { + //we don't need to test this side plane again + brush->sides[i].flags |= SFL_TESTED; + return PSIDE_BACK|PSIDE_FACING; + } //end if + if (num == (planenum ^ 1) ) + { + //we don't need to test this side plane again + brush->sides[i].flags |= SFL_TESTED; + return PSIDE_FRONT|PSIDE_FACING; + } //end if + } //end for + + // box on plane side + s = BoxOnPlaneSide (brush->mins, brush->maxs, plane); + + if (s != PSIDE_BOTH) return s; + } //end if + + // if both sides, count the visible faces split + d_front = d_back = 0; + + for (i = 0; i < brush->numsides; i++) + { + if (brush->sides[i].texinfo == TEXINFO_NODE) + continue; // on node, don't worry about splits + if (!(brush->sides[i].flags & SFL_VISIBLE)) + continue; // we don't care about non-visible + w = brush->sides[i].winding; + if (!w) continue; + front = back = 0; + for (j = 0; j < w->numpoints; j++) + { + d = DotProduct(w->p[j], plane->normal) - plane->dist; + if (d > d_front) d_front = d; + if (d < d_back) d_back = d; + if (d > 0.1) // PLANESIDE_EPSILON) + front = 1; + if (d < -0.1) // PLANESIDE_EPSILON) + back = 1; + } //end for + if (front && back) + { + if ( !(brush->sides[i].surf & SURF_SKIP) ) + { + (*numsplits)++; + if (brush->sides[i].surf & SURF_HINT) + { + *hintsplit = true; + } //end if + } //end if + } //end if + } //end for + + if ( (d_front > 0.0 && d_front < 1.0) + || (d_back < 0.0 && d_back > -1.0) ) + (*epsilonbrush)++; + +#if 0 + if (*numsplits == 0) + { // didn't really need to be split + if (front) s = PSIDE_FRONT; + else if (back) s = PSIDE_BACK; + else s = 0; + } +#endif + + return s; +} //end of the function TestBrushToPlanenum +//=========================================================================== +// Returns true if the winding would be crunched out of +// existance by the vertex snapping. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#define EDGE_LENGTH 0.2 +qboolean WindingIsTiny (winding_t *w) +{ +#if 0 + if (WindingArea (w) < 1) + return true; + return false; +#else + int i, j; + vec_t len; + vec3_t delta; + int edges; + + edges = 0; + for (i=0 ; inumpoints ; i++) + { + j = i == w->numpoints - 1 ? 0 : i+1; + VectorSubtract (w->p[j], w->p[i], delta); + len = VectorLength (delta); + if (len > EDGE_LENGTH) + { + if (++edges == 3) + return false; + } + } + return true; +#endif +} //end of the function WindingIsTiny +//=========================================================================== +// Returns true if the winding still has one of the points +// from basewinding for plane +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WindingIsHuge (winding_t *w) +{ + int i, j; + + for (i=0 ; inumpoints ; i++) + { + for (j=0 ; j<3 ; j++) + if (w->p[i][j] < -BOGUS_RANGE+1 || w->p[i][j] > BOGUS_RANGE-1) + return true; + } + return false; +} //end of the function WindingIsHuge +//=========================================================================== +// creates a leaf out of the given nodes with the given brushes +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void LeafNode(node_t *node, bspbrush_t *brushes) +{ + bspbrush_t *b; + int i; + + node->side = NULL; + node->planenum = PLANENUM_LEAF; + node->contents = 0; + + for (b = brushes; b; b = b->next) + { + // if the brush is solid and all of its sides are on nodes, + // it eats everything + if (b->original->contents & CONTENTS_SOLID) + { + for (i=0 ; inumsides ; i++) + if (b->sides[i].texinfo != TEXINFO_NODE) + break; + if (i == b->numsides) + { + node->contents = CONTENTS_SOLID; + break; + } //end if + } //end if + node->contents |= b->original->contents; + } //end for + + if (create_aas) + { + node->expansionbboxes = 0; + node->contents = 0; + for (b = brushes; b; b = b->next) + { + node->expansionbboxes |= b->original->expansionbbox; + node->contents |= b->original->contents; + if (b->original->modelnum) + node->modelnum = b->original->modelnum; + } //end for + if (node->contents & CONTENTS_SOLID) + { + if (node->expansionbboxes != cfg.allpresencetypes) + { + node->contents &= ~CONTENTS_SOLID; + } //end if + } //end if + } //end if + + node->brushlist = brushes; +} //end of the function LeafNode +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CheckPlaneAgainstParents (int pnum, node_t *node) +{ + node_t *p; + + for (p = node->parent; p; p = p->parent) + { + if (p->planenum == pnum) Error("Tried parent"); + } //end for +} //end of the function CheckPlaneAgainstParants +//=========================================================================== +// +// 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 +//=========================================================================== +// Using a hueristic, choses one of the sides out of the brushlist +// to partition the brushes with. +// Returns NULL if there are no valid planes to split with.. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +side_t *SelectSplitSide (bspbrush_t *brushes, node_t *node) +{ + int value, bestvalue; + bspbrush_t *brush, *test; + side_t *side, *bestside; + int i, pass, numpasses; + int pnum; + int s; + int front, back, both, facing, splits; + int bsplits; + int bestsplits; + int epsilonbrush; + qboolean hintsplit = false; + + bestside = NULL; + bestvalue = -99999; + bestsplits = 0; + + // the search order goes: visible-structural, visible-detail, + // nonvisible-structural, nonvisible-detail. + // If any valid plane is available in a pass, no further + // passes will be tried. + numpasses = 2; + for (pass = 0; pass < numpasses; pass++) + { + for (brush = brushes; brush; brush = brush->next) + { + // only check detail the second pass +// if ( (pass & 1) && !(brush->original->contents & CONTENTS_DETAIL) ) +// continue; +// if ( !(pass & 1) && (brush->original->contents & CONTENTS_DETAIL) ) +// continue; + for (i = 0; i < brush->numsides; i++) + { + side = brush->sides + i; +// if (side->flags & SFL_BEVEL) +// continue; // never use a bevel as a spliter + if (!side->winding) + continue; // nothing visible, so it can't split + if (side->texinfo == TEXINFO_NODE) + continue; // allready a node splitter + if (side->flags & SFL_TESTED) + continue; // we allready have metrics for this plane +// if (side->surf & SURF_SKIP) +// continue; // skip surfaces are never chosen + +// if (!(side->flags & SFL_VISIBLE) && (pass < 2)) +// continue; // only check visible faces on first pass + + if ((side->flags & SFL_CURVE) && (pass < 1)) + continue; // only check curves the second pass + + pnum = side->planenum; + pnum &= ~1; // allways use positive facing plane + + CheckPlaneAgainstParents (pnum, node); + + if (!CheckPlaneAgainstVolume (pnum, node)) + continue; // would produce a tiny volume + + front = 0; + back = 0; + both = 0; + facing = 0; + splits = 0; + epsilonbrush = 0; + + //inner loop: optimize + for (test = brushes; test; test = test->next) + { + s = TestBrushToPlanenum (test, pnum, &bsplits, &hintsplit, &epsilonbrush); + + splits += bsplits; +// if (bsplits && (s&PSIDE_FACING) ) +// Error ("PSIDE_FACING with splits"); + + test->testside = s; + // + if (s & PSIDE_FACING) facing++; + if (s & PSIDE_FRONT) front++; + if (s & PSIDE_BACK) back++; + if (s == PSIDE_BOTH) both++; + } //end for + + // give a value estimate for using this plane + value = 5*facing - 5*splits - abs(front-back); +// value = -5*splits; +// value = 5*facing - 5*splits; + if (mapplanes[pnum].type < 3) + value+=5; // axial is better + + value -= epsilonbrush * 1000; // avoid! + + // never split a hint side except with another hint + if (hintsplit && !(side->surf & SURF_HINT) ) + value = -9999999; + + // save off the side test so we don't need + // to recalculate it when we actually seperate + // the brushes + if (value > bestvalue) + { + bestvalue = value; + bestside = side; + bestsplits = splits; + for (test = brushes; test ; test = test->next) + test->side = test->testside; + } //end if + } //end for + } //end for (brush = brushes; + + // if we found a good plane, don't bother trying any + // other passes + if (bestside) + { + if (pass > 1) + { + if (numthreads == 1) c_nonvis++; + } + if (pass > 0) node->detail_seperator = true; // not needed for vis + break; + } //end if + } //end for (pass = 0; + + // + // clear all the tested flags we set + // + for (brush = brushes ; brush ; brush=brush->next) + { + for (i = 0; i < brush->numsides; i++) + { + brush->sides[i].flags &= ~SFL_TESTED; + } //end for + } //end for + + return bestside; +} //end of the function SelectSplitSide +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane) +{ + int i, j; + winding_t *w; + vec_t d, max; + int side; + + max = 0; + side = PSIDE_FRONT; + for (i=0 ; inumsides ; i++) + { + w = brush->sides[i].winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; j++) + { + d = DotProduct (w->p[j], plane->normal) - plane->dist; + if (d > max) + { + max = d; + side = PSIDE_FRONT; + } + if (-d > max) + { + max = -d; + side = PSIDE_BACK; + } + } + } + return side; +} //end of the function BrushMostlyOnSide +//=========================================================================== +// Generates two new brushes, leaving the original +// unchanged +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void SplitBrush (bspbrush_t *brush, int planenum, + bspbrush_t **front, bspbrush_t **back) +{ + bspbrush_t *b[2]; + int i, j; + winding_t *w, *cw[2], *midwinding; + plane_t *plane, *plane2; + side_t *s, *cs; + float d, d_front, d_back; + + *front = *back = NULL; + plane = &mapplanes[planenum]; + + // check all points + d_front = d_back = 0; + for (i=0 ; inumsides ; i++) + { + w = brush->sides[i].winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; j++) + { + d = DotProduct (w->p[j], plane->normal) - plane->dist; + if (d > 0 && d > d_front) + d_front = d; + if (d < 0 && d < d_back) + d_back = d; + } + } + + if (d_front < 0.2) // PLANESIDE_EPSILON) + { // only on back + *back = CopyBrush (brush); + return; + } + if (d_back > -0.2) // PLANESIDE_EPSILON) + { // only on front + *front = CopyBrush (brush); + return; + } + + // create a new winding from the split plane + + w = BaseWindingForPlane (plane->normal, plane->dist); + for (i=0 ; inumsides && w ; i++) + { + plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; + ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON); + } + + if (!w || WindingIsTiny(w)) + { // the brush isn't really split + int side; + + side = BrushMostlyOnSide (brush, plane); + if (side == PSIDE_FRONT) + *front = CopyBrush (brush); + if (side == PSIDE_BACK) + *back = CopyBrush (brush); + //free a possible winding + if (w) FreeWinding(w); + return; + } + + if (WindingIsHuge (w)) + { + Log_Write("WARNING: huge winding\n"); + } + + midwinding = w; + + // split it for real + + for (i=0 ; i<2 ; i++) + { + b[i] = AllocBrush (brush->numsides+1); + b[i]->original = brush->original; + } + + // split all the current windings + + for (i=0 ; inumsides ; i++) + { + s = &brush->sides[i]; + w = s->winding; + if (!w) + continue; + ClipWindingEpsilon (w, plane->normal, plane->dist, + 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); + for (j=0 ; j<2 ; j++) + { + if (!cw[j]) + continue; +#if 0 + if (WindingIsTiny (cw[j])) + { + FreeWinding (cw[j]); + continue; + } +#endif + cs = &b[j]->sides[b[j]->numsides]; + b[j]->numsides++; + *cs = *s; +// cs->planenum = s->planenum; +// cs->texinfo = s->texinfo; +// cs->original = s->original; + cs->winding = cw[j]; + cs->flags &= ~SFL_TESTED; + } + } + + + // see if we have valid polygons on both sides + + for (i=0 ; i<2 ; i++) + { + BoundBrush (b[i]); + for (j=0 ; j<3 ; j++) + { + if (b[i]->mins[j] < -MAX_MAP_BOUNDS || b[i]->maxs[j] > MAX_MAP_BOUNDS) + { + Log_Write("bogus brush after clip"); + break; + } + } + + if (b[i]->numsides < 3 || j < 3) + { + FreeBrush (b[i]); + b[i] = NULL; + } + } + + if ( !(b[0] && b[1]) ) + { + if (!b[0] && !b[1]) + Log_Write("split removed brush\r\n"); + else + Log_Write("split not on both sides\r\n"); + if (b[0]) + { + FreeBrush (b[0]); + *front = CopyBrush (brush); + } + if (b[1]) + { + FreeBrush (b[1]); + *back = CopyBrush (brush); + } + return; + } + + // add the midwinding to both sides + for (i=0 ; i<2 ; i++) + { + cs = &b[i]->sides[b[i]->numsides]; + b[i]->numsides++; + + cs->planenum = planenum^i^1; + cs->texinfo = TEXINFO_NODE; //never use these sides as splitters + cs->flags &= ~SFL_VISIBLE; + cs->flags &= ~SFL_TESTED; + if (i==0) + cs->winding = CopyWinding (midwinding); + else + cs->winding = midwinding; + } + +{ + vec_t v1; + int i; + + for (i = 0; i < 2; i++) + { + v1 = BrushVolume (b[i]); + if (v1 < 1.0) + { + FreeBrush(b[i]); + b[i] = NULL; + //Log_Write("tiny volume after clip"); + } + } + if (!b[0] && !b[1]) + { + Log_Write("two tiny brushes\r\n"); + } //end if +} + + *front = b[0]; + *back = b[1]; +} //end of the function SplitBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void SplitBrushList (bspbrush_t *brushes, + node_t *node, bspbrush_t **front, bspbrush_t **back) +{ + bspbrush_t *brush, *newbrush, *newbrush2; + side_t *side; + int sides; + int i; + + *front = *back = NULL; + + for (brush = brushes; brush; brush = brush->next) + { + sides = brush->side; + + if (sides == PSIDE_BOTH) + { // split into two brushes + SplitBrush (brush, node->planenum, &newbrush, &newbrush2); + if (newbrush) + { + newbrush->next = *front; + *front = newbrush; + } //end if + if (newbrush2) + { + newbrush2->next = *back; + *back = newbrush2; + } //end if + continue; + } //end if + + newbrush = CopyBrush (brush); + + // if the planenum is actualy a part of the brush + // find the plane and flag it as used so it won't be tried + // as a splitter again + if (sides & PSIDE_FACING) + { + for (i=0 ; inumsides ; i++) + { + side = newbrush->sides + i; + if ( (side->planenum& ~1) == node->planenum) + side->texinfo = TEXINFO_NODE; + } //end for + } //end if + if (sides & PSIDE_FRONT) + { + newbrush->next = *front; + *front = newbrush; + continue; + } //end if + if (sides & PSIDE_BACK) + { + newbrush->next = *back; + *back = newbrush; + continue; + } //end if + } //end for +} //end of the function SplitBrushList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CheckBrushLists(bspbrush_t *brushlist1, bspbrush_t *brushlist2) +{ + bspbrush_t *brush1, *brush2; + + for (brush1 = brushlist1; brush1; brush1 = brush1->next) + { + for (brush2 = brushlist2; brush2; brush2 = brush2->next) + { + assert(brush1 != brush2); + } //end for + } //end for +} //end of the function CheckBrushLists +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int numrecurse = 0; + +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 +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +node_t *firstnode; //first node in the list +node_t *lastnode; //last node in the list +int nodelistsize; //number of nodes in the list +int use_nodequeue = 0; //use nodequeue, otherwise a node stack is used +int numwaiting = 0; + +void (*AddNodeToList)(node_t *node); + +//add the node to the front of the node list +//(effectively using a node stack) +void AddNodeToStack(node_t *node) +{ + ThreadLock(); + + node->next = firstnode; + firstnode = node; + if (!lastnode) lastnode = node; + nodelistsize++; + + ThreadUnlock(); + // + ThreadSemaphoreIncrease(1); +} //end of the function AddNodeToStack +//add the node to the end of the node list +//(effectively using a node queue) +void AddNodeToQueue(node_t *node) +{ + ThreadLock(); + + node->next = NULL; + if (lastnode) lastnode->next = node; + else firstnode = node; + lastnode = node; + nodelistsize++; + + ThreadUnlock(); + // + ThreadSemaphoreIncrease(1); +} //end of the function AddNodeToQueue +//get the first node from the front of the node list +node_t *NextNodeFromList(void) +{ + node_t *node; + + ThreadLock(); + numwaiting++; + if (!firstnode) + { + if (numwaiting >= GetNumThreads()) ThreadSemaphoreIncrease(GetNumThreads()); + } //end if + ThreadUnlock(); + + ThreadSemaphoreWait(); + + ThreadLock(); + + numwaiting--; + + node = firstnode; + if (firstnode) + { + firstnode = firstnode->next; + nodelistsize--; + } //end if + if (!firstnode) lastnode = NULL; + + ThreadUnlock(); + + return node; +} //end of the function NextNodeFromList +//returns the size of the node list +int NodeListSize(void) +{ + int size; + + ThreadLock(); + size = nodelistsize; + ThreadUnlock(); + + return size; +} //end of the function NodeListSize +// +void IncreaseNodeCounter(void) +{ + ThreadLock(); + //if (verbose) printf("\r%6d", numrecurse++); + qprintf("\r%6d", numrecurse++); + //qprintf("\r%6d %d, %5d ", numrecurse++, GetNumThreads(), nodelistsize); + ThreadUnlock(); +} //end of the function IncreaseNodeCounter +//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 +//=========================================================================== +// build the bsp tree using a node list +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BuildTree(tree_t *tree) +{ + int i; + + firstnode = NULL; + lastnode = NULL; + //use a node queue or node stack + if (use_nodequeue) AddNodeToList = AddNodeToQueue; + else AddNodeToList = AddNodeToStack; + //setup thread locking + ThreadSetupLock(); + ThreadSetupSemaphore(); + numwaiting = 0; + // + Log_Print("%6d threads max\n", numthreads); + if (use_nodequeue) Log_Print("breadth first bsp building\n"); + else Log_Print("depth first bsp building\n"); + qprintf("%6d splits", 0); + //add the first node to the list + AddNodeToList(tree->headnode); + //start the threads + for (i = 0; i < numthreads; i++) + AddThread(BuildTreeThread); + //wait for all added threads to be finished + WaitForAllThreadsFinished(); + //shutdown the thread locking + ThreadShutdownLock(); + ThreadShutdownSemaphore(); +} //end of the function BuildTree +//=========================================================================== +// The incoming brush list will be freed before exiting +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tree_t *BrushBSP(bspbrush_t *brushlist, vec3_t mins, vec3_t maxs) +{ + int i, c_faces, c_nonvisfaces, c_brushes; + bspbrush_t *b; + node_t *node; + tree_t *tree; + vec_t volume; +// vec3_t point; + + Log_Print("-------- Brush BSP ---------\n"); + + tree = Tree_Alloc(); + + c_faces = 0; + c_nonvisfaces = 0; + c_brushes = 0; + c_totalsides = 0; + for (b = brushlist; b; b = b->next) + { + c_brushes++; + + volume = BrushVolume(b); + if (volume < microvolume) + { + Log_Print("WARNING: entity %i, brush %i: microbrush\n", + b->original->entitynum, b->original->brushnum); + } //end if + + for (i=0 ; inumsides ; i++) + { + if (b->sides[i].flags & SFL_BEVEL) + continue; + if (!b->sides[i].winding) + continue; + if (b->sides[i].texinfo == TEXINFO_NODE) + continue; + if (b->sides[i].flags & SFL_VISIBLE) + { + c_faces++; + } //end if + else + { + c_nonvisfaces++; + //if (create_aas) b->sides[i].texinfo = TEXINFO_NODE; + } //end if + } //end for + c_totalsides += b->numsides; + + AddPointToBounds (b->mins, tree->mins, tree->maxs); + AddPointToBounds (b->maxs, tree->mins, tree->maxs); + } //end for + + Log_Print("%6i brushes\n", c_brushes); + Log_Print("%6i visible faces\n", c_faces); + Log_Print("%6i nonvisible faces\n", c_nonvisfaces); + Log_Print("%6i total sides\n", c_totalsides); + + c_active_brushes = c_brushes; + c_nodememory = 0; + c_brushmemory = 0; + c_peak_brushmemory = 0; + + c_nodes = 0; + c_nonvis = 0; + node = AllocNode (); + + //volume of first node (head node) + node->volume = BrushFromBounds (mins, maxs); + // + tree->headnode = node; + //just get some statistics and the mins/maxs of the node + numrecurse = 0; +// qprintf("%6d splits", numrecurse); + + tree->headnode->brushlist = brushlist; + BuildTree(tree); + + //build the bsp tree with the start node from the brushlist +// node = BuildTree_r(node, brushlist); + + //if the conversion is cancelled + if (cancelconversion) return tree; + + qprintf("\n"); + Log_Write("%6d splits\r\n", numrecurse); +// Log_Print("%6i visible nodes\n", c_nodes/2 - c_nonvis); +// Log_Print("%6i nonvis nodes\n", c_nonvis); +// Log_Print("%6i leaves\n", (c_nodes+1)/2); +// Log_Print("%6i solid leaf nodes\n", c_solidleafnodes); +// Log_Print("%6i active brushes\n", c_active_brushes); + if (numthreads == 1) + { +// Log_Print("%6i KB of node memory\n", c_nodememory >> 10); +// Log_Print("%6i KB of brush memory\n", c_brushmemory >> 10); +// Log_Print("%6i KB of peak brush memory\n", c_peak_brushmemory >> 10); +// Log_Print("%6i KB of winding memory\n", WindingMemory() >> 10); +// Log_Print("%6i KB of peak winding memory\n", WindingPeakMemory() >> 10); + Log_Print("%6i KB of peak total bsp memory\n", c_peak_totalbspmemory >> 10); + } //end if + + /* + point[0] = 1485; + point[1] = 956.125; + point[2] = 352.125; + node = PointInLeaf(tree->headnode, point); + if (node->planenum != PLANENUM_LEAF) + { + Log_Print("node not a leaf\n"); + } //end if + Log_Print("at %f %f %f:\n", point[0], point[1], point[2]); + PrintContents(node->contents); + Log_Print("node->expansionbboxes = %d\n", node->expansionbboxes); + //*/ + return tree; +} //end of the function BrushBSP + -- cgit v1.2.3