brushbsp.c

Go to the documentation of this file.
00001 
00005 /*
00006 Copyright (C) 1997-2001 Id Software, Inc.
00007 
00008 This program is free software; you can redistribute it and/or
00009 modify it under the terms of the GNU General Public License
00010 as published by the Free Software Foundation; either version 2
00011 of the License, or (at your option) any later version.
00012 
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00016 
00017 See the GNU General Public License for more details.
00018 
00019 You should have received a copy of the GNU General Public License
00020 along with this program; if not, write to the Free Software
00021 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00022 
00023 */
00024 
00025 #include "map.h"
00026 #include "bsp.h"
00027 
00028 int c_nodes;
00029 static int c_nonvis;
00030 static int c_active_brushes;
00031 
00032 
00036 static void BoundBrush (bspbrush_t *brush)
00037 {
00038     int i, j;
00039     winding_t *w;
00040 
00041     ClearBounds(brush->mins, brush->maxs);
00042     for (i = 0; i < brush->numsides; i++) {
00043         w = brush->sides[i].winding;
00044         if (!w)
00045             continue;
00046         for (j = 0; j < w->numpoints; j++)
00047             AddPointToBounds(w->p[j], brush->mins, brush->maxs);
00048     }
00049 }
00050 
00051 #define SNAP_EPSILON    0.01
00052 
00057 static void SnapWeldVector (const vec3_t a, const vec3_t b, vec3_t out)
00058 {
00059     int i;
00060     vec_t outi;
00061 
00062     /* dummy check */
00063     if (a == NULL || b == NULL || out == NULL)
00064         return;
00065 
00066     /* do each element */
00067     for (i = 0; i < 3; i++) {
00068         /* round to integer */
00069         const double ai = rint(a[i]);
00070         const double bi = rint(a[i]);
00071 
00072         /* prefer exact integer */
00073         if (ai == a[i])
00074             out[i] = a[i];
00075         else if (bi == b[i])
00076             out[i] = b[i];
00077 
00078         /* use nearest */
00079         else if (fabs(ai - a[i]) < fabs(bi < b[i]))
00080             out[i] = a[i];
00081         else
00082             out[i] = b[i];
00083 
00084         /* snap */
00085         outi = rint(out[i]);
00086         if (fabs(outi - out[i]) <= SNAP_EPSILON)
00087             out[i] = outi;
00088     }
00089 }
00090 
00095 static qboolean FixWinding (winding_t *w)
00096 {
00097     qboolean valid;
00098     int i, k;
00099 
00100     /* dummy check */
00101     if (!w)
00102         return qfalse;
00103 
00104     valid = qtrue;
00105 
00106     /* check all verts */
00107     for (i = 0; i < w->numpoints; i++) {
00108         /* get second point index */
00109         const int j = (i + 1) % w->numpoints;
00110         vec3_t vec;
00111         float dist;
00112 
00113         /* don't remove points if winding is a triangle */
00114         if (w->numpoints == 3)
00115             return valid;
00116 
00117         /* degenerate edge? */
00118         VectorSubtract(w->p[i], w->p[j], vec);
00119         dist = VectorLength(vec);
00120         if (dist < ON_EPSILON) {
00121             valid = qfalse;
00122 
00123             /* create an average point (ydnar 2002-01-26: using nearest-integer weld preference) */
00124             SnapWeldVector(w->p[i], w->p[j], vec);
00125             VectorCopy(vec, w->p[i]);
00126 
00127             /* move the remaining verts */
00128             for (k = i + 2; k < w->numpoints; k++)
00129                 VectorCopy(w->p[k], w->p[k - 1]);
00130 
00131             w->numpoints--;
00132         }
00133     }
00134 
00135     /* one last check and return */
00136     if (w->numpoints < 3)
00137         valid = qfalse;
00138     return valid;
00139 }
00140 
00141 
00146 static void CreateBrushWindings (bspbrush_t *brush)
00147 {
00148     int i;
00149 
00150     for (i = 0; i < brush->numsides; i++) {
00151         side_t *side = &brush->sides[i];
00152         const plane_t *plane = &mapplanes[side->planenum];
00153         int j;
00154 
00155         /* evidence that winding_t represents a hessian normal plane */
00156         winding_t *w = BaseWindingForPlane(plane->normal, plane->dist);
00157 
00158         for (j = 0; j < brush->numsides && w; j++) {
00159             if (i == j)
00160                 continue;
00161             /* back side clipaway */
00162             if (brush->sides[j].planenum == (brush->sides[i].planenum ^ 1))
00163                 continue;
00164             if (brush->sides[j].bevel)
00165                 continue;
00166             plane = &mapplanes[brush->sides[j].planenum ^ 1];
00167             ChopWindingInPlace(&w, plane->normal, plane->dist, 0); /*CLIP_EPSILON); */
00168 
00169             /* fix broken windings that would generate trifans */
00170             if (!FixWinding(w))
00171                 Verb_Printf(VERB_EXTRA, "removed degenerated edge(s) from winding\n");
00172         }
00173 
00174         side->winding = w;
00175     }
00176 
00177     BoundBrush(brush);
00178 }
00179 
00183 static bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs)
00184 {
00185     bspbrush_t *b;
00186     int i;
00187     vec3_t normal;
00188     vec_t dist;
00189 
00190     b = AllocBrush(6);
00191     b->numsides = 6;
00192     for (i = 0; i < 3; i++) {
00193         VectorClear(normal);
00194         normal[i] = 1;
00195         dist = maxs[i];
00196         b->sides[i].planenum = FindOrCreateFloatPlane(normal, dist);
00197 
00198         normal[i] = -1;
00199         dist = -mins[i];
00200         b->sides[3 + i].planenum = FindOrCreateFloatPlane(normal, dist);
00201     }
00202 
00203     CreateBrushWindings(b);
00204 
00205     return b;
00206 }
00207 
00211 static vec_t BrushVolume (bspbrush_t *brush)
00212 {
00213     int i;
00214     winding_t *w;
00215     vec3_t corner;
00216     vec_t volume;
00217 
00218     if (!brush)
00219         return 0;
00220 
00221     /* grab the first valid point as the corner */
00222     w = NULL;
00223     for (i = 0; i < brush->numsides; i++) {
00224         w = brush->sides[i].winding;
00225         if (w)
00226             break;
00227     }
00228     if (!w)
00229         return 0;
00230     VectorCopy(w->p[0], corner);
00231 
00232     /* make tetrahedrons to all other faces */
00233     volume = 0;
00234     for (; i < brush->numsides; i++) {
00235         w = brush->sides[i].winding;
00236         if (w) {
00237             const plane_t *plane = &mapplanes[brush->sides[i].planenum];
00238             const vec_t d = -(DotProduct(corner, plane->normal) - plane->dist);
00239             const vec_t area = WindingArea(w);
00240             volume += d * area;
00241         }
00242     }
00243 
00244     volume /= 3;
00245     return volume;
00246 }
00247 
00251 int CountBrushList (bspbrush_t *brushes)
00252 {
00253     int c;
00254 
00255     c = 0;
00256     for (; brushes; brushes = brushes->next)
00257         c++;
00258     return c;
00259 }
00260 
00265 static tree_t *AllocTree (void)
00266 {
00267     tree_t *tree = Mem_Alloc(sizeof(*tree));
00268 
00269     ClearBounds(tree->mins, tree->maxs);
00270 
00271     return tree;
00272 }
00273 
00278 static node_t *AllocNode (void)
00279 {
00280     return Mem_Alloc(sizeof(node_t));
00281 }
00282 
00287 bspbrush_t *AllocBrush (int numsides)
00288 {
00289     if (threadstate.numthreads == 1)
00290         c_active_brushes++;
00291 
00292     return Mem_Alloc(offsetof(bspbrush_t, sides[numsides]));
00293 }
00294 
00298 void FreeBrush (bspbrush_t *brushes)
00299 {
00300     int i;
00301 
00302     for (i = 0; i < brushes->numsides; i++)
00303         if (brushes->sides[i].winding)
00304             FreeWinding(brushes->sides[i].winding);
00305     Mem_Free(brushes);
00306     if (threadstate.numthreads == 1)
00307         c_active_brushes--;
00308 }
00309 
00314 void FreeBrushList (bspbrush_t *brushes)
00315 {
00316     bspbrush_t *next;
00317 
00318     for (; brushes; brushes = next) {
00319         next = brushes->next;
00320         FreeBrush(brushes);
00321     }
00322 }
00323 
00328 bspbrush_t *CopyBrush (const bspbrush_t *brush)
00329 {
00330     bspbrush_t *newbrush;
00331     int i;
00332     size_t size = offsetof(bspbrush_t, sides[brush->numsides]);
00333 
00334     newbrush = AllocBrush(brush->numsides);
00335     memcpy(newbrush, brush, size);
00336 
00337     for (i = 0; i < brush->numsides; i++) {
00338         if (brush->sides[i].winding)
00339             newbrush->sides[i].winding = CopyWinding(brush->sides[i].winding);
00340     }
00341 
00342     return newbrush;
00343 }
00344 
00345 
00346 static int TestBrushToPlanenum (bspbrush_t *brush, int planenum,
00347             int *numsplits, qboolean *hintsplit, int *epsilonbrush)
00348 {
00349     int i, j, s;
00350     plane_t *plane;
00351     dBspPlane_t plane2;
00352     winding_t *w;
00353     vec_t d_front, d_back;
00354     int front, back;
00355 
00356     *numsplits = 0;
00357     *hintsplit = qfalse;
00358 
00359     /* if the brush actually uses the planenum,
00360      * we can tell the side for sure */
00361     for (i = 0; i < brush->numsides; i++) {
00362         const int num = brush->sides[i].planenum;
00363         if (num >= 0x10000)
00364             Sys_Error("bad planenum");
00365         if (num == planenum)
00366             return (PSIDE_BACK | PSIDE_FACING);
00367         if (num == (planenum ^ 1))
00368             return (PSIDE_FRONT | PSIDE_FACING);
00369     }
00370 
00371     /* box on plane side */
00372     plane = &mapplanes[planenum];
00373 
00374     /* Convert to cBspPlane */
00375     VectorCopy(plane->normal, plane2.normal);
00376     plane2.dist = plane->dist;
00377     plane2.type = plane->type;
00378     s = TR_BoxOnPlaneSide(brush->mins, brush->maxs, &plane2);
00379 
00380     if (s != PSIDE_BOTH)
00381         return s;
00382 
00383     /* if both sides, count the visible faces split */
00384     d_front = d_back = 0;
00385 
00386     for (i = 0; i < brush->numsides; i++) {
00387         if (brush->sides[i].texinfo == TEXINFO_NODE)
00388             continue;       /* on node, don't worry about splits */
00389         if (!brush->sides[i].visible)
00390             continue;       /* we don't care about non-visible */
00391         w = brush->sides[i].winding;
00392         if (!w)
00393             continue;
00394         front = back = 0;
00395         for (j = 0; j < w->numpoints; j++) {
00396             const vec_t d = DotProduct(w->p[j], plane->normal) - plane->dist;
00397             if (d > d_front)
00398                 d_front = d;
00399             if (d < d_back)
00400                 d_back = d;
00401 
00402             if (d > 0.1) /* PLANESIDE_EPSILON) */
00403                 front = 1;
00404             else if (d < -0.1) /* PLANESIDE_EPSILON) */
00405                 back = 1;
00406         }
00407         if (front && back) {
00408             (*numsplits)++;
00409             if (brush->sides[i].surfaceFlags & SURF_HINT)
00410                 *hintsplit = qtrue;
00411         }
00412     }
00413 
00414     if ((d_front > 0.0 && d_front < 1.0) || (d_back < 0.0 && d_back > -1.0))
00415         (*epsilonbrush)++;
00416 
00417     return s;
00418 }
00419 
00420 
00421 #define EDGE_LENGTH 0.2
00422 
00426 qboolean WindingIsTiny (winding_t *w)
00427 {
00428     int i, edges;
00429     vec_t len;
00430     vec3_t delta;
00431 
00432     edges = 0;
00433     for (i = 0; i < w->numpoints; i++) {
00434         const int j = ((i == w->numpoints - 1) ? 0 : i) + 1;
00435         VectorSubtract(w->p[j], w->p[i], delta);
00436         len = VectorLength(delta);
00437         if (len > EDGE_LENGTH) {
00438             if (++edges == 3)
00439                 return qfalse;
00440         }
00441     }
00442     return qtrue;
00443 }
00444 
00449 static qboolean WindingIsHuge (const winding_t *w)
00450 {
00451     int i, j;
00452 
00453     for (i = 0; i < w->numpoints; i++) {
00454         for (j = 0; j < 3; j++)
00455             if (w->p[i][j] < -MAX_WORLD_WIDTH || w->p[i][j] > MAX_WORLD_WIDTH)
00456                 return qtrue;
00457     }
00458     return qfalse;
00459 }
00460 
00461 static void LeafNode (node_t *node, bspbrush_t *brushes)
00462 {
00463     bspbrush_t *b;
00464     int i;
00465 
00466     node->planenum = PLANENUM_LEAF;
00467     node->contentFlags = 0;
00468 
00469     Verb_Printf(VERB_DUMP, "LeafNode: scanning brushes.\n");
00470 
00471     for (b = brushes; b; b = b->next) {
00472         Verb_Printf(VERB_DUMP, "LeafNode: scanning brush %i\n", b->original->brushnum);
00473         /* if the brush is solid and all of its sides are on nodes,
00474          * it eats everything */
00475         if (b->original->contentFlags & CONTENTS_SOLID && !(b->original->contentFlags & CONTENTS_PASSABLE)) {
00476             for (i = 0; i < b->numsides; i++)
00477                 if (b->sides[i].texinfo != TEXINFO_NODE)
00478                     break;
00479             if (i == b->numsides) {
00480                 node->contentFlags = CONTENTS_SOLID;
00481                 break;
00482             }
00483         }
00484         node->contentFlags |= b->original->contentFlags;
00485     }
00486 
00487     node->brushlist = brushes;
00488 }
00489 
00490 
00491 static void CheckPlaneAgainstParents (int pnum, const node_t *node)
00492 {
00493     node_t *p;
00494 
00495     for (p = node->parent; p; p = p->parent) {
00496         if (p->planenum == pnum)
00497             Sys_Error("Tried parent");
00498     }
00499 }
00500 
00501 static qboolean CheckPlaneAgainstVolume (int pnum, node_t *node)
00502 {
00503     bspbrush_t *front, *back;
00504     qboolean good;
00505 
00506     SplitBrush(node->volume, pnum, &front, &back);
00507 
00508     good = (front && back);
00509 
00510     if (front)
00511         FreeBrush(front);
00512     if (back)
00513         FreeBrush(back);
00514 
00515     return good;
00516 }
00517 
00523 static side_t *SelectSplitSide (bspbrush_t *brushes, node_t *node)
00524 {
00525     int value, bestvalue;
00526     bspbrush_t *brush, *test;
00527     side_t *bestside;
00528     int i, j, pass, numpasses, pnum;
00529     int front, back, both, facing, splits;
00530     int bsplits, epsilonbrush;
00531     qboolean hintsplit;
00532 
00533     bestside = NULL;
00534     bestvalue = -99999;
00535 
00542     numpasses = 4;
00543     for (pass = 0; pass < numpasses; pass++) {
00544         for (brush = brushes; brush; brush = brush->next) {
00545             if ((pass & 1) && !(brush->original->contentFlags & CONTENTS_DETAIL))
00546                 continue;
00547             if (!(pass & 1) && (brush->original->contentFlags & CONTENTS_DETAIL))
00548                 continue;
00549             for (i = 0; i < brush->numsides; i++) {
00552                 side_t *side = brush->sides + i;
00553                 if (side->bevel)
00554                     continue;   /* never use a bevel as a spliter */
00555                 if (!side->winding)
00556                     continue;   /* nothing visible, so it can't split */
00557                 if (side->texinfo == TEXINFO_NODE)
00558                     continue;   /* already a node splitter */
00559                 if (side->tested)
00560                     continue;   /* we already have metrics for this plane */
00561                 if (side->surfaceFlags & SURF_SKIP)
00562                     continue;   /* skip surfaces are never chosen */
00563                 if (side->visible ^ (pass < 2))
00564                     continue;   /* only check visible faces on first pass */
00565 
00566                 pnum = side->planenum;
00567                 pnum &= ~1; /* always use positive facing plane */
00568 
00569                 CheckPlaneAgainstParents(pnum, node);
00570 
00571                 if (!CheckPlaneAgainstVolume(pnum, node))
00572                     continue;   /* would produce a tiny volume */
00573 
00574                 front = 0;
00575                 back = 0;
00576                 both = 0;
00577                 facing = 0;
00578                 splits = 0;
00579                 epsilonbrush = 0;
00580                 hintsplit = qfalse;
00581 
00582                 for (test = brushes; test; test = test->next) {
00583                     const int s = TestBrushToPlanenum(test, pnum, &bsplits, &hintsplit, &epsilonbrush);
00584 
00585                     splits += bsplits;
00586                     if (bsplits && (s & PSIDE_FACING))
00587                         Sys_Error("PSIDE_FACING with splits");
00588 
00589                     test->testside = s;
00590                     /* if the brush shares this face, don't bother
00591                      * testing that facenum as a splitter again */
00592                     if (s & PSIDE_FACING) {
00593                         facing++;
00594                         for (j = 0; j < test->numsides; j++) {
00595                             if ((test->sides[j].planenum &~ 1) == pnum)
00596                                 test->sides[j].tested = qtrue;
00597                         }
00598                     }
00599                     if (s & PSIDE_FRONT)
00600                         front++;
00601                     if (s & PSIDE_BACK)
00602                         back++;
00603                     if (s == PSIDE_BOTH)
00604                         both++;
00605                 }
00606 
00607                 /* give a value estimate for using this plane */
00608 
00609                 value = 5 * facing - 5 * splits - abs(front - back);
00610                 if (AXIAL(&mapplanes[pnum]))
00611                     value += 5;     /* axial is better */
00612                 value -= epsilonbrush * 1000;   /* avoid! */
00613 
00614                 /* never split a hint side except with another hint */
00615                 if (hintsplit && !(side->surfaceFlags & SURF_HINT))
00616                     value = -9999999;
00617 
00618                 /* save off the side test so we don't need
00619                  * to recalculate it when we actually seperate
00620                  * the brushes */
00621                 if (value > bestvalue) {
00622                     bestvalue = value;
00623                     bestside = side;
00624                     for (test = brushes; test; test = test->next)
00625                         test->side = test->testside;
00626                 }
00627             }
00628         }
00629 
00630         /* if we found a good plane, don't bother trying any other passes */
00631         if (bestside) {
00632             if (pass > 1) {
00633                 if (threadstate.numthreads == 1)
00634                     c_nonvis++;
00635             }
00636             break;
00637         }
00638     }
00639 
00640     /* clear all the tested flags we set */
00641     for (brush = brushes; brush; brush = brush->next) {
00642         for (i = 0; i < brush->numsides; i++)
00643             brush->sides[i].tested = qfalse;
00644     }
00645 
00646     return bestside;
00647 }
00648 
00653 static int BrushMostlyOnSide (const bspbrush_t *brush, const plane_t *plane)
00654 {
00655     int i, j, side;
00656     vec_t max;
00657 
00658     max = 0;
00659     side = PSIDE_FRONT;
00660     for (i = 0; i < brush->numsides; i++) {
00661         const winding_t *w = brush->sides[i].winding;
00662         if (!w)
00663             continue;
00664         for (j = 0; j < w->numpoints; j++) {
00665             const vec_t d = DotProduct(w->p[j], plane->normal) - plane->dist;
00666             if (d > max) {
00667                 max = d;
00668                 side = PSIDE_FRONT;
00669             }
00670             if (-d > max) {
00671                 max = -d;
00672                 side = PSIDE_BACK;
00673             }
00674         }
00675     }
00676     return side;
00677 }
00678 
00682 void SplitBrush (const bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back)
00683 {
00684     bspbrush_t *b[2];
00685     int i, j;
00686     winding_t *w, *cw[2], *midwinding;
00687     plane_t *plane, *plane2;
00688     side_t *cs;
00689     float d_front, d_back;
00690 
00691     *front = *back = NULL;
00692     plane = &mapplanes[planenum];
00693 
00694     /* check all points */
00695     d_front = d_back = 0;
00696     for (i = 0; i < brush->numsides; i++) {
00697         w = brush->sides[i].winding;
00698         if (!w)
00699             continue;
00700         for (j = 0; j < w->numpoints; j++) {
00701             const float d = DotProduct(w->p[j], plane->normal) - plane->dist;
00702             if (d > 0 && d > d_front)
00703                 d_front = d;
00704             if (d < 0 && d < d_back)
00705                 d_back = d;
00706         }
00707     }
00708     if (d_front < 0.1) { /* PLANESIDE_EPSILON) */
00709         /* only on back */
00710         *back = CopyBrush(brush);
00711         return;
00712     }
00713     if (d_back > -0.1) { /* PLANESIDE_EPSILON) */
00714         /* only on front */
00715         *front = CopyBrush(brush);
00716         return;
00717     }
00718 
00719     /* create a new winding from the split plane */
00720     w = BaseWindingForPlane(plane->normal, plane->dist);
00721     for (i = 0; i < brush->numsides && w; i++) {
00722         plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
00723         ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); /* PLANESIDE_EPSILON); */
00724     }
00725 
00726     /* the brush isn't really split */
00727     if (!w || WindingIsTiny(w)) {
00728         const int side = BrushMostlyOnSide(brush, plane);
00729         if (side == PSIDE_FRONT)
00730             *front = CopyBrush(brush);
00731         else if (side == PSIDE_BACK)
00732             *back = CopyBrush(brush);
00733         return;
00734     }
00735 
00736     if (WindingIsHuge(w)) {
00739         Com_Printf("WARNING: Large winding\n");
00740     }
00741 
00742     midwinding = w;
00743 
00744     /* split it for real */
00745     for (i = 0; i < 2; i++) {
00746         b[i] = AllocBrush(brush->numsides + 1);
00747         b[i]->original = brush->original;
00748     }
00749 
00750     /* split all the current windings */
00751     for (i = 0; i < brush->numsides; i++) {
00752         const side_t *s = &brush->sides[i];
00753         w = s->winding;
00754         if (!w)
00755             continue;
00756         ClipWindingEpsilon(w, plane->normal, plane->dist,
00757             0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
00758         for (j = 0; j < 2; j++) {
00759             if (!cw[j])
00760                 continue;
00761 
00762             cs = &b[j]->sides[b[j]->numsides];
00763             b[j]->numsides++;
00764             *cs = *s;
00765 
00766             cs->winding = cw[j];
00767             cs->tested = qfalse;
00768         }
00769     }
00770 
00771     /* see if we have valid polygons on both sides */
00772     for (i = 0; i < 2; i++) {
00773         BoundBrush(b[i]);
00774         for (j = 0; j < 3; j++) {
00775             if (b[i]->mins[j] < -MAX_WORLD_WIDTH || b[i]->maxs[j] > MAX_WORLD_WIDTH) {
00778                 Verb_Printf(VERB_EXTRA, "bogus brush after clip\n");
00779                 break;
00780             }
00781         }
00782 
00783         if (b[i]->numsides < 3 || j < 3) {
00784             FreeBrush(b[i]);
00785             b[i] = NULL;
00786         }
00787     }
00788 
00789     if (!(b[0] && b[1])) {
00792         if (!b[0] && !b[1])
00793             Verb_Printf(VERB_EXTRA, "split removed brush\n");
00794         else
00795             Verb_Printf(VERB_EXTRA, "split not on both sides\n");
00796         if (b[0]) {
00797             FreeBrush(b[0]);
00798             *front = CopyBrush(brush);
00799         }
00800         if (b[1]) {
00801             FreeBrush(b[1]);
00802             *back = CopyBrush(brush);
00803         }
00804         return;
00805     }
00806 
00807     /* add the midwinding to both sides */
00808     for (i = 0; i < 2; i++) {
00809         cs = &b[i]->sides[b[i]->numsides];
00810         b[i]->numsides++;
00811 
00812         cs->planenum = planenum ^ i ^ 1;
00813         cs->texinfo = TEXINFO_NODE;
00814         cs->visible = qfalse;
00815         cs->tested = qfalse;
00816         if (i == 0)
00817             cs->winding = CopyWinding(midwinding);
00818         else
00819             cs->winding = midwinding;
00820     }
00821 
00822     for (i = 0; i < 2; i++) {
00823         const vec_t v1 = BrushVolume(b[i]);
00824         if (v1 < 1.0) {
00825             FreeBrush(b[i]);
00826             b[i] = NULL;
00829             Verb_Printf(VERB_EXTRA, "tiny volume after clip\n");
00830         }
00831     }
00832 
00833     *front = b[0];
00834     *back = b[1];
00835 }
00836 
00837 static void SplitBrushList (bspbrush_t *brushes, node_t *node, bspbrush_t **front, bspbrush_t **back)
00838 {
00839     bspbrush_t *brush, *newbrush, *newbrush2;
00840     side_t *side;
00841     int sides, i;
00842 
00843     *front = *back = NULL;
00844 
00845     for (brush = brushes; brush; brush = brush->next) {
00846         sides = brush->side;
00847 
00848         if (sides == PSIDE_BOTH) {  /* split into two brushes */
00849             SplitBrush(brush, node->planenum, &newbrush, &newbrush2);
00850             if (newbrush) {
00851                 newbrush->next = *front;
00852                 Verb_Printf(VERB_DUMP, "SplitBrushList: Adding brush %i to front list.\n", newbrush->original->brushnum);
00853                 *front = newbrush;
00854             }
00855             if (newbrush2) {
00856                 newbrush2->next = *back;
00857                 Verb_Printf(VERB_DUMP, "SplitBrushList: Adding brush %i to back list.\n", newbrush2->original->brushnum);
00858                 *back = newbrush2;
00859             }
00860             continue;
00861         }
00862 
00863         newbrush = CopyBrush(brush);
00864 
00865         /* if the planenum is actualy a part of the brush
00866          * find the plane and flag it as used so it won't be tried
00867          * as a splitter again */
00868         if (sides & PSIDE_FACING) {
00869             for (i = 0; i < newbrush->numsides; i++) {
00870                 side = newbrush->sides + i;
00871                 if ((side->planenum & ~1) == node->planenum)
00872                     side->texinfo = TEXINFO_NODE;
00873             }
00874         }
00875 
00876         if (sides & PSIDE_FRONT) {
00877             newbrush->next = *front;
00878             *front = newbrush;
00879             Verb_Printf(VERB_DUMP, "SplitBrushList: Adding brush %i to front list.\n", newbrush->original->brushnum);
00880             continue;
00881         }
00882         if (sides & PSIDE_BACK) {
00883             newbrush->next = *back;
00884             Verb_Printf(VERB_DUMP, "SplitBrushList: Adding brush %i to back list.\n", newbrush->original->brushnum);
00885             *back = newbrush;
00886             continue;
00887         }
00888         Verb_Printf(VERB_DUMP, "SplitBrushList: Brush %i fell off the map.\n", newbrush->original->brushnum);
00889     }
00890 }
00891 
00892 
00893 static node_t *BuildTree_r (node_t *node, bspbrush_t *brushes)
00894 {
00895     node_t *newnode;
00896     side_t *bestside;
00897     int i;
00898     bspbrush_t *children[2];
00899 
00900     if (threadstate.numthreads == 1)
00901         c_nodes++;
00902 
00903     /* find the best plane to use as a splitter */
00904     bestside = SelectSplitSide(brushes, node);
00905     if (!bestside) {
00906         /* leaf node */
00907         node->side = NULL;
00908         node->planenum = PLANENUM_LEAF;
00909         LeafNode(node, brushes);
00910         Verb_Printf(VERB_DUMP, "BuildTree_r: Created a leaf node.\n");
00911         return node;
00912     }
00913 
00914     Verb_Printf(VERB_DUMP, "BuildTree_r: splitting along plane %i\n", bestside->planenum);
00915 
00916     /* this is a splitplane node */
00917     node->side = bestside;
00918     assert(bestside->planenum < MAX_MAP_PLANES);
00919     node->planenum = bestside->planenum & ~1;   /* always use front facing */
00920 
00921     SplitBrushList(brushes, node, &children[0], &children[1]);
00922     FreeBrushList(brushes);
00923 
00924     /* allocate children before recursing */
00925     for (i = 0; i < 2; i++) {
00926         newnode = AllocNode();
00927         newnode->parent = node;
00928         node->children[i] = newnode;
00929     }
00930 
00931     SplitBrush(node->volume, node->planenum, &node->children[0]->volume,
00932         &node->children[1]->volume);
00933 
00934     /* recursively process children */
00935     for (i = 0; i < 2; i++) {
00936         node->children[i] = BuildTree_r(node->children[i], children[i]);
00937     }
00938 
00939     return node;
00940 }
00941 
00945 tree_t *BrushBSP (bspbrush_t *brushlist, vec3_t mins, vec3_t maxs)
00946 {
00947     node_t *node;
00948     bspbrush_t *b;
00949     int c_faces, c_nonvisfaces, c_brushes;
00950     tree_t *tree;
00951     int i;
00952     vec_t volume;
00953 
00954     Verb_Printf(VERB_EXTRA, "--- BrushBSP ---\n");
00955 
00956     tree = AllocTree();
00957 
00958     c_faces = 0;
00959     c_nonvisfaces = 0;
00960     c_brushes = 0;
00961     for (b = brushlist; b; b = b->next) {
00962         c_brushes++;
00963 
00964         volume = BrushVolume(b);
00965         if (volume < config.microvolume) {
00966             Com_Printf("\nWARNING: entity %i, brush %i: microbrush, volume %.3g\n",
00967                 b->original->entitynum, b->original->brushnum, volume);
00968         }
00969 
00970         for (i = 0; i < b->numsides; i++) {
00971             if (b->sides[i].bevel)
00972                 continue;
00973             if (!b->sides[i].winding)
00974                 continue;
00975             if (b->sides[i].texinfo == TEXINFO_NODE)
00976                 continue;
00977             if (b->sides[i].visible)
00978                 c_faces++;
00979             else
00980                 c_nonvisfaces++;
00981         }
00982 
00983         AddPointToBounds(b->mins, tree->mins, tree->maxs);
00984         AddPointToBounds(b->maxs, tree->mins, tree->maxs);
00985     }
00986 
00987     Verb_Printf(VERB_EXTRA, "%5i brushes\n", c_brushes);
00988     Verb_Printf(VERB_EXTRA, "%5i visible faces\n", c_faces);
00989     Verb_Printf(VERB_EXTRA, "%5i nonvisible faces\n", c_nonvisfaces);
00990 
00991     c_nodes = 0;
00992     c_nonvis = 0;
00993     node = AllocNode();
00994 
00995     node->volume = BrushFromBounds(mins, maxs);
00996 
00997     tree->headnode = node;
00998 
00999     node = BuildTree_r(node, brushlist);
01000     Verb_Printf(VERB_EXTRA, "%5i visible nodes\n", c_nodes / 2 - c_nonvis);
01001     Verb_Printf(VERB_EXTRA, "%5i nonvis nodes\n", c_nonvis);
01002     Verb_Printf(VERB_EXTRA, "%5i leafs\n", (c_nodes + 1) / 2);
01003     return tree;
01004 }
01005 
01006 
01010 void WriteBSPBrushMap (const char *name, const bspbrush_t *list)
01011 {
01012     qFILE f;
01013 
01014     /* note it */
01015     Verb_Printf(VERB_LESS, "Writing %s\n", name);
01016 
01017     /* open the map file */
01018     FS_OpenFile(name, &f, FILE_WRITE);
01019     if (f.f == NULL)
01020         Sys_Error("Can't write %s\b", name);
01021 
01022     FS_Printf(&f, "{\n\"classname\" \"worldspawn\"\n");
01023 
01024     for (; list; list = list->next) {
01025         const side_t *s;
01026         int i;
01027 
01028         FS_Printf(&f, "{\n");
01029         for (i = 0, s = list->sides; i < list->numsides; i++, s++) {
01030             winding_t *w = BaseWindingForPlane(mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
01031             const dBspTexinfo_t *t = &curTile->texinfo[s->texinfo];
01032 
01033             FS_Printf(&f, "( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
01034             FS_Printf(&f, "( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
01035             FS_Printf(&f, "( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
01036 
01037             FS_Printf(&f, "%s 0 0 0 1 1 0 %i %i\n", t->texture, t->surfaceFlags, t->value);
01038             FreeWinding(w);
01039         }
01040         FS_Printf(&f, "}\n");
01041     }
01042     FS_Printf(&f, "}\n");
01043 
01044     FS_CloseFile(&f);
01045 }

Generated by  doxygen 1.6.2