csg.c

Go to the documentation of this file.
00001 
00028 /*
00029 Copyright (C) 1997-2001 Id Software, Inc.
00030 
00031 This program is free software; you can redistribute it and/or
00032 modify it under the terms of the GNU General Public License
00033 as published by the Free Software Foundation; either version 2
00034 of the License, or (at your option) any later version.
00035 
00036 This program is distributed in the hope that it will be useful,
00037 but WITHOUT ANY WARRANTY; without even the implied warranty of
00038 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00039 
00040 See the GNU General Public License for more details.
00041 
00042 You should have received a copy of the GNU General Public License
00043 along with this program; if not, write to the Free Software
00044 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00045 
00046 */
00047 
00048 #include "bsp.h"
00049 
00055 static bspbrush_t *SubtractBrush (bspbrush_t *a, const bspbrush_t *b)
00056 {
00057     /* a - b = out (list) */
00058     int i;
00059     bspbrush_t *front, *back, *out;
00060     bspbrush_t *in;
00061 
00062     in = a;
00063     out = NULL;
00064     for (i = 0; i < b->numsides && in; i++) {
00065         SplitBrush(in, b->sides[i].planenum, &front, &back);
00066         if (in != a)
00067             FreeBrush(in);
00068         if (front) {    /* add to list */
00069             front->next = out;
00070             out = front;
00071         }
00072         in = back;
00073     }
00074     if (in)
00075         FreeBrush(in);
00076     else {  /* didn't really intersect */
00077         FreeBrushList(out);
00078         return a;
00079     }
00080     return out;
00081 }
00082 
00087 static qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b)
00088 {
00089     int i, j;
00090 
00091     /* check bounding boxes */
00092     for (i = 0; i < 3; i++)
00093         if (a->mins[i] >= b->maxs[i] || a->maxs[i] <= b->mins[i])
00094             return qtrue;   /* bounding boxes don't overlap */
00095 
00096     /* check for opposing planes */
00097     for (i = 0; i < a->numsides; i++) {
00098         for (j = 0; j < b->numsides; j++) {
00099             if (a->sides[i].planenum == (b->sides[j].planenum ^ 1))
00100                 return qtrue;   /* opposite planes, so not touching */
00101         }
00102     }
00103 
00104     return qfalse;  /* might intersect */
00105 }
00106 
00107 static int minplanenums[2];
00108 static int maxplanenums[2];
00109 
00113 static bspbrush_t *ClipBrushToBox (bspbrush_t *brush, vec3_t clipmins, vec3_t clipmaxs)
00114 {
00115     int i, j, p;
00116     bspbrush_t *front, *back;
00117 
00118     for (j = 0; j < 2; j++) {
00119         if (brush->maxs[j] > clipmaxs[j]) {
00120             SplitBrush(brush, maxplanenums[j], &front, &back);
00121             FreeBrush(brush);
00122             if (front)
00123                 FreeBrush(front);
00124             brush = back;
00125             if (!brush)
00126                 return NULL;
00127         }
00128         if (brush->mins[j] < clipmins[j]) {
00129             SplitBrush(brush, minplanenums[j], &front, &back);
00130             FreeBrush(brush);
00131             if (back)
00132                 FreeBrush(back);
00133             brush = front;
00134             if (!brush)
00135                 return NULL;
00136         }
00137     }
00138 
00139     /* remove any colinear faces */
00140     for (i = 0; i < brush->numsides; i++) {
00141         p = brush->sides[i].planenum & ~1;
00142         if (p == maxplanenums[0] || p == maxplanenums[1]
00143             || p == minplanenums[0] || p == minplanenums[1]) {
00144             brush->sides[i].texinfo = TEXINFO_NODE;
00145             brush->sides[i].visible = qfalse;
00146         }
00147     }
00148     return brush;
00149 }
00150 
00158 static qboolean IsInLevel (const int contents, const int level)
00159 {
00160     /* special levels */
00161     switch (level) {
00162     case LEVEL_LIGHTCLIP:
00163         if (contents & CONTENTS_LIGHTCLIP)
00164             return qtrue;
00165         else
00166             return qfalse;
00167     case LEVEL_WEAPONCLIP:
00168         if (contents & CONTENTS_WEAPONCLIP)
00169             return qtrue;
00170         else
00171             return qfalse;
00172     case LEVEL_ACTORCLIP:
00173         if (contents & CONTENTS_ACTORCLIP)
00174             return qtrue;
00175         else
00176             return qfalse;
00177     }
00178 
00179     /* If the brush is any kind of clip, we are not looking for it after here. */
00180     if (contents & MASK_CLIP)
00181         return qfalse;
00182 
00183     /* standard levels */
00184     if (level == -1)
00185         return qtrue;
00186     else if (level) {
00187         if (((contents >> 8) & 0xFF) == level)
00188             return qtrue;
00189         else
00190             return qfalse;
00191     } else {
00192         if (contents & 0xFF00)
00193             return qfalse;
00194         else
00195             return qtrue;
00196     }
00197 }
00198 
00199 static bspbrush_t *AddBrushListToTail (bspbrush_t *list, bspbrush_t *tail)
00200 {
00201     bspbrush_t *walk, *next;
00202 
00203     /* add to end of list */
00204     for (walk = list; walk; walk = next) {
00205         next = walk->next;
00206         walk->next = NULL;
00207         tail->next = walk;
00208         tail = walk;
00209     }
00210 
00211     return tail;
00212 }
00213 
00220 static bspbrush_t *CullList (bspbrush_t *list, bspbrush_t *skip)
00221 {
00222     bspbrush_t *newlist;
00223     bspbrush_t *next;
00224 
00225     newlist = NULL;
00226 
00227     for (; list; list = next) {
00228         next = list->next;
00229         if (list == skip) {
00230             FreeBrush(list);
00231             continue;
00232         }
00233         list->next = newlist;
00234         newlist = list;
00235     }
00236     return newlist;
00237 }
00238 
00242 static inline qboolean BrushGE (bspbrush_t *b1, bspbrush_t *b2)
00243 {
00244     /* detail brushes never bite structural brushes */
00245     if ((b1->original->contentFlags & CONTENTS_DETAIL)
00246         && !(b2->original->contentFlags & CONTENTS_DETAIL))
00247         return qfalse;
00248     if (b1->original->contentFlags & CONTENTS_SOLID)
00249         return qtrue;
00250     return qfalse;
00251 }
00252 
00266 int MapBrushesBounds (const int startbrush, const int endbrush, const int level, const vec3_t clipmins, const vec3_t clipmaxs, vec3_t mins, vec3_t maxs)
00267 {
00268     int i, j, num;
00269 
00270     ClearBounds(mins, maxs);
00271     num = 0;
00272 
00273     for (i = startbrush; i < endbrush; i++) {
00274         const mapbrush_t *b = &mapbrushes[i];
00275 
00276         /* don't use finished brushes again */
00277         if (b->finished)
00278             continue;
00279 
00280         if (!IsInLevel(b->contentFlags, level))
00281             continue;
00282 
00283         /* check the bounds */
00284         for (j = 0; j < 3; j++)
00285             if (b->mins[j] < clipmins[j]
00286              || b->maxs[j] > clipmaxs[j])
00287             break;
00288         if (j != 3)
00289             continue;
00290 
00291         num++;
00292         AddPointToBounds(b->mins, mins, maxs);
00293         AddPointToBounds(b->maxs, mins, maxs);
00294     }
00295 
00296     return num;
00297 }
00298 
00299 bspbrush_t *MakeBspBrushList (int startbrush, int endbrush, int level, vec3_t clipmins, vec3_t clipmaxs)
00300 {
00301     bspbrush_t *brushlist, *newbrush;
00302     int i, j, vis;
00303     int c_faces, c_brushes, numsides;
00304 
00305     Verb_Printf(VERB_DUMP, "MakeBspBrushList: bounds (%f %f %f) (%f %f %f)\n",
00306         clipmins[0], clipmins[1], clipmins[2], clipmaxs[0], clipmaxs[1], clipmaxs[2]);
00307 
00308     for (i = 0; i < 2; i++) {
00309         float dist;
00310         vec3_t normal = {0, 0, 0};
00311         normal[i] = 1;
00312         dist = clipmaxs[i];
00313         maxplanenums[i] = FindOrCreateFloatPlane(normal, dist);
00314         dist = clipmins[i];
00315         minplanenums[i] = FindOrCreateFloatPlane(normal, dist);
00316     }
00317 
00318     brushlist = NULL;
00319     c_faces = 0;
00320     c_brushes = 0;
00321 
00322     for (i = startbrush; i < endbrush; i++) {
00323         mapbrush_t *mb = &mapbrushes[i];
00324 
00325         if (!IsInLevel(mb->contentFlags, level)){
00326             Verb_Printf(VERB_DUMP, "Rejected brush %i: wrong level.\n", i);
00327             continue;
00328         }
00329 
00330         if (mb->finished){
00331             Verb_Printf(VERB_DUMP, "Rejected brush %i: already finished.\n", i);
00332             continue;
00333         }
00334 
00335         numsides = mb->numsides;
00336         if (!numsides) {
00337             Verb_Printf(VERB_DUMP, "Rejected brush %i: no sides.\n", i);
00338             continue;
00339         }
00340 
00341         /* make sure the brush has at least one face showing */
00342         vis = 0;
00343         for (j = 0; j < numsides; j++)
00344             if (mb->original_sides[j].visible && mb->original_sides[j].winding)
00345                 vis++;
00346 
00347         /* if the brush is outside the clip area, skip it */
00348         for (j = 0; j < 3; j++)
00349             if (mb->mins[j] < clipmins[j] || mb->maxs[j] > clipmaxs[j])
00350                 break;
00351         if (j != 3)
00352             continue;
00353 
00354         mb->finished = qtrue;
00355 
00356         /* make a copy of the brush */
00357         newbrush = AllocBrush(mb->numsides);
00358         newbrush->original = mb;
00359         newbrush->numsides = mb->numsides;
00360         memcpy(newbrush->sides, mb->original_sides, numsides * sizeof(side_t));
00361         for (j = 0; j < numsides; j++) {
00362             if (newbrush->sides[j].winding)
00363                 newbrush->sides[j].winding = CopyWinding(newbrush->sides[j].winding);
00364             if (newbrush->sides[j].surfaceFlags & SURF_HINT)
00365                 newbrush->sides[j].visible = qtrue; /* hints are always visible */
00366         }
00367         VectorCopy(mb->mins, newbrush->mins);
00368         VectorCopy(mb->maxs, newbrush->maxs);
00369 
00370         /* carve off anything outside the clip box */
00371         newbrush = ClipBrushToBox(newbrush, clipmins, clipmaxs);
00372         if (!newbrush) {
00373             Verb_Printf(VERB_DUMP, "Rejected brush %i: cannot clip to box.\n", i);
00374             continue;
00375         }
00376 
00377         c_faces += vis;
00378         c_brushes++;
00379 
00380         newbrush->next = brushlist;
00381         brushlist = newbrush;
00382         Verb_Printf(VERB_DUMP, "Added brush %i.\n", i);
00383     }
00384 
00385     return brushlist;
00386 }
00387 
00391 bspbrush_t *ChopBrushes (bspbrush_t *head)
00392 {
00393     bspbrush_t *b1, *b2, *next;
00394     bspbrush_t *tail;
00395     bspbrush_t *keep;
00396     bspbrush_t *sub, *sub2;
00397     int c1, c2;
00398     const int originalBrushes = CountBrushList(head);
00399     int keepBrushes;
00400 
00401     Verb_Printf(VERB_EXTRA, "---- ChopBrushes ----\n");
00402 
00403     keep = NULL;
00404 
00405 newlist:
00406     /* find tail */
00407     if (!head)
00408         return NULL;
00409 
00410     for (tail = head; tail->next; tail = tail->next);
00411 
00412     for (b1 = head; b1; b1 = next) {
00413         next = b1->next;
00414         for (b2 = b1->next; b2; b2 = b2->next) {
00415             if (BrushesDisjoint(b1, b2))
00416                 continue;
00417 
00418             sub = sub2 = NULL;
00419             c1 = c2 = 999999;
00420 
00421             if (BrushGE(b2, b1)) {
00422                 sub = SubtractBrush(b1, b2);
00423                 if (sub == b1)
00424                     continue;       /* didn't really intersect */
00425                 if (!sub) { /* b1 is swallowed by b2 */
00426                     head = CullList(b1, b1);
00427                     goto newlist;
00428                 }
00429                 c1 = CountBrushList(sub);
00430             }
00431 
00432             if (BrushGE(b1, b2)) {
00433                 sub2 = SubtractBrush(b2, b1);
00434                 if (sub2 == b2)
00435                     continue;       /* didn't really intersect */
00436                 if (!sub2) {    /* b2 is swallowed by b1 */
00437                     FreeBrushList(sub);
00438                     head = CullList(b1, b2);
00439                     goto newlist;
00440                 }
00441                 c2 = CountBrushList(sub2);
00442             }
00443 
00444             if (!sub && !sub2)
00445                 continue;       /* neither one can bite */
00446 
00447             /* only accept if it didn't fragment */
00448             /* (commenting this out allows full fragmentation) */
00449             if (c1 > 1 && c2 > 1) {
00450                 if (sub2)
00451                     FreeBrushList(sub2);
00452                 if (sub)
00453                     FreeBrushList(sub);
00454                 continue;
00455             }
00456 
00457             if (c1 < c2) {
00458                 if (sub2)
00459                     FreeBrushList(sub2);
00460                 tail = AddBrushListToTail(sub, tail);
00461                 head = CullList(b1, b1);
00462                 goto newlist;
00463             } else {
00464                 if (sub)
00465                     FreeBrushList(sub);
00466                 tail = AddBrushListToTail(sub2, tail);
00467                 head = CullList(b1, b2);
00468                 goto newlist;
00469             }
00470         }
00471 
00472         /* b1 is no longer intersecting anything, so keep it */
00473         if (!b2) {
00474             b1->next = keep;
00475             keep = b1;
00476         }
00477     }
00478 
00479     keepBrushes = CountBrushList(keep);
00480     if (keepBrushes != originalBrushes) {
00481         Verb_Printf(VERB_EXTRA, "original brushes: %i\n", originalBrushes);
00482         Verb_Printf(VERB_EXTRA, "output brushes: %i\n", keepBrushes);
00483     }
00484     return keep;
00485 }

Generated by  doxygen 1.6.2