check.c

Go to the documentation of this file.
00001 
00006 /*
00007 All original material Copyright (C) 2002-2010 UFO: Alien Invasion.
00008 
00009 Copyright (C) 1997-2001 Id Software, Inc.
00010 
00011 This program is free software; you can redistribute it and/or
00012 modify it under the terms of the GNU General Public License
00013 as published by the Free Software Foundation; either version 2
00014 of the License, or (at your option) any later version.
00015 
00016 This program is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00019 
00020 See the GNU General Public License for more details.
00021 
00022 You should have received a copy of the GNU General Public License
00023 along with this program; if not, write to the Free Software
00024 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00025 
00026 */
00027 
00028 #include "../common/shared.h"
00029 #include "../common/bspfile.h"
00030 #include "../common/scriplib.h"
00031 #include "../../../shared/entitiesdef.h"
00032 #include "../map.h"
00033 #include "check.h"
00034 #include "checklib.h"
00035 #include "../bsp.h"
00036 #include "../ufo2map.h"
00037 
00040 #define CH_DIST_EPSILON 0.001f
00041 #define CH_DIST_EPSILON_SQR 0.000001
00042 
00043 #define CH_DIST_EPSILON_COLLINEAR_POINTS 0.02f 
00046 #define COS_EPSILON 0.9999f
00047 
00049 #define SIN_EPSILON 0.0001f
00050 
00056 typedef enum {
00057     PIB_EXCL_SURF,              
00058     PIB_INCL_SURF_EXCL_EDGE,    
00059     PIB_INCL_SURF,              
00060     PIB_ON_SURFACE_ONLY         
00061 } pointInBrush_t;
00062 
00065 #define NEARDOWN_COS 0.985
00066 
00071 static qboolean Check_SidePointsDown (const side_t *s)
00072 {
00073     const vec3_t down = {0.0f, 0.0f, -1.0f};
00074     const plane_t *plane = &mapplanes[s->planenum];
00075     const float dihedralCos = DotProduct(plane->normal, down);
00076     return dihedralCos >= NEARDOWN_COS;
00077 }
00078 
00084 static inline float Check_PointPlaneDistance (const vec3_t point, const plane_t *plane)
00085 {
00086     /* normal should have a magnitude of one */
00087     assert(fabs(VectorLengthSqr(plane->normal) - 1.0f) < CH_DIST_EPSILON);
00088 
00089     return DotProduct(point, plane->normal) - plane->dist;
00090 }
00091 
00104 static qboolean FacingAndCoincidentTo (const side_t *side1, const side_t *side2)
00105 {
00106     const plane_t *plane1 = &mapplanes[side1->planenum];
00107     const plane_t *plane2 = &mapplanes[side2->planenum];
00108     float distance;
00109 
00110     const float dihedralCos = DotProduct(plane1->normal, plane2->normal);
00111     if (dihedralCos >= -COS_EPSILON)
00112         return qfalse; /* not facing each other */
00113 
00114     /* calculate the distance of point from plane2. as we have established that the
00115      * plane's normals are antiparallel, and plane1->planeVector[0] is a point on plane1
00116      * (that was supplied in the map file), this is the distance
00117      * between the planes */
00118     distance = Check_PointPlaneDistance(plane1->planeVector[0], plane2);
00119 
00120     return fabs(distance) < CH_DIST_EPSILON;
00121 }
00122 
00129 static qboolean ParallelAndCoincidentTo (const side_t *side1, const side_t *side2)
00130 {
00131     float distance;
00132     const plane_t *plane1 = &mapplanes[side1->planenum];
00133     const plane_t *plane2 = &mapplanes[side2->planenum];
00134     const float dihedralCos = DotProduct(plane1->normal, plane2->normal);
00135     if (dihedralCos <= COS_EPSILON)
00136         return qfalse; /* not parallel */
00137 
00138     distance = Check_PointPlaneDistance(plane1->planeVector[0], plane2);
00139 
00140     return fabs(distance) < CH_DIST_EPSILON;
00141 }
00142 
00150 static inline qboolean Check_IsPointInsideBrush (const vec3_t point, const mapbrush_t *brush, const pointInBrush_t mode)
00151 {
00152     int i;
00153     int numPlanes = 0; /* how many of the sides the point is on. on 2 sides, means on an edge. on 3 a vertex */
00154     /* PIB_INCL_SURF is the default */
00155     /* apply epsilon the other way if the surface is excluded */
00156     const float epsilon = CH_DIST_EPSILON * (mode == PIB_EXCL_SURF ? -1.0f : 1.0f);
00157 
00158     for (i = 0; i < brush->numsides; i++) {
00159         const plane_t *plane = &mapplanes[brush->original_sides[i].planenum];
00160 
00161         /* if the point is on the wrong side of any face, then it is outside */
00162         /* distance to one of the planes of the sides, negative implies the point is inside this plane */
00163         const float dist = Check_PointPlaneDistance(point, plane);
00164         if (dist > epsilon)
00165             return qfalse;
00166 
00167         numPlanes += fabs(dist) < CH_DIST_EPSILON ? 1 : 0;
00168     }
00169 
00170     if (mode == PIB_ON_SURFACE_ONLY && numPlanes == 0)
00171         return qfalse; /* must be on at least one surface */
00172 
00173     if (mode == PIB_INCL_SURF_EXCL_EDGE && numPlanes > 1)
00174         return qfalse; /* must not be on more than one side, that would be an edge */
00175 
00176     /* inside all planes, therefore inside the brush */
00177     return qtrue;
00178 }
00179 
00189 static qboolean Check_SurfProp (const int flag, const side_t *s)
00190 {
00191     const ptrdiff_t index = s - brushsides;
00192     const brush_texture_t *tex = &side_brushtextures[index];
00193     switch (flag) {
00194     case SURF_NODRAW:
00195         return !strcmp(tex->name, "tex_common/nodraw");
00196     case CONTENTS_LADDER:
00197         return !strcmp(tex->name, "tex_common/ladder");
00198     case CONTENTS_WEAPONCLIP:
00199         return !strcmp(tex->name, "tex_common/weaponclip");
00200     case CONTENTS_ACTORCLIP:
00201         return !strcmp(tex->name, "tex_common/actorclip");
00202     case CONTENTS_LIGHTCLIP:
00203         return !strcmp(tex->name, "tex_common/lightclip");
00204     case CONTENTS_ORIGIN:
00205         return !strcmp(tex->name, "tex_common/origin");
00206     default:
00207         return qfalse;
00208     }
00209 }
00210 
00221 static qboolean Check_SurfProps (const int flags, const side_t *s)
00222 {
00223     const ptrdiff_t index = s - brushsides;
00224     const brush_texture_t *tex = &side_brushtextures[index];
00225     const char *texname = tex->name;
00226     assert(flags & (SURF_NODRAW | MASK_CLIP));
00227 
00228     if ((flags & SURF_NODRAW) && !strcmp(texname, "tex_common/nodraw"))
00229         return qtrue;
00230 
00231     if ((flags & CONTENTS_WEAPONCLIP) && !strcmp(texname, "tex_common/weaponclip"))
00232         return qtrue;
00233 
00234     if ((flags & CONTENTS_ACTORCLIP) && !strcmp(texname, "tex_common/actorclip"))
00235         return qtrue;
00236 
00237     if ((flags & CONTENTS_LIGHTCLIP) && !strcmp(texname, "tex_common/lightclip"))
00238         return qtrue;
00239 
00240     if ((flags & CONTENTS_LADDER) && !strcmp(texname, "tex_common/ladder"))
00241         return qtrue;
00242 
00243     if ((flags & CONTENTS_ORIGIN) && !strcmp(texname, "tex_common/origin"))
00244         return qtrue;
00245 
00246     return qfalse;
00247 }
00248 
00252 static qboolean Check_IsOptimisable (const mapbrush_t *b)
00253 {
00254     const entity_t *e = &entities[b->entitynum];
00255     const char *name = ValueForKey(e, "classname");
00256     int i, numNodraws = 0;
00257 
00258     if (strcmp(name, "func_group") && strcmp(name, "worldspawn"))
00259         return qfalse;/* other entities, eg func_breakable are no use */
00260 
00261     /* content flags should be the same on all faces, but we shall be suspicious */
00262     for (i = 0; i < b->numsides; i++) {
00263         const side_t *side = &b->original_sides[i];
00264         if (Check_SurfProps(CONTENTS_ORIGIN | MASK_CLIP, side))
00265             return qfalse;
00266         if (side->contentFlags & CONTENTS_TRANSLUCENT)
00267             return qfalse;
00268         numNodraws += Check_SurfProp(SURF_NODRAW, side) ? 1 : 0;
00269     }
00270 
00271     /* all nodraw brushes are special too */
00272     return numNodraws == b->numsides ? qfalse : qtrue;
00273 }
00274 
00278 static qboolean Check_BoundingBoxIntersects (const mapbrush_t *a, const mapbrush_t *b)
00279 {
00280     int i;
00281 
00282     for (i = 0; i < 3; i++)
00283         if (a->mins[i] - CH_DIST_EPSILON >= b->maxs[i] || a->maxs[i] <= b->mins[i] - CH_DIST_EPSILON)
00284             return qfalse;
00285 
00286     return qtrue;
00287 }
00288 
00295 static void Check_NearList (void)
00296 {
00297     /* this function may be called more than once, but we only want this done once */
00298     static qboolean done = qfalse;
00299     mapbrush_t *bbuf[MAX_MAP_BRUSHES];/*< store pointers to brushes here and then malloc them when we know how many */
00300     int i, j, numNear;
00301 
00302     if (done)
00303         return;
00304 
00305     /* make a list for iBrush*/
00306     for (i = 0; i < nummapbrushes; i++) {
00307         mapbrush_t *iBrush = &mapbrushes[i];
00308 
00309         /* test all brushes for nearness to iBrush */
00310         for (j = 0, numNear = 0 ; j < nummapbrushes; j++) {
00311             mapbrush_t *jBrush = &mapbrushes[j];
00312 
00313             if (i == j) /* do not list a brush as being near itself - not useful!*/
00314                 continue;
00315 
00316             if (!Check_BoundingBoxIntersects(iBrush, jBrush))
00317                 continue;
00318 
00319             /* near, therefore add to temp list for iBrush */
00320             assert(numNear < nummapbrushes);
00321             bbuf[numNear++] = jBrush;
00322         }
00323 
00324         iBrush->numNear = numNear;
00325         if (!numNear)
00326             continue;
00327 
00328         /* now we know how many, we can malloc. then copy the pointers */
00329         iBrush->nearBrushes = (mapbrush_t **)Mem_Alloc(numNear * sizeof(mapbrush_t *));
00330 
00331         if (!iBrush->nearBrushes)
00332             Sys_Error("Check_Nearlist: out of memory");
00333 
00334         for (j = 0; j < numNear; j++)
00335             iBrush->nearBrushes[j] = bbuf[j];
00336     }
00337 
00338     done = qtrue;
00339 }
00340 
00349 static qboolean Check_SideIsInBrush (const side_t *side, const mapbrush_t *brush, pointInBrush_t mode)
00350 {
00351     int i;
00352     const winding_t *w = side->winding;
00353 
00354     assert(w->numpoints > 0);
00355 
00356     for (i = 0; i < w->numpoints ; i++)
00357         if (!Check_IsPointInsideBrush(w->p[i], brush, mode))
00358             return qfalse;
00359 
00360     return qtrue;
00361 }
00362 
00363 #if 0
00364 
00368 static void Check_SetError (side_t *s)
00369 {
00370     const ptrdiff_t index = s - brushsides;
00371     brush_texture_t *tex = &side_brushtextures[index];
00372 
00373     Q_strncpyz(tex->name, "tex_common/error", sizeof(tex->name));
00374 }
00375 #endif
00376 
00384 static qboolean Check_SidesTouch (side_t *a, side_t *b)
00385 {
00386     side_t *s[2];
00387     int i, j;
00388 
00389     s[0] = a;
00390     s[1] = b;
00391 
00392     for (i = 0; i < 2; i++) {
00393         const winding_t *w = s[i]->winding; /* winding from one of the sides */
00394         const mapbrush_t *b = s[i ^ 1]->brush; /* the brush that the other side belongs to */
00395 
00396         for (j = 0; j < w->numpoints ; j++) {
00397             if (Check_IsPointInsideBrush(w->p[j], b, PIB_INCL_SURF))
00398                 return qtrue;
00399         }
00400     }
00401     return qfalse;
00402 }
00403 
00404 #if 0
00405 
00406 static qboolean Check_HasTex(const side_t *s, const char *tex)
00407 {
00408     const ptrdiff_t index = s - brushsides;
00409     brush_texture_t *stex = &side_brushtextures[index];
00410 
00411     return strcmp(stex->name, tex) ? qfalse : qtrue;
00412 }
00413 #endif
00414 
00420 static void Check_FindCompositeSides (void)
00421 {
00422     static qboolean done = qfalse;
00423     int i, is, j, k, l, m, numMembers, numDone = 0, numTodo;
00424 
00425     /* store pointers to sides here and then malloc them when we know how many.
00426      * divide by 4 becuase, the minimum number of sides for a brush is 4, so if
00427      * all brushes were lined up, and had one side as a member, that would be their number */
00428     side_t *sbuf[MAX_MAP_SIDES / 4];
00429 
00430     mapbrush_t *bDone[MAX_MAP_SIDES]; /*< an array of brushes to check if the composite propagates across */
00431     mapbrush_t *bTodo[MAX_MAP_SIDES]; /*< an array of brushes that have been checked, without this it would never stop */
00432 
00433     /* this function may be called more than once, but we only want this done once */
00434     if (done)
00435         return;
00436 
00437     Check_NearList();
00438 
00439     /* check each brush, iBrush */
00440     for (i = 0; i < nummapbrushes; i++) {
00441         mapbrush_t *iBrush = &mapbrushes[i];
00442 
00443         if (!Check_IsOptimisable(iBrush))
00444             continue; /* skip clips etc */
00445 
00446         /* check each side, iSide, of iBrush for being the seed of a composite face */
00447         for (is = 0; is < iBrush->numsides; is++) {
00448             side_t *iSide = &iBrush->original_sides[is];
00449 
00450             /* do not find the same composite again. no nodraws
00451              * AddBrushBevels creates sides without windings. skip these too */
00452             if (iSide->isCompositeMember || Check_SurfProp(SURF_NODRAW, iSide) || !iSide->winding)
00453                 continue;
00454 
00455             /* start making the list of brushes in the composite,
00456              * we will only keep it if the composite has more than member */
00457             sbuf[0] = iSide; /* set iSide->isCompositeMember = true later, if we keep the composite */
00458             numMembers = 1;
00459 
00460             /* add neighbouring brushes to the list to check for composite propagation */
00461             numTodo = 0;
00462             for (j = 0; j < iBrush->numNear; j++)
00463                 if (Check_IsOptimisable(iBrush->nearBrushes[j])) {
00464                     bTodo[numTodo++] = iBrush->nearBrushes[j];
00465                 }
00466 
00467             /* this brush's nearlist is listed for checking, so it is done */
00468             bDone[numDone++] = iBrush;
00469 
00470             while (numTodo > 0) {
00471                 mapbrush_t *bChecking = bTodo[--numTodo];
00472                 if (bChecking == NULL)
00473                     continue;
00474                 bDone[numDone++] = bChecking; /* remember so it is not added to the todo list again */
00475 
00476                 for (j = 0; j < bChecking->numsides; j++) {
00477                     side_t *sChecking = &bChecking->original_sides[j];
00478 
00479                     if (Check_SurfProp(SURF_NODRAW, sChecking) || !sChecking->winding)
00480                         continue; /* no nodraws in composites. see comment above above regarding winding*/
00481 
00482                     if (ParallelAndCoincidentTo(iSide, sChecking)) {
00483 
00484                         /* test if sChecking intersects or touches any of sides that are already in the composite*/
00485                         for (k = 0; k < numMembers; k++) {
00486                             if (Check_SidesTouch(sChecking, sbuf[k])) {
00487                                 const mapbrush_t *newMembersBrush = sChecking->brush;
00488                                 sbuf[numMembers++] = sChecking; /* add to the array of members */
00489                                 sChecking->isCompositeMember = qtrue;
00490 
00491                                 /* add this brush's nearList to the todo list, as the compostite may propagate through it */
00492                                 for (l = 0; l < newMembersBrush->numNear;l++) {
00493                                     mapbrush_t *nearListBrush = newMembersBrush->nearBrushes[l];
00494 
00495                                     if (!Check_IsOptimisable(nearListBrush))
00496                                         continue; /* do not propogate across clips etc */
00497 
00498                                     /* only add them to the todo list if they are not on the done list
00499                                      * as a brush cannot have parallel sides, this also ensures the same side
00500                                      * is not added to a composite more than once */
00501                                     for (m = 0; m < numDone; m++) {
00502                                         if (nearListBrush == bDone[m])
00503                                             goto skip_add_brush_to_todo_list;
00504                                     }
00505                                     bTodo[numTodo++] = nearListBrush;
00506 
00507                                     skip_add_brush_to_todo_list:
00508                                     ; /* there must be a statement after the label, ";" will do */
00509                                 }
00510                                 goto next_brush_todo; /* need not test any more sides of this brush, if a member is found */
00511                             }
00512                         }
00513                     }
00514                 }
00515                 next_brush_todo:
00516                 ;
00517             }
00518 
00519             if (numMembers > 1) { /* composite found */
00520                 side_t **sidesInNewComposite = (side_t **)Mem_Alloc(numMembers * sizeof(*sidesInNewComposite));
00521 
00522                 if (!sidesInNewComposite)
00523                     Sys_Error("Check_FindCompositeSides: out of memory");
00524 
00525                 /* this was not done before for the first side, as we did not know it would have at least 2 members */
00526                 iSide->isCompositeMember = qtrue;
00527 
00528                 compositeSides[numCompositeSides].numMembers = numMembers;
00529                 compositeSides[numCompositeSides].memberSides = sidesInNewComposite;
00530 
00531                 for (j = 0; j < numMembers; j++) {
00532                     compositeSides[numCompositeSides].memberSides[j] = sbuf[j];
00533                 }
00534                 numCompositeSides++;
00535             }
00536         }
00537     }
00538 
00539     Check_Printf(VERB_EXTRA, qfalse, -1, -1, "%i composite sides found\n", numCompositeSides);
00540 
00541     done = qtrue;
00542 }
00543 
00551 static int Check_EdgePlaneIntersection (const vec3_t vert1, const vec3_t vert2, const plane_t *plane, vec3_t intersection)
00552 {
00553     vec3_t direction; /* a vector in the direction of the line */
00554     vec3_t lineToPlane; /* a line from vert1 on the line to a point on the plane */
00555     float sin; /* sine of angle to plane, cosine of angle to normal */
00556     float param; /* param in line equation  line = vert1 + param * (vert2 - vert1) */
00557     float length; /* length of the edge */
00558 
00559     VectorSubtract(vert2, vert1, direction);/*< direction points from vert1 to vert2 */
00560     length = VectorLength(direction);
00561     if (length < DIST_EPSILON)
00562         return qfalse;
00563     sin = DotProduct(direction, plane->normal) / length;
00564     if (fabs(sin) < SIN_EPSILON)
00565         return qfalse;
00566     VectorSubtract(plane->planeVector[0], vert1, lineToPlane);
00567     param = DotProduct(plane->normal, lineToPlane) / DotProduct(plane->normal, direction);
00568     VectorMul(param, direction, direction);/*< now direction points from vert1 to intersection */
00569     VectorAdd(vert1, direction, intersection);
00570     param = param * length;/*< param is now the distance along the edge from vert1 */
00571     return (param > CH_DIST_EPSILON) && (param < (length - CH_DIST_EPSILON));
00572 }
00573 
00578 static qboolean Check_WindingIntersects (const winding_t *winding, const mapbrush_t *brush)
00579 {
00580     vec3_t intersection;
00581     int vi, bi;
00582 
00583     for (bi = 0; bi < brush->numsides; bi++) {
00584         for (vi = 0; vi < winding->numpoints; vi++) {
00585             const int val = vi + 1;
00586             const int vj = (winding->numpoints == val) ? 0 : val;
00587             if (Check_EdgePlaneIntersection(winding->p[vi], winding->p[vj], &mapplanes[brush->original_sides[bi].planenum], intersection))
00588                 if (Check_IsPointInsideBrush(intersection, brush, PIB_INCL_SURF_EXCL_EDGE))
00589                     return qtrue;
00590         }
00591     }
00592     return qfalse;
00593 }
00594 
00598 void Check_BrushIntersection (void)
00599 {
00600     int i, j, is;
00601 
00602     /* initialise mapbrush_t.nearBrushes */
00603     Check_NearList();
00604 
00605     for (i = 0; i < nummapbrushes; i++) {
00606         const mapbrush_t *iBrush = &mapbrushes[i];
00607 
00608         if (!Check_IsOptimisable(iBrush))
00609             continue;
00610 
00611         for (j = 0; j < iBrush->numNear; j++) {
00612             const mapbrush_t *jBrush = iBrush->nearBrushes[j];
00613 
00614             if (!Check_IsOptimisable(jBrush))
00615                 continue;
00616 
00617             /* check each side of i for intersection with brush j */
00618             for (is = 0; is < iBrush->numsides; is++) {
00619                 const winding_t *winding = (iBrush->original_sides[is].winding);
00620                 if (Check_WindingIntersects(winding, jBrush)) {
00621                     Check_Printf(VERB_CHECK, qfalse, iBrush->entitynum, iBrush->brushnum, "intersects with brush %i (entity %i)\n", jBrush->brushnum, jBrush->entitynum);
00622                     break;
00623                 }
00624             }
00625         }
00626     }
00627 }
00628 
00637 static qboolean Check_EdgeEdgeIntersection (const vec3_t e1p1, const vec3_t e1p2,
00638                     const vec3_t e2p1, const vec3_t e2p2, vec3_t intersection)
00639 {
00640     vec3_t dir1, dir2, unitDir1, unitDir2;
00641     vec3_t dirClosestApproach, from1To2, e1p1ToIntersection, e2p1ToIntersection;
00642     vec3_t cross1, cross2;
00643     float cosAngle, length1, length2, dist, magCross2, param1;
00644     float e1p1Dist, e2p1Dist;
00645 
00646     VectorSubtract(e1p2, e1p1, dir1);
00647     VectorSubtract(e2p2, e2p1, dir2);
00648     length1 = VectorLength(dir1);
00649     length2 = VectorLength(dir2);
00650 
00651     if (length1 < CH_DIST_EPSILON || length2 < CH_DIST_EPSILON)
00652         return qfalse; /* edges with no length cannot intersect */
00653 
00654     VectorScale(dir1, 1.0f / length1, unitDir1);
00655     VectorScale(dir2, 1.0f / length2, unitDir2);
00656 
00657     cosAngle = fabs(DotProduct(unitDir1, unitDir2));
00658 
00659     if (cosAngle >= COS_EPSILON)
00660         return qfalse; /* parallel lines either do not intersect, or are coincident */
00661 
00662     CrossProduct(unitDir1, unitDir2, dirClosestApproach);
00663     VectorNormalize(dirClosestApproach);
00664 
00665     VectorSubtract(e2p1, e1p1, from1To2);
00666     dist = fabs(DotProduct(dirClosestApproach, from1To2));
00667 
00668     if (dist > CH_DIST_EPSILON)
00669         return qfalse; /* closest approach of skew lines is nonzero: no intersection */
00670 
00671     CrossProduct(from1To2, dir2, cross1);
00672     CrossProduct(dir1, dir2, cross2);
00673     magCross2 = VectorLength(cross2);
00674     param1 = DotProduct(cross1, cross2) / (magCross2 * magCross2);
00675     VectorScale(dir1, param1, e1p1ToIntersection);
00676     VectorAdd(e1p1, e1p1ToIntersection, intersection);
00677     e1p1Dist = DotProduct(e1p1ToIntersection, unitDir1);
00678 
00679     if (e1p1Dist < CH_DIST_EPSILON || e1p1Dist > (length1 - CH_DIST_EPSILON))
00680         return qfalse; /* intersection is not between vertices of edge 1 */
00681 
00682     VectorSubtract(intersection, e2p1, e2p1ToIntersection);
00683     e2p1Dist = DotProduct(e2p1ToIntersection, unitDir2);
00684     if (e2p1Dist < CH_DIST_EPSILON || e2p1Dist > (length2 - CH_DIST_EPSILON))
00685         return qfalse; /* intersection is not between vertices of edge 2 */
00686 
00687     return qtrue;
00688 }
00689 
00690 #if 0 /* not used since z-fight test changed to calculate area of overlap instead */
00691 
00698 static qboolean Check_PointsAreCollinear (const vec3_t a, const vec3_t b, const vec3_t c)
00699 {
00700     vec3_t d1, d2, d3, cross;
00701     float d1d, d2d, d3d, offLineDist;
00702 
00703     VectorSubtract(a, b, d1);
00704     VectorSubtract(a, c, d2);
00705     VectorSubtract(b, c, d3);
00706 
00707     d1d = VectorLength(d1);
00708     d2d = VectorLength(d2);
00709     d3d = VectorLength(d3);
00710 
00711     /* if 2 points are in the same place, we only have 2 points, which must be in a line */
00712     if (d1d < CH_DIST_EPSILON || d2d < CH_DIST_EPSILON || d3d < CH_DIST_EPSILON)
00713         return qtrue;
00714 
00715     if (d1d >= d2d && d1d >= d3d) {
00716         CrossProduct(d2, d3, cross);
00717         offLineDist = VectorLength(cross) / d1d;
00718     } else if (d2d >= d1d && d2d >= d3d) {
00719         CrossProduct(d1, d3, cross);
00720         offLineDist = VectorLength(cross) / d2d;
00721     } else { /* d3d must be the largest */
00722         CrossProduct(d1, d2, cross);
00723         offLineDist = VectorLength(cross) / d3d;
00724     }
00725 
00726     return offLineDist < CH_DIST_EPSILON_COLLINEAR_POINTS;
00727 }
00728 #endif
00729 
00730 static float Check_LongestEdge (const winding_t *w)
00731 {
00732     float longestSqr = 0;
00733     int i;
00734     for (i = 0; i < w->numpoints; i++) {
00735         const int j = (i + 1) % w->numpoints;
00736         const float lengthSqr = VectorDistSqr(w->p[i], w->p[j]);
00737         longestSqr = longestSqr > lengthSqr ? longestSqr : lengthSqr;
00738     }
00739     return sqrt(longestSqr);
00740 }
00741 
00742 #define VERT_BUF_SIZE_DISJOINT_SIDES 21
00743 #define OVERLAP_AREA_TOL 0.2f
00744 #define OVERLAP_WIDTH_TOL 0.1f
00745 
00755 static float Check_SidesOverlap (const side_t *s1, const side_t *s2)
00756 {
00757     vec3_t vertbuf[VERT_BUF_SIZE_DISJOINT_SIDES];/* vertices of intersection of sides. arbitrary choice of size: more than 4 is unusual */
00758     int numVert = 0, i, j, k;
00759     winding_t *w[2];
00760     mapbrush_t *b[2];
00761 
00762     w[0] = s1->winding; w[1] = s2->winding;
00763     b[0] = s1->brush; b[1] = s2->brush;
00764 
00765     /* test if points from first winding are in (or on) brush that is parent of second winding
00766      * and vice - versa. i ^ 1 toggles */
00767     for (i = 0; i < 2; i++) {
00768         for (j = 0; j < w[i]->numpoints ; j++) {
00769             if (Check_IsPointInsideBrush(w[i]->p[j], b[i ^ 1], PIB_INCL_SURF)) {
00770                 if (numVert == VERT_BUF_SIZE_DISJOINT_SIDES) {
00771                     Check_Printf(VERB_CHECK, qfalse, b[i]->entitynum, b[i]->brushnum, "warning: Check_SidesAreDisjoint buffer too small");
00772                     return -1.0f;
00773                 }
00774                 VectorCopy(w[i]->p[j], vertbuf[numVert]);
00775                 numVert++;
00776             }
00777         }
00778     }
00779 
00780     /* test for intersections between windings*/
00781     for (i = 0; i < w[0]->numpoints; i++) {
00782         const int pointIndex = (i + 1) % w[0]->numpoints;
00783         for (k = 0; k < w[1]->numpoints; k++) {
00784             const int pointIndex2 = (k + 1) % w[1]->numpoints;
00785             if (Check_EdgeEdgeIntersection(w[0]->p[i], w[0]->p[pointIndex], w[1]->p[k], w[1]->p[pointIndex2], vertbuf[numVert])) {
00786                 numVert++; /* if intersection, keep it */
00787                 if (numVert == VERT_BUF_SIZE_DISJOINT_SIDES) {
00788                     Check_Printf(VERB_CHECK, qfalse, b[i]->entitynum, b[i]->brushnum, "warning: Check_SidesAreDisjoint buffer too small");
00789                     return -1.0f;
00790                 }
00791             }
00792         }
00793     }
00794 
00795     if (numVert < 3)
00796         return -1.0f; /* must be at least 3 points to be not in a line */
00797 
00798     {
00799         /* make a winding, so WindingArea can be used */
00800         float overlapArea, longestEdge, width;
00801         winding_t *overlap = AllocWinding(numVert);
00802         overlap->numpoints = numVert;
00803         memcpy(overlap->p, vertbuf, numVert * sizeof(vec3_t));
00804         overlapArea = WindingArea(overlap);
00805 #if 0
00806         if (overlapArea > OVERLAP_AREA_TOL) {
00807             int i;
00808             for (i = 0; i < numVert; i++) {
00809                 Print3Vector(vertbuf[i]);
00810             }
00811         }
00812 #endif
00813         /* small area, do not waste time calculating width */
00814         if (overlapArea < OVERLAP_AREA_TOL) {
00815             Mem_Free(overlap);
00816             return -1.0f;
00817         }
00818 
00819         longestEdge = Check_LongestEdge(overlap);
00820         width = overlapArea / longestEdge;
00821         Mem_Free(overlap);
00822         return width > OVERLAP_WIDTH_TOL ? width : -1.0f;
00823     }
00824 
00825 #if 0
00826     {
00827         vec3_t from0to1, one, zero;
00828 
00829         /* skip past elements 0, 1, ... if they are coincident - to avoid division by zero */
00830         i = 0;
00831         do {
00832             i++;
00833             if ((i + 1) >= numVert)
00834                 return qfalse; /* not enough separated points - they must be in a line */
00835             VectorSubtract(vertbuf[i], vertbuf[i - 1], from0to1);
00836             VectorCopy(vertbuf[i - 1], zero);
00837             VectorCopy(vertbuf[i], one);
00838         } while (VectorLength(from0to1) < CH_DIST_EPSILON);
00839 
00840         for (i++; i < numVert; i++) {
00841             if (!Check_PointsAreCollinear(zero, one, vertbuf[i])) {
00842                 return qtrue; /* 3 points not in a line, there is overlap */
00843             }
00844         }
00845     }
00846 
00847     return qfalse; /* all points are collinear */
00848 #endif
00849 }
00850 
00854 void CheckZFighting (void)
00855 {
00856     int i, j, is, js;
00857 
00858     /* initialise mapbrush_t.nearBrushes */
00859     Check_NearList();
00860 
00861     /* loop through all pairs of near brushes */
00862     for (i = 0; i < nummapbrushes; i++) {
00863         const mapbrush_t *iBrush = &mapbrushes[i];
00864 
00865         if (!Check_IsOptimisable(iBrush))
00866             continue; /* skip moving brushes, clips etc */
00867 
00868         for (j = 0; j < iBrush->numNear; j++) {
00869             const mapbrush_t *jBrush = iBrush->nearBrushes[j];
00870 
00871             if ((iBrush->contentFlags & CONTENTS_LEVEL_ALL) != (jBrush->contentFlags & CONTENTS_LEVEL_ALL))
00872                 continue; /* must be on the same level */
00873 
00874             if (!Check_IsOptimisable(jBrush))
00875                 continue; /* skip moving brushes, clips etc */
00876 
00877             for (is = 0; is < iBrush->numsides; is++) {
00878                 const side_t *iSide = &iBrush->original_sides[is];
00879 
00880                 if (Check_SurfProp(SURF_NODRAW, iSide))
00881                     continue; /* skip nodraws */
00882 
00883                 if (Check_SidePointsDown(iSide))
00884                     continue; /* can't see these, view is always from above */
00885 
00886                 /* check each side of brush j for doing the hiding */
00887                 for (js = 0; js < jBrush->numsides; js++) {
00888                     const side_t *jSide = &jBrush->original_sides[js];
00889 
00890                     /* skip nodraws */
00891                     if (Check_SurfProp(SURF_NODRAW, jSide))
00892                         continue;
00893 
00894 #if 0
00895 
00898                     if (ParallelAndCoincidentTo(iSide, jSide))
00899                         if (jSide->planenum != jSide->planenum)
00900                             Com_Printf("CheckZFighting: plane indices %i %i \n",
00901                                 iSide->planenum, jSide->planenum);
00902 #endif
00903 
00904                     if (ParallelAndCoincidentTo(iSide, jSide) ) {
00905                         float overlapWidth = Check_SidesOverlap(iSide, jSide);
00906                         if ( overlapWidth > 0.0f) {
00907                             Check_Printf(VERB_CHECK, qfalse, iBrush->entitynum, iBrush->brushnum,
00908                                 "z-fighting with brush %i (entity %i). overlap width: %.3g units\n",
00909                                 jBrush->brushnum, jBrush->entitynum, overlapWidth);
00910 #if 0
00911                             Check_SetError(iSide);
00912                             Check_SetError(jSide);
00913 #endif
00914                         }
00915                     }
00916                 }
00917             }
00918         }
00919     }
00920 }
00921 
00925 void Check_ContainedBrushes (void)
00926 {
00927     int i, j, js;
00928 
00929     /* initialise mapbrush_t.nearBrushes */
00930     Check_NearList();
00931 
00932     for (i = 0; i < nummapbrushes; i++) {
00933         mapbrush_t *iBrush = &mapbrushes[i];
00934 
00935         /* do not check for brushes inside special (clip etc) brushes */
00936         if (!Check_IsOptimisable(iBrush))
00937             continue;
00938 
00939         for (j = 0; j < iBrush->numNear; j++) {
00940             mapbrush_t *jBrush = iBrush->nearBrushes[j];
00941             int numSidesInside = 0;
00942 
00943             if (jBrush->contentFlags & CONTENTS_ORIGIN)
00944                 continue; /* origin brushes are allowed inside others */
00945 
00946             for (js = 0; js < jBrush->numsides; js++) {
00947                 const side_t *jSide = &jBrush->original_sides[js];
00948 
00949                 if (Check_SideIsInBrush(jSide, iBrush, PIB_INCL_SURF))
00950                     numSidesInside++;
00951             }
00952 
00953             if (numSidesInside == jBrush->numsides) {
00954                 Check_Printf(VERB_CHECK, qfalse, jBrush->entitynum, jBrush->brushnum, "inside brush %i (entity %i)\n",
00955                             iBrush->brushnum, iBrush->entitynum);
00956             }
00957         }
00958     }
00959 }
00960 
00965 static int Check_LevelForNodraws (const side_t *coverer, const side_t *coveree)
00966 {
00967     return !(CONTENTS_LEVEL_ALL & ~coverer->contentFlags & coveree->contentFlags);
00968 }
00969 
00970 static void Check_SetNodraw (side_t *s)
00971 {
00972     const ptrdiff_t index = s - brushsides;
00973     brush_texture_t *tex = &side_brushtextures[index];
00974 
00975     Q_strncpyz(tex->name, "tex_common/nodraw", sizeof(tex->name));
00976 
00977     /* do not actually set the flag that will be written back on -fix
00978      * the texture is set, this should trigger the flag to be set
00979      * in compile mode. check should behave the same as fix.
00980      * The flag must be set in compile mode, as SetImpliedFlags calls are before the
00981      * CheckNodraws call */
00982     if (!(config.fixMap || config.performMapCheck))
00983         tex->surfaceFlags |= SURF_NODRAW;
00984 
00985     s->surfaceFlags &= ~SURF_PHONG;
00986     tex->surfaceFlags &= ~SURF_PHONG;
00987     s->surfaceFlags |= SURF_NODRAW;
00988 }
00989 
00990 #define CH_COMP_NDR_EDGE_INTSCT_BUF 21
00991 
00999 void CheckNodraws (void)
01000 {
01001     int i, j, k, l, m, n, is, js;
01002     int numSetFromSingleSide = 0, numSetPointingDown = 0, numSetFromCompositeSide = 0, iBrushNumSet = 0;
01003 
01004     /* Initialise compositeSides[].. Note that this function
01005      * calls Check_NearList to initialise mapbrush_t.nearBrushes */
01006     Check_FindCompositeSides();
01007 
01008     /* check each brush, i, for downward sides */
01009     for (i = 0; i < nummapbrushes; i++) {
01010         mapbrush_t *iBrush = &mapbrushes[i];
01011         iBrushNumSet = 0;
01012 
01013         /* skip moving brushes, clips etc */
01014         if (!Check_IsOptimisable(iBrush))
01015             continue;
01016 
01017         /* check each side of i for pointing down */
01018         for (is = 0; is < iBrush->numsides; is++) {
01019             side_t *iSide = &iBrush->original_sides[is];
01020 
01021             /* skip those that are already nodraw */
01022             if (Check_SurfProp(SURF_NODRAW, iSide))
01023                 continue;
01024             /* surface lights may point downwards */
01025             else if (iSide->surfaceFlags & SURF_LIGHT)
01026                 continue;
01027 
01028             if (Check_SidePointsDown(iSide)) {
01029                 Check_SetNodraw(iSide);
01030                 numSetPointingDown++;
01031                 iBrushNumSet++;
01032             }
01033 
01034         }
01035         if (iBrushNumSet)
01036             Check_Printf(VERB_EXTRA, qtrue, iBrush->entitynum, iBrush->brushnum, "set nodraw on %i sides (point down, or are close to pointing down).\n", iBrushNumSet);
01037     } /* next iBrush for downward faces that can be nodraw */
01038     if (numSetPointingDown)
01039         Check_Printf(VERB_CHECK, qtrue, -1, -1, "total of %i nodraws set (point down, or are close to pointing down)\n", numSetPointingDown);
01040 
01041     /* check each brush, i, for hidden sides */
01042     for (i = 0; i < nummapbrushes; i++) {
01043         mapbrush_t *iBrush = &mapbrushes[i];
01044         iBrushNumSet = 0;
01045 
01046         /* skip moving brushes, clips etc */
01047         if (!Check_IsOptimisable(iBrush))
01048             continue;
01049 
01050         /* check each brush, j, for having a side that hides one of i's faces */
01051         for (j = 0; j < iBrush->numNear; j++) {
01052             mapbrush_t *jBrush = iBrush->nearBrushes[j];
01053 
01054             /* skip moving brushes, clips etc */
01055             if (!Check_IsOptimisable(jBrush))
01056                 continue;
01057 
01058             /* check each side of i for being hidden */
01059             for (is = 0; is < iBrush->numsides; is++) {
01060                 side_t *iSide = &iBrush->original_sides[is];
01061 
01062                 if (!iSide->winding)
01063                     continue; /* AddBrushBevels adds sides with no windings. skip these */
01064 
01065                 /* skip those that are already nodraw */
01066                 if (Check_SurfProp(SURF_NODRAW, iSide))
01067                     continue;
01068                 /* surface lights may point downwards */
01069                 else if (iSide->surfaceFlags & SURF_LIGHT)
01070                     continue;
01071 
01072                 /* check each side of brush j for doing the hiding */
01073                 for (js = 0; js < jBrush->numsides; js++) {
01074                     const side_t *jSide = &jBrush->original_sides[js];
01075 
01076                     if (!jSide->winding)
01077                         continue; /* AddBrushBevels adds sides with no windings. skip these */
01078 
01079 #if 0
01080                     /* run on a largish map, this section proves that the plane indices alone cannot
01081                      * cannot be relied on to test for 2 planes facing each other. */
01082                     if (FacingAndCoincidentTo(iSide, jSide)) {
01083                         const int minIndex = min(iSide->planenum, jSide->planenum);
01084                         const int maxIndex = max(iSide->planenum, jSide->planenum);
01085                         const int diff = maxIndex - minIndex, minOdd = (minIndex & 1);
01086                         if ((diff != 1) || minOdd) {
01087                             Com_Printf("CheckNodraws: facing and coincident plane indices %i %i diff:%i minOdd:%i\n",
01088                                 iSide->planenum, jSide->planenum, diff, minOdd);
01089                         }
01090                     }
01091 #endif
01092 
01093                     if (Check_LevelForNodraws(jSide, iSide) &&
01094                         FacingAndCoincidentTo(iSide, jSide) &&
01095                         Check_SideIsInBrush(iSide, jBrush, PIB_INCL_SURF)) {
01096                         Check_SetNodraw(iSide);
01097                         iBrushNumSet++;
01098                         numSetFromSingleSide++;
01099                     }
01100                 }
01101             }
01102         } /* next jBrush */
01103         if (iBrushNumSet)
01104             Check_Printf(VERB_EXTRA, qtrue, iBrush->entitynum, iBrush->brushnum, "set nodraw on %i sides (covered by another brush).\n", iBrushNumSet);
01105 
01106         iBrushNumSet = 0; /* reset to count composite side coverings */
01107 
01108         /* check each composite side for hiding one of iBrush's sides */
01109         for (j = 0; j < numCompositeSides; j++) {
01110             const compositeSide_t *composite = &compositeSides[j];
01111             assert(composite);
01112             assert(composite->memberSides[0]);
01113 
01114             /* check each side for being hidden */
01115             for (is = 0; is < iBrush->numsides; is++) {
01116                 side_t *iSide = &iBrush->original_sides[is];
01117                 winding_t *iWinding;
01118                 vec3_t lastIntersection = {0, 0, 0}; /* used in innner loop, here to avoid repeated memset calls */
01119 
01120                 if (!FacingAndCoincidentTo(iSide, composite->memberSides[0]))
01121                     continue; /* all sides in the composite are parallel, and iSide must face them to be hidden */
01122 
01123                 /* skip those that are already nodraw. note: this includes sides hidden by single sides
01124                  * set above - this prevents duplicate nodraw reports */
01125                 if (Check_SurfProp(SURF_NODRAW, iSide))
01126                     continue;
01127 
01128                 iWinding = iSide->winding;
01129 
01130                 if (!iWinding)
01131                     continue; /* AddBrushBevels adds sides with no windings. skip these */
01132 
01133                 /* to be covered each vertex of iSide must be on one of the composite side's members */
01134                 for (k = 0; k < iWinding->numpoints; k++) {
01135                     qboolean pointOnComposite = qfalse;
01136                     for (l = 0; l < composite->numMembers; l++) {
01137                         if (Check_IsPointInsideBrush(iWinding->p[k], composite->memberSides[l]->brush, PIB_INCL_SURF)) {
01138 
01139                             /* levelflags mean this member cannot cover iSide
01140                              * might be wrong to assume the composite will not cover iSide (if the members intersect)
01141                               * it is _safe_ in that it will not result in an exposed nodraw */
01142                             if (!Check_LevelForNodraws(composite->memberSides[l], iSide))
01143                                 goto next_iSide;
01144 
01145                             pointOnComposite = qtrue;
01146                             break;
01147                         }
01148                     }
01149                     if (!pointOnComposite)
01150                         goto next_iSide;
01151                 }
01152 
01153                 /* search for intersections between composite and iSide */
01154                 for (k = 0; k < iWinding->numpoints; k++) {
01155                     vec3_t intersection;
01156                     int lastIntersectionMembInd = -1;
01157                     vec3_t intersections[CH_COMP_NDR_EDGE_INTSCT_BUF];
01158                     qboolean paired[CH_COMP_NDR_EDGE_INTSCT_BUF];
01159                     int numIntsct = 0;
01160 
01161                     memset(paired, '\0', CH_COMP_NDR_EDGE_INTSCT_BUF * sizeof(qboolean));
01162 
01163                     for (l = 0; l < composite->numMembers; l++) {
01164                         const winding_t *mWinding = composite->memberSides[l]->winding;
01165 
01166                         for (m = 0; m < mWinding->numpoints; m++) {
01167                             qboolean intersects = Check_EdgeEdgeIntersection(
01168                                 iWinding->p[k], iWinding->p[(k + 1) % iWinding->numpoints],
01169                                 mWinding->p[m], mWinding->p[(m + 1) % mWinding->numpoints],
01170                                 intersection);
01171 
01172                             if (intersects) {
01173                                 qboolean coincident = qfalse;
01174                                 /* check for coincident intersections */
01175                                 for (n = 0; n < numIntsct; n++) {
01176                                     float distSq = VectorDistSqr(intersection, intersections[n]);
01177                                     if (CH_DIST_EPSILON_SQR > distSq) {
01178                                         paired[n] = qtrue;
01179                                         coincident = qtrue;
01180                                     }
01181                                 }
01182 
01183                                 /* if it is not coincident, then add it to the list */
01184                                 if (!coincident) {
01185                                     VectorCopy(intersection, intersections[numIntsct]);
01186                                     numIntsct++;
01187                                     if (numIntsct >= CH_COMP_NDR_EDGE_INTSCT_BUF) {
01188                                         Check_Printf(VERB_LESS, qfalse, -1, -1, "warning: CheckNodraws: buffer too small");
01189                                         return;
01190                                     }
01191                                 }
01192 
01193                                 /* if edge k of iSide crosses side l of composite then check levelflags */
01194                                 /* note that as each member side is convex any line can intersect its edges a maximum of twice,
01195                                  * as the member sides of the composite are the inner loop, these two (if they exist) will
01196                                  * be found consecutively */
01197                                 if ((lastIntersectionMembInd == l) /* same composite as last intersection found */
01198                                  && (VectorDistSqr(intersection, lastIntersection) > CH_DIST_EPSILON_SQR) /* dist between this and last intersection is nonzero, indicating they are different intersections */
01199                                  && !Check_LevelForNodraws(composite->memberSides[l], iSide)) /* check nodraws */
01200                                     goto next_iSide;
01201 
01202                                 lastIntersectionMembInd = l;
01203                                 VectorCopy(intersection, lastIntersection);
01204                             }
01205                         }
01206                     }
01207 
01208                     /* make sure all intersections are paired. an unpaired intersection indicates
01209                      * that iSide's boundary crosses out of the composite side, so iSide is not hidden */
01210                     for (l = 0; l < numIntsct; l++) {
01211                         if (!paired[l])
01212                             goto next_iSide;
01213                     }
01214 
01215                 }
01216 
01217                 /* set nodraw for iSide (covered by composite) */
01218                 Check_SetNodraw(iSide);
01219                 iBrushNumSet++;
01220                 numSetFromCompositeSide++;
01221 
01222                 next_iSide:
01223                 ;
01224             }
01225         } /* next composite */
01226         if (iBrushNumSet)
01227             Check_Printf(VERB_EXTRA, qtrue, iBrush->entitynum, iBrush->brushnum, "set nodraw on %i sides (covered by a composite side).\n", iBrushNumSet);
01228     } /* next iBrush */
01229 
01230     if (numSetFromSingleSide)
01231         Check_Printf(VERB_CHECK, qtrue, -1, -1, "%i nodraws set (covered by another brush).\n", numSetFromSingleSide);
01232 
01233     if (numSetFromCompositeSide)
01234         Check_Printf(VERB_CHECK, qtrue, -1, -1, "%i nodraws set (covered by a composite side).\n", numSetFromCompositeSide);
01235 
01236 }
01237 
01243 static qboolean Check_DuplicateBrushPlanes (const mapbrush_t *b)
01244 {
01245     int i, j;
01246     const side_t *sides = b->original_sides;
01247 
01248     for (i = 1; i < b->numsides; i++) {
01249         /* check for a degenerate plane */
01250         if (sides[i].planenum == -1) {
01251             Check_Printf(VERB_CHECK, qfalse, b->entitynum, b->brushnum, "degenerate plane\n");
01252             continue;
01253         }
01254 
01255         /* check for duplication and mirroring */
01256         for (j = 0; j < i; j++) {
01257             if (sides[i].planenum == sides[j].planenum) {
01258                 /* remove the second duplicate */
01259                 Check_Printf(VERB_CHECK, qfalse, b->entitynum, b->brushnum, "mirrored or duplicated\n");
01260                 break;
01261             }
01262 
01263             if (sides[i].planenum == (sides[j].planenum ^ 1)) {
01264                 Check_Printf(VERB_CHECK, qfalse, b->entitynum, b->brushnum, "mirror plane - brush is invalid\n");
01265                 return qfalse;
01266             }
01267         }
01268     }
01269     return qtrue;
01270 }
01271 
01275 static vec_t Check_MapBrushVolume (const mapbrush_t *brush)
01276 {
01277     int i;
01278     const winding_t *w;
01279     vec3_t corner;
01280     vec_t d, area, volume;
01281     const plane_t *plane;
01282 
01283     if (!brush)
01284         return 0;
01285 
01286     /* grab the first valid point as the corner */
01287     w = NULL;
01288     for (i = 0; i < brush->numsides; i++) {
01289         w = brush->original_sides[i].winding;
01290         if (w)
01291             break;
01292     }
01293     if (!w)
01294         return 0;
01295     VectorCopy(w->p[0], corner);
01296 
01297     /* make tetrahedrons to all other faces */
01298     volume = 0;
01299     for (; i < brush->numsides; i++) {
01300         w = brush->original_sides[i].winding;
01301         if (!w)
01302             continue;
01303         plane = &mapplanes[brush->original_sides[i].planenum];
01304         d = -(DotProduct(corner, plane->normal) - plane->dist);
01305         area = WindingArea(w);
01306         volume += d * area;
01307     }
01308 
01309     return volume / 3;
01310 }
01311 
01315 void CheckMapMicro (void)
01316 {
01317     int i;
01318 
01319     for (i = 0; i < nummapbrushes; i++) {
01320         mapbrush_t *brush = &mapbrushes[i];
01321         const float vol = Check_MapBrushVolume(brush);
01322         if (vol < config.mapMicrovol) {
01323             Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum, "microbrush volume %f - will be deleted\n", vol);
01324             brush->skipWriteBack = qtrue;
01325         }
01326     }
01327 }
01328 
01333 void DisplayContentFlags (const int flags)
01334 {
01335     if (!flags) {
01336         Check_Printf(VERB_CHECK, qfalse, NUM_SAME, NUM_SAME, " no contentflags");
01337         return;
01338     }
01339 #define M(x) if (flags & CONTENTS_##x) Check_Printf(VERB_CHECK, qfalse, NUM_SAME, NUM_SAME, " " #x)
01340     M(SOLID);
01341     M(WINDOW);
01342     M(LADDER);
01343     M(WATER);
01344     M(LEVEL_1);
01345     M(LEVEL_2);
01346     M(LEVEL_3);
01347     M(LEVEL_4);
01348     M(LEVEL_5);
01349     M(LEVEL_6);
01350     M(LEVEL_7);
01351     M(LEVEL_8);
01352     M(ACTORCLIP);
01353     M(PASSABLE);
01354     M(ACTOR);
01355     M(ORIGIN);
01356     M(WEAPONCLIP);
01357     M(DEADACTOR);
01358     M(DETAIL);
01359     M(TRANSLUCENT);
01360 #undef M
01361 }
01362 
01366 static int Check_CalculateLevelFlagFill (int contentFlags)
01367 {
01368     int firstSetLevel = 0, lastSetLevel = 0;
01369     int scanLevel, flagFill = 0;
01370 
01371     for (scanLevel = CONTENTS_LEVEL_1; scanLevel <= CONTENTS_LEVEL_8; scanLevel <<= 1) {
01372         if (scanLevel & contentFlags) {
01373             if (!firstSetLevel) {
01374                 firstSetLevel = scanLevel;
01375             } else {
01376                 lastSetLevel = scanLevel;
01377             }
01378         }
01379     }
01380     for (scanLevel = firstSetLevel << 1 ; scanLevel < lastSetLevel; scanLevel <<= 1)
01381         flagFill |= scanLevel & ~contentFlags;
01382     return flagFill;
01383 }
01384 
01388 void CheckFillLevelFlags (void)
01389 {
01390     int i, j, flagFill;
01391 
01392     for (i = 0; i < nummapbrushes; i++) {
01393         mapbrush_t *brush = &mapbrushes[i];
01394 
01395         /* CheckLevelFlags should be done first, so we will boldly
01396          * assume that levelflags are the same on each face */
01397         flagFill = Check_CalculateLevelFlagFill(brush->original_sides[0].contentFlags);
01398         if (flagFill) {
01399             Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum, "making set levelflags continuous by setting");
01400             DisplayContentFlags(flagFill);
01401             Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum, "\n");
01402             for (j = 0; j < brush->numsides; j++)
01403                 brush->original_sides[j].contentFlags |= flagFill;
01404         }
01405     }
01406 }
01407 
01411 void CheckLevelFlags (void)
01412 {
01413     int i, j;
01414     qboolean allNodraw, setFlags;
01415     int allLevelFlagsForBrush;
01416 
01417     for (i = 0; i < nummapbrushes; i++) {
01418         mapbrush_t *brush = &mapbrushes[i];
01419 
01420         /* test if all faces are nodraw */
01421         allNodraw = qtrue;
01422         for (j = 0; j < brush->numsides; j++) {
01423             const side_t *side = &brush->original_sides[j];
01424             assert(side);
01425 
01426             if (!(Check_SurfProp(SURF_NODRAW, side))) {
01427                 allNodraw = qfalse;
01428                 break;
01429             }
01430         }
01431 
01432         /* proceed if some or all faces are not nodraw */
01433         if (!allNodraw) {
01434             allLevelFlagsForBrush = 0;
01435 
01436             setFlags = qfalse;
01437             /* test if some faces do not have levelflags and remember
01438              * all levelflags which are set. */
01439             for (j = 0; j < brush->numsides; j++) {
01440                 const side_t *side = &brush->original_sides[j];
01441 
01442                 allLevelFlagsForBrush |= (side->contentFlags & CONTENTS_LEVEL_ALL);
01443 
01444                 if (!(side->contentFlags & (CONTENTS_ORIGIN | MASK_CLIP))) {
01445                     /* check level 1 - level 8 */
01446                     if (!(side->contentFlags & CONTENTS_LEVEL_ALL)) {
01447                         setFlags = qtrue;
01448                         break;
01449                     }
01450                 }
01451             }
01452 
01453             /* set the same flags for each face */
01454             if (setFlags) {
01455                 const int flagsToSet = allLevelFlagsForBrush ? allLevelFlagsForBrush : CONTENTS_LEVEL_ALL;
01456                 Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum, "at least one face has no levelflags, setting %i on all faces\n", flagsToSet);
01457                 for (j = 0; j < brush->numsides; j++) {
01458                     side_t *side = &brush->original_sides[j];
01459                     side->contentFlags |= flagsToSet;
01460                 }
01461             }
01462         }
01463     }
01464 }
01465 
01474 void SetImpliedFlags (side_t *side, brush_texture_t *tex, const mapbrush_t *brush)
01475 {
01476     const char *texname = tex->name;
01477     const int initSurf = tex->surfaceFlags;
01478     const int initCont = side->contentFlags;
01479     const char *flagsDescription = NULL;
01480 
01481     /* see discussion at Check_SetNodraw */
01482     if (!config.fixMap && !config.performMapCheck) {
01483         if (!strcmp(texname, "tex_common/actorclip")) {
01484             side->contentFlags |= CONTENTS_ACTORCLIP;
01485             flagsDescription = "CONTENTS_ACTORCLIP";
01486         } else if (!strcmp(texname, "tex_common/caulk")) {
01487             side->surfaceFlags |= SURF_NODRAW;
01488             tex->surfaceFlags |= SURF_NODRAW;
01489             flagsDescription = "SURF_NODRAW";
01490         } else if (!strcmp(texname, "tex_common/hint")) {
01491             side->surfaceFlags |= SURF_HINT;
01492             tex->surfaceFlags |= SURF_HINT;
01493             flagsDescription = "SURF_HINT";
01494         } else if (!strcmp(texname, "tex_common/ladder")) {
01495             side->contentFlags |= CONTENTS_LADDER;
01496             side->surfaceFlags |= SURF_NODRAW;
01497             tex->surfaceFlags |= SURF_NODRAW;
01498             flagsDescription = "CONTENTS_LADDER";
01499         } else if (!strcmp(texname, "tex_common/lightclip")) {
01500             side->contentFlags |= CONTENTS_LIGHTCLIP;
01501             flagsDescription = "CONTENTS_LIGHTCLIP";
01502         } else if (!strcmp(texname, "tex_common/nodraw")) {
01503             /*side->contentFlags |= CONTENTS_SOLID;*/
01504             side->surfaceFlags |= SURF_NODRAW;
01505             tex->surfaceFlags |= SURF_NODRAW;
01506             flagsDescription = "SURF_NODRAW";
01507         } else if (!strcmp(texname, "tex_common/trigger")) {
01508             side->surfaceFlags |= SURF_NODRAW;
01509             tex->surfaceFlags |= SURF_NODRAW;
01510             flagsDescription = "SURF_NODRAW";
01511         } else if (!strcmp(texname, "tex_common/origin")) {
01512             side->contentFlags |= CONTENTS_ORIGIN;
01513             flagsDescription = "CONTENTS_ORIGIN";
01514         } else if (!strcmp(texname, "tex_common/slick")) {
01515             side->contentFlags |= SURF_SLICK;
01516             flagsDescription = "SURF_SLICK";
01517         } else if (!strcmp(texname, "tex_common/weaponclip")) {
01518             side->contentFlags |= CONTENTS_WEAPONCLIP;
01519             flagsDescription = "CONTENTS_WEAPONCLIP";
01520         }
01521 
01522         if (strstr(texname, "water")) {
01523 #if 0
01524             side->surfaceFlags |= SURF_WARP;
01525             tex->surfaceFlags |= SURF_WARP;
01526 #endif
01527             side->contentFlags |= CONTENTS_WATER;
01528             side->contentFlags |= CONTENTS_PASSABLE;
01529             flagsDescription = "CONTENTS_WATER and CONTENTS_PASSABLE";
01530         }
01531 
01532         /* If in check/fix mode and we have made a change, give output. */
01533         if ((side->contentFlags != initCont) || (tex->surfaceFlags != initSurf)) {
01534             Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum,
01535                     "%s implied by %s texture has been set\n", flagsDescription ? flagsDescription : "-", texname);
01536         }
01537     }
01538 
01539     /* additional test, which does not directly depend on tex. */
01540     if (Check_SurfProp(SURF_NODRAW, side) && (tex->surfaceFlags & SURF_PHONG)) {
01541         /* nodraw never has phong set */
01542         side->surfaceFlags &= ~SURF_PHONG;
01543         tex->surfaceFlags &= ~SURF_PHONG;
01544         Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum,
01545             "SURF_PHONG unset, as it has SURF_NODRAW set\n");
01546     }
01547 
01548     if (side->surfaceFlags & SURF_SKIP) {
01549         side->surfaceFlags &= ~SURF_SKIP;
01550         Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum,
01551             "removing legacy flag, SURF_SKIP\n");
01552     }
01553 }
01554 
01558 void CheckFlagsBasedOnTextures (void)
01559 {
01560     int i, j;
01561 
01562     for (i = 0; i < nummapbrushes; i++) {
01563         mapbrush_t *brush = &mapbrushes[i];
01564 
01565         for (j = 0; j < brush->numsides; j++) {
01566             side_t *side = &brush->original_sides[j];
01567             const ptrdiff_t index = side - brushsides;
01568             brush_texture_t *tex = &side_brushtextures[index];
01569 
01570             assert(side);
01571             assert(tex);
01572 
01573             /* set surface and content flags based on texture. */
01574             SetImpliedFlags(side, tex, brush);
01575         }
01576     }
01577 }
01578 
01583 void CheckTexturesBasedOnFlags (void)
01584 {
01585     int i, j;
01586 
01587     for (i = 0; i < nummapbrushes; i++) {
01588         mapbrush_t *brush = &mapbrushes[i];
01589 
01590         for (j = 0; j < brush->numsides; j++) {
01591             side_t *side = &brush->original_sides[j];
01592             const ptrdiff_t index = side - brushsides;
01593             brush_texture_t *tex = &side_brushtextures[index];
01594 
01595             assert(side);
01596             assert(tex);
01597 
01598             /* set textures based on flags */
01599             if (tex->name[0] == '\0') {
01600                 Check_Printf(VERB_CHECK, qfalse, brush->entitynum, brush->brushnum, " no texture assigned\n");
01601             }
01602 
01603             if (!strcmp(tex->name, "tex_common/error")) {
01604                 Check_Printf(VERB_CHECK, qfalse, brush->entitynum, brush->brushnum, "error texture assigned - check this brush\n");
01605             }
01606 
01607             if (!strcmp(tex->name, "NULL")) {
01608                 Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum, "replaced NULL with nodraw texture\n");
01609                 Q_strncpyz(tex->name, "tex_common/nodraw", sizeof(tex->name));
01610                 tex->surfaceFlags |= SURF_NODRAW;
01611             }
01612             if (tex->surfaceFlags & SURF_NODRAW && strcmp(tex->name, "tex_common/nodraw")) {
01613                 Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum, "set nodraw texture for SURF_NODRAW\n");
01614                 tex->surfaceFlags &= ~SURF_PHONG;
01615                 side->surfaceFlags &= ~SURF_PHONG;
01616                 Q_strncpyz(tex->name, "tex_common/nodraw", sizeof(tex->name));
01617             }
01618             if (tex->surfaceFlags & SURF_HINT && strcmp(tex->name, "tex_common/hint")) {
01619                 Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum,  "set hint texture for SURF_HINT\n");
01620                 Q_strncpyz(tex->name, "tex_common/hint", sizeof(tex->name));
01621             }
01622 
01623             if (side->contentFlags & CONTENTS_WEAPONCLIP && strcmp(tex->name, "tex_common/weaponclip")) {
01624                 Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum,  "set weaponclip texture for CONTENTS_WEAPONCLIP\n");
01625                 Q_strncpyz(tex->name, "tex_common/weaponclip", sizeof(tex->name));
01626             }
01627             if (side->contentFlags & CONTENTS_ACTORCLIP && strcmp(tex->name, "tex_common/actorclip")) {
01628                 Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum,  "set actorclip texture for CONTENTS_ACTORCLIP\n");
01629                 Q_strncpyz(tex->name, "tex_common/actorclip", sizeof(tex->name));
01630             }
01631             if (side->contentFlags & CONTENTS_LIGHTCLIP && strcmp(tex->name, "tex_common/lightclip")) {
01632                 Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum,  "set lightclip texture for CONTENTS_LIGHTCLIP\n");
01633                 Q_strncpyz(tex->name, "tex_common/lightclip", sizeof(tex->name));
01634             }
01635             if (side->contentFlags & CONTENTS_ORIGIN && strcmp(tex->name, "tex_common/origin")) {
01636                 Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum, "set origin texture for CONTENTS_ORIGIN\n");
01637                 Q_strncpyz(tex->name, "tex_common/origin", sizeof(tex->name));
01638             }
01639         }
01640     }
01641 }
01642 
01649 void CheckPropagateParserContentFlags(mapbrush_t *b)
01650 {
01651     int notInformedMixedFace = 1;
01652     int m, contentFlagDiff;
01653     int transferFlags = (CONTENTS_DETAIL | CONTENTS_TRANSLUCENT);
01654 
01655     for (m = 0; m < b->numsides; m++) {
01656         contentFlagDiff = (b->original_sides[m].contentFlags ^ b->contentFlags) & transferFlags;
01657         if (contentFlagDiff) {
01658             /* only tell them once per brush */
01659             if (notInformedMixedFace) {
01660                 Check_Printf(VERB_CHECK, qtrue, b->entitynum , b->brushnum, "transferring contentflags to all faces:");
01661                 DisplayContentFlags(contentFlagDiff);
01662                 Check_Printf(VERB_CHECK, qtrue, b->entitynum , b->brushnum, "\n");
01663                 notInformedMixedFace = 0;
01664             }
01665             b->original_sides[m].contentFlags |= b->contentFlags ;
01666         }
01667     }
01668 }
01669 
01678 void CheckMixedFaceContents (void)
01679 {
01680     int i, j;
01681     int nfActorclip; /* number of faces with actorclip contentflag set */
01682 
01683     for (i = 0; i < nummapbrushes; i++) {
01684         mapbrush_t *brush = &mapbrushes[i];
01685         side_t *side0;
01686 
01687         /* if the origin flag is set in the mapbrush_t struct, then the brushes
01688          * work is done, and we can skip the mixed face contents check for this brush */
01689         if (brush->contentFlags & CONTENTS_ORIGIN)
01690             continue;
01691 
01692         side0 = &brush->original_sides[0];
01693         nfActorclip = 0;
01694 
01695         CheckPropagateParserContentFlags(brush);
01696 
01697         for (j = 0; j < brush->numsides; j++) {
01698             side_t *side = &brush->original_sides[j];
01699             assert(side);
01700 
01701             nfActorclip += (side->contentFlags & CONTENTS_ACTORCLIP) ? 1 : 0;
01702 
01703             if (side0->contentFlags != side->contentFlags) {
01704                 const int jNotZero = side->contentFlags & ~side0->contentFlags;
01705                 const int zeroNotJ = side0->contentFlags & ~side->contentFlags;
01706                 Check_Printf(VERB_CHECK, qfalse, brush->entitynum, brush->brushnum, "mixed face contents (");
01707                 if (jNotZero) {
01708                     Check_Printf(VERB_CHECK, qfalse, NUM_SAME, NUM_SAME, "face %i has and face 0 has not", j);
01709                     DisplayContentFlags(jNotZero);
01710                     if (zeroNotJ)
01711                         Check_Printf(VERB_CHECK, qfalse, NUM_SAME, NUM_SAME, ", ");
01712                 }
01713                 if (zeroNotJ) {
01714                     Check_Printf(VERB_CHECK, qfalse, NUM_SAME, NUM_SAME, "face 0 has and face %i has not", j);
01715                     DisplayContentFlags(zeroNotJ);
01716                 }
01717                 Check_Printf(VERB_CHECK, qfalse, NUM_SAME, NUM_SAME, ")\n");
01718             }
01719         }
01720 
01721         if (nfActorclip && nfActorclip != brush->numsides) {
01722             Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum, "ACTORCLIP is not set on all of the faces: removing.\n");
01723             for (j = 0; j < brush->numsides; j++) {
01724                 side_t *side = &brush->original_sides[j];
01725                 const ptrdiff_t index = side - brushsides;
01726                 brush_texture_t *tex = &side_brushtextures[index];
01727 
01728                 if (side->contentFlags & CONTENTS_ACTORCLIP && !strcmp(tex->name, "tex_common/actorclip")) {
01729                     Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum, "removing tex_common/actorclip, setting tex_common/error\n");
01730                     Q_strncpyz(tex->name, "tex_common/error", sizeof(tex->name));
01731                 }
01732 
01733                 side->contentFlags &= ~CONTENTS_ACTORCLIP;
01734             }
01735         }
01736     }
01737 }
01738 
01739 void CheckBrushes (void)
01740 {
01741     int i, j;
01742 
01743     for (i = 0; i < nummapbrushes; i++) {
01744         mapbrush_t *brush = &mapbrushes[i];
01745 
01746         Check_DuplicateBrushPlanes(brush);
01747 
01748         for (j = 0; j < brush->numsides; j++) {
01749             side_t *side = &brush->original_sides[j];
01750 
01751             assert(side);
01752 
01753             if (side->contentFlags & CONTENTS_ORIGIN && brush->entitynum == 0) {
01754                 Check_Printf(VERB_CHECK, qtrue, brush->entitynum, brush->brushnum, "origin brush inside worldspawn - removed CONTENTS_ORIGIN\n");
01755                 side->contentFlags &= ~CONTENTS_ORIGIN;
01756             }
01757         }
01758     }
01759 }

Generated by  doxygen 1.6.2