00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
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
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;
00113
00114
00115
00116
00117
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;
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;
00154
00155
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
00162
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;
00172
00173 if (mode == PIB_INCL_SURF_EXCL_EDGE && numPlanes > 1)
00174 return qfalse;
00175
00176
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;
00260
00261
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
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
00298 static qboolean done = qfalse;
00299 mapbrush_t *bbuf[MAX_MAP_BRUSHES];
00300 int i, j, numNear;
00301
00302 if (done)
00303 return;
00304
00305
00306 for (i = 0; i < nummapbrushes; i++) {
00307 mapbrush_t *iBrush = &mapbrushes[i];
00308
00309
00310 for (j = 0, numNear = 0 ; j < nummapbrushes; j++) {
00311 mapbrush_t *jBrush = &mapbrushes[j];
00312
00313 if (i == j)
00314 continue;
00315
00316 if (!Check_BoundingBoxIntersects(iBrush, jBrush))
00317 continue;
00318
00319
00320 assert(numNear < nummapbrushes);
00321 bbuf[numNear++] = jBrush;
00322 }
00323
00324 iBrush->numNear = numNear;
00325 if (!numNear)
00326 continue;
00327
00328
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;
00394 const mapbrush_t *b = s[i ^ 1]->brush;
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
00426
00427
00428 side_t *sbuf[MAX_MAP_SIDES / 4];
00429
00430 mapbrush_t *bDone[MAX_MAP_SIDES];
00431 mapbrush_t *bTodo[MAX_MAP_SIDES];
00432
00433
00434 if (done)
00435 return;
00436
00437 Check_NearList();
00438
00439
00440 for (i = 0; i < nummapbrushes; i++) {
00441 mapbrush_t *iBrush = &mapbrushes[i];
00442
00443 if (!Check_IsOptimisable(iBrush))
00444 continue;
00445
00446
00447 for (is = 0; is < iBrush->numsides; is++) {
00448 side_t *iSide = &iBrush->original_sides[is];
00449
00450
00451
00452 if (iSide->isCompositeMember || Check_SurfProp(SURF_NODRAW, iSide) || !iSide->winding)
00453 continue;
00454
00455
00456
00457 sbuf[0] = iSide;
00458 numMembers = 1;
00459
00460
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
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;
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;
00481
00482 if (ParallelAndCoincidentTo(iSide, sChecking)) {
00483
00484
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;
00489 sChecking->isCompositeMember = qtrue;
00490
00491
00492 for (l = 0; l < newMembersBrush->numNear;l++) {
00493 mapbrush_t *nearListBrush = newMembersBrush->nearBrushes[l];
00494
00495 if (!Check_IsOptimisable(nearListBrush))
00496 continue;
00497
00498
00499
00500
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 ;
00509 }
00510 goto next_brush_todo;
00511 }
00512 }
00513 }
00514 }
00515 next_brush_todo:
00516 ;
00517 }
00518
00519 if (numMembers > 1) {
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
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;
00554 vec3_t lineToPlane;
00555 float sin;
00556 float param;
00557 float length;
00558
00559 VectorSubtract(vert2, vert1, direction);
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);
00569 VectorAdd(vert1, direction, intersection);
00570 param = param * length;
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
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
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;
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;
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;
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;
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;
00686
00687 return qtrue;
00688 }
00689
00690 #if 0
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
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 {
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];
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
00766
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
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++;
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;
00797
00798 {
00799
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
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
00830 i = 0;
00831 do {
00832 i++;
00833 if ((i + 1) >= numVert)
00834 return qfalse;
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;
00843 }
00844 }
00845 }
00846
00847 return qfalse;
00848 #endif
00849 }
00850
00854 void CheckZFighting (void)
00855 {
00856 int i, j, is, js;
00857
00858
00859 Check_NearList();
00860
00861
00862 for (i = 0; i < nummapbrushes; i++) {
00863 const mapbrush_t *iBrush = &mapbrushes[i];
00864
00865 if (!Check_IsOptimisable(iBrush))
00866 continue;
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;
00873
00874 if (!Check_IsOptimisable(jBrush))
00875 continue;
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;
00882
00883 if (Check_SidePointsDown(iSide))
00884 continue;
00885
00886
00887 for (js = 0; js < jBrush->numsides; js++) {
00888 const side_t *jSide = &jBrush->original_sides[js];
00889
00890
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
00930 Check_NearList();
00931
00932 for (i = 0; i < nummapbrushes; i++) {
00933 mapbrush_t *iBrush = &mapbrushes[i];
00934
00935
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;
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
00978
00979
00980
00981
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
01005
01006 Check_FindCompositeSides();
01007
01008
01009 for (i = 0; i < nummapbrushes; i++) {
01010 mapbrush_t *iBrush = &mapbrushes[i];
01011 iBrushNumSet = 0;
01012
01013
01014 if (!Check_IsOptimisable(iBrush))
01015 continue;
01016
01017
01018 for (is = 0; is < iBrush->numsides; is++) {
01019 side_t *iSide = &iBrush->original_sides[is];
01020
01021
01022 if (Check_SurfProp(SURF_NODRAW, iSide))
01023 continue;
01024
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 }
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
01042 for (i = 0; i < nummapbrushes; i++) {
01043 mapbrush_t *iBrush = &mapbrushes[i];
01044 iBrushNumSet = 0;
01045
01046
01047 if (!Check_IsOptimisable(iBrush))
01048 continue;
01049
01050
01051 for (j = 0; j < iBrush->numNear; j++) {
01052 mapbrush_t *jBrush = iBrush->nearBrushes[j];
01053
01054
01055 if (!Check_IsOptimisable(jBrush))
01056 continue;
01057
01058
01059 for (is = 0; is < iBrush->numsides; is++) {
01060 side_t *iSide = &iBrush->original_sides[is];
01061
01062 if (!iSide->winding)
01063 continue;
01064
01065
01066 if (Check_SurfProp(SURF_NODRAW, iSide))
01067 continue;
01068
01069 else if (iSide->surfaceFlags & SURF_LIGHT)
01070 continue;
01071
01072
01073 for (js = 0; js < jBrush->numsides; js++) {
01074 const side_t *jSide = &jBrush->original_sides[js];
01075
01076 if (!jSide->winding)
01077 continue;
01078
01079 #if 0
01080
01081
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 }
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;
01107
01108
01109 for (j = 0; j < numCompositeSides; j++) {
01110 const compositeSide_t *composite = &compositeSides[j];
01111 assert(composite);
01112 assert(composite->memberSides[0]);
01113
01114
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};
01119
01120 if (!FacingAndCoincidentTo(iSide, composite->memberSides[0]))
01121 continue;
01122
01123
01124
01125 if (Check_SurfProp(SURF_NODRAW, iSide))
01126 continue;
01127
01128 iWinding = iSide->winding;
01129
01130 if (!iWinding)
01131 continue;
01132
01133
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
01140
01141
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
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
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
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
01194
01195
01196
01197 if ((lastIntersectionMembInd == l)
01198 && (VectorDistSqr(intersection, lastIntersection) > CH_DIST_EPSILON_SQR)
01199 && !Check_LevelForNodraws(composite->memberSides[l], iSide))
01200 goto next_iSide;
01201
01202 lastIntersectionMembInd = l;
01203 VectorCopy(intersection, lastIntersection);
01204 }
01205 }
01206 }
01207
01208
01209
01210 for (l = 0; l < numIntsct; l++) {
01211 if (!paired[l])
01212 goto next_iSide;
01213 }
01214
01215 }
01216
01217
01218 Check_SetNodraw(iSide);
01219 iBrushNumSet++;
01220 numSetFromCompositeSide++;
01221
01222 next_iSide:
01223 ;
01224 }
01225 }
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 }
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
01250 if (sides[i].planenum == -1) {
01251 Check_Printf(VERB_CHECK, qfalse, b->entitynum, b->brushnum, "degenerate plane\n");
01252 continue;
01253 }
01254
01255
01256 for (j = 0; j < i; j++) {
01257 if (sides[i].planenum == sides[j].planenum) {
01258
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
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
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
01396
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
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
01433 if (!allNodraw) {
01434 allLevelFlagsForBrush = 0;
01435
01436 setFlags = qfalse;
01437
01438
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
01446 if (!(side->contentFlags & CONTENTS_LEVEL_ALL)) {
01447 setFlags = qtrue;
01448 break;
01449 }
01450 }
01451 }
01452
01453
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
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
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
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
01540 if (Check_SurfProp(SURF_NODRAW, side) && (tex->surfaceFlags & SURF_PHONG)) {
01541
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
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
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
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;
01682
01683 for (i = 0; i < nummapbrushes; i++) {
01684 mapbrush_t *brush = &mapbrushes[i];
01685 side_t *side0;
01686
01687
01688
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 }