UFO: Alien Invasion
Loading...
Searching...
No Matches
check.cpp
Go to the documentation of this file.
1
5
6/*
7All original material Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9Copyright (C) 1997-2001 Id Software, Inc.
10
11This program is free software; you can redistribute it and/or
12modify it under the terms of the GNU General Public License
13as published by the Free Software Foundation; either version 2
14of the License, or (at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
20See the GNU General Public License for more details.
21
22You should have received a copy of the GNU General Public License
23along with this program; if not, write to the Free Software
24Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26*/
27
28#include "check.h"
29#include "../common/shared.h"
30#include "../common/bspfile.h"
31#include "../common/scriplib.h"
33#include "checklib.h"
34#include "../bsp.h"
35#include "../ufo2map.h"
36
39
42#define CH_DIST_EPSILON 0.001f
43#define CH_DIST_EPSILON_SQR 0.000001
44
45#define CH_DIST_EPSILON_COLLINEAR_POINTS 0.02f
46
48#define COS_EPSILON 0.9999f
49
51#define SIN_EPSILON 0.0001f
52
64
67#define NEARDOWN_COS 0.985
68
73static bool Check_SidePointsDown (const side_t* s)
74{
75 const vec3_t down = {0.0f, 0.0f, -1.0f};
76 const plane_t* plane = &mapplanes[s->planenum];
77 const float dihedralCos = DotProduct(plane->normal, down);
78 return dihedralCos >= NEARDOWN_COS;
79}
80
86static inline float Check_PointPlaneDistance (const vec3_t point, const plane_t* plane)
87{
88 /* normal should have a magnitude of one */
89 assert(fabs(VectorLengthSqr(plane->normal) - 1.0f) < CH_DIST_EPSILON);
90
91 return DotProduct(point, plane->normal) - plane->dist;
92}
93
106static bool FacingAndCoincidentTo (const side_t* side1, const side_t* side2)
107{
108 const plane_t* plane1 = &mapplanes[side1->planenum];
109 const plane_t* plane2 = &mapplanes[side2->planenum];
110 float distance;
111
112 const float dihedralCos = DotProduct(plane1->normal, plane2->normal);
113 if (dihedralCos >= -COS_EPSILON)
114 return false; /* not facing each other */
115
116 /* calculate the distance of point from plane2. as we have established that the
117 * plane's normals are antiparallel, and plane1->planeVector[0] is a point on plane1
118 * (that was supplied in the map file), this is the distance
119 * between the planes */
120 distance = Check_PointPlaneDistance(plane1->planeVector[0], plane2);
121
122 return fabs(distance) < CH_DIST_EPSILON;
123}
124
131static bool ParallelAndCoincidentTo (const side_t* side1, const side_t* side2)
132{
133 float distance;
134 const plane_t* plane1 = &mapplanes[side1->planenum];
135 const plane_t* plane2 = &mapplanes[side2->planenum];
136 const float dihedralCos = DotProduct(plane1->normal, plane2->normal);
137 if (dihedralCos <= COS_EPSILON)
138 return false; /* not parallel */
139
140 distance = Check_PointPlaneDistance(plane1->planeVector[0], plane2);
141
142 return fabs(distance) < CH_DIST_EPSILON;
143}
144
152static inline bool Check_IsPointInsideBrush (const vec3_t point, const mapbrush_t* brush, const pointInBrush_t mode)
153{
154 int numPlanes = 0; /* how many of the sides the point is on. on 2 sides, means on an edge. on 3 a vertex */
155 /* PIB_INCL_SURF is the default */
156 /* apply epsilon the other way if the surface is excluded */
157 const float epsilon = CH_DIST_EPSILON * (mode == PIB_EXCL_SURF ? -1.0f : 1.0f);
158
159 for (int i = 0; i < brush->numsides; i++) {
160 const plane_t* plane = &mapplanes[brush->original_sides[i].planenum];
161
162 /* if the point is on the wrong side of any face, then it is outside */
163 /* distance to one of the planes of the sides, negative implies the point is inside this plane */
164 const float dist = Check_PointPlaneDistance(point, plane);
165 if (dist > epsilon)
166 return false;
167
168 numPlanes += fabs(dist) < CH_DIST_EPSILON ? 1 : 0;
169 }
170
171 if (mode == PIB_ON_SURFACE_ONLY && numPlanes == 0)
172 return false; /* must be on at least one surface */
173
174 if (mode == PIB_INCL_SURF_EXCL_EDGE && numPlanes > 1)
175 return false; /* must not be on more than one side, that would be an edge */
176
177 /* inside all planes, therefore inside the brush */
178 return true;
179}
180
190static bool Check_SurfProp (const int flag, const side_t* s)
191{
192 const ptrdiff_t indexSide = s - brushsides;
193 const brush_texture_t* tex = &side_brushtextures[indexSide];
194 switch (flag) {
195 case SURF_NODRAW:
196 return Q_streq(tex->name, "tex_common/nodraw");
197 case CONTENTS_LADDER:
198 return Q_streq(tex->name, "tex_common/ladder");
200 return Q_streq(tex->name, "tex_common/weaponclip");
202 return Q_streq(tex->name, "tex_common/actorclip");
204 return Q_streq(tex->name, "tex_common/lightclip");
205 case CONTENTS_ORIGIN:
206 return Q_streq(tex->name, "tex_common/origin");
207 default:
208 return false;
209 }
210}
211
222static bool Check_SurfProps (const int flags, const side_t* s)
223{
224 const ptrdiff_t indexSide = s - brushsides;
225 const brush_texture_t* tex = &side_brushtextures[indexSide];
226 const char* texname = tex->name;
227 assert(flags & (SURF_NODRAW | MASK_CLIP));
228
229 if ((flags & SURF_NODRAW) && Q_streq(texname, "tex_common/nodraw"))
230 return true;
231
232 if ((flags & CONTENTS_WEAPONCLIP) && Q_streq(texname, "tex_common/weaponclip"))
233 return true;
234
235 if ((flags & CONTENTS_ACTORCLIP) && Q_streq(texname, "tex_common/actorclip"))
236 return true;
237
238 if ((flags & CONTENTS_LIGHTCLIP) && Q_streq(texname, "tex_common/lightclip"))
239 return true;
240
241 if ((flags & CONTENTS_LADDER) && Q_streq(texname, "tex_common/ladder"))
242 return true;
243
244 if ((flags & CONTENTS_ORIGIN) && Q_streq(texname, "tex_common/origin"))
245 return true;
246
247 return false;
248}
249
253static bool Check_IsOptimisable (const mapbrush_t* b)
254{
255 const entity_t* e = &entities[b->entitynum];
256 const char* name = ValueForKey(e, "classname");
257 int numNodraws = 0;
258
259 if (!Q_streq(name, "func_group") && !Q_streq(name, "worldspawn"))
260 return false;/* other entities, eg func_breakable are no use */
261
262 /* content flags should be the same on all faces, but we shall be suspicious */
263 for (int i = 0; i < b->numsides; i++) {
264 const side_t* side = &b->original_sides[i];
266 return false;
268 return false;
269 numNodraws += Check_SurfProp(SURF_NODRAW, side) ? 1 : 0;
270 }
271
272 /* all nodraw brushes are special too */
273 return numNodraws == b->numsides ? false : true;
274}
275
279static bool Check_BoundingBoxIntersects (const mapbrush_t* a, const mapbrush_t* b)
280{
281 for (int i = 0; i < 3; i++)
282 if (a->mbBox.mins[i] - CH_DIST_EPSILON >= b->mbBox.maxs[i] || a->mbBox.maxs[i] <= b->mbBox.mins[i] - CH_DIST_EPSILON)
283 return false;
284
285 return true;
286}
287
294static void Check_NearList (void)
295{
296 /* this function may be called more than once, but we only want this done once */
297 static bool done = false;
298 mapbrush_t* bbuf[MAX_MAP_BRUSHES];/*< store pointers to brushes here and then malloc them when we know how many */
299
300 if (done)
301 return;
302
303 /* make a list for iBrush*/
304 for (int i = 0; i < nummapbrushes; i++) {
305 mapbrush_t* iBrush = &mapbrushes[i];
306 int j, numNear;
307
308 /* test all brushes for nearness to iBrush */
309 for (j = 0, numNear = 0 ; j < nummapbrushes; j++) {
310 mapbrush_t* jBrush = &mapbrushes[j];
311
312 if (i == j) /* do not list a brush as being near itself - not useful!*/
313 continue;
314
315 if (!Check_BoundingBoxIntersects(iBrush, jBrush))
316 continue;
317
318 /* near, therefore add to temp list for iBrush */
319 assert(numNear < nummapbrushes);
320 bbuf[numNear++] = jBrush;
321 }
322
323 iBrush->numNear = numNear;
324 if (!numNear)
325 continue;
326
327 /* now we know how many, we can malloc. then copy the pointers */
328 iBrush->nearBrushes = Mem_AllocTypeN(mapbrush_t* , numNear);
329
330 if (!iBrush->nearBrushes)
331 Sys_Error("Check_Nearlist: out of memory");
332
333 for (j = 0; j < numNear; j++)
334 iBrush->nearBrushes[j] = bbuf[j];
335 }
336
337 done = true;
338}
339
348static bool Check_SideIsInBrush (const side_t* side, const mapbrush_t* brush, pointInBrush_t mode)
349{
350 const winding_t* w = side->winding;
351
352 assert(w->numpoints > 0);
353
354 for (int i = 0; i < w->numpoints ; i++)
355 if (!Check_IsPointInsideBrush(w->p[i], brush, mode))
356 return false;
357
358 return true;
359}
360
361#if 0
366static void Check_SetError (side_t* s)
367{
368 const ptrdiff_t index = s - brushsides;
370
371 Q_strncpyz(tex->name, "tex_common/error", sizeof(tex->name));
372}
373#endif
374
382static bool Check_SidesTouch (side_t* a, side_t* b)
383{
384 side_t* s[2];
385
386 s[0] = a;
387 s[1] = b;
388
389 for (int i = 0; i < 2; i++) {
390 const winding_t* w = s[i]->winding; /* winding from one of the sides */
391 const mapbrush_t* brush = s[i ^ 1]->brush; /* the brush that the other side belongs to */
392
393 for (int j = 0; j < w->numpoints ; j++) {
394 if (Check_IsPointInsideBrush(w->p[j], brush, PIB_INCL_SURF))
395 return true;
396 }
397 }
398 return false;
399}
400
406static void Check_FindCompositeSides (void)
407{
408 static bool done = false;
409 int i, j, k, l, numMembers, numDone = 0, numTodo;
410
411 /* store pointers to sides here and then malloc them when we know how many.
412 * divide by 4 becuase, the minimum number of sides for a brush is 4, so if
413 * all brushes were lined up, and had one side as a member, that would be their number */
414 side_t* sbuf[MAX_MAP_SIDES / 4];
415
416 mapbrush_t* bDone[MAX_MAP_SIDES]; /*< an array of brushes to check if the composite propagates across */
417 mapbrush_t* bTodo[MAX_MAP_SIDES]; /*< an array of brushes that have been checked, without this it would never stop */
418
419 /* this function may be called more than once, but we only want this done once */
420 if (done)
421 return;
422
424
425 /* check each brush, iBrush */
426 for (i = 0; i < nummapbrushes; i++) {
427 mapbrush_t* iBrush = &mapbrushes[i];
428
429 if (!Check_IsOptimisable(iBrush))
430 continue; /* skip clips etc */
431
432 /* check each side, iSide, of iBrush for being the seed of a composite face */
433 for (int is = 0; is < iBrush->numsides; is++) {
434 side_t* iSide = &iBrush->original_sides[is];
435
436 /* do not find the same composite again. no nodraws
437 * AddBrushBevels creates sides without windings. skip these too */
438 if (iSide->isCompositeMember || Check_SurfProp(SURF_NODRAW, iSide) || !iSide->winding)
439 continue;
440
441 /* start making the list of brushes in the composite,
442 * we will only keep it if the composite has more than member */
443 sbuf[0] = iSide; /* set iSide->isCompositeMember = true later, if we keep the composite */
444 numMembers = 1;
445
446 /* add neighbouring brushes to the list to check for composite propagation */
447 numTodo = 0;
448 for (j = 0; j < iBrush->numNear; j++)
449 if (Check_IsOptimisable(iBrush->nearBrushes[j])) {
450 bTodo[numTodo++] = iBrush->nearBrushes[j];
451 }
452
453 /* this brush's nearlist is listed for checking, so it is done */
454 bDone[numDone++] = iBrush;
455
456 while (numTodo > 0) {
457 mapbrush_t* bChecking = bTodo[--numTodo];
458 if (bChecking == nullptr)
459 continue;
460 bDone[numDone++] = bChecking; /* remember so it is not added to the todo list again */
461
462 for (j = 0; j < bChecking->numsides; j++) {
463 side_t* sChecking = &bChecking->original_sides[j];
464
465 if (Check_SurfProp(SURF_NODRAW, sChecking) || !sChecking->winding)
466 continue; /* no nodraws in composites. see comment above above regarding winding*/
467
468 if (ParallelAndCoincidentTo(iSide, sChecking)) {
469
470 /* test if sChecking intersects or touches any of sides that are already in the composite*/
471 for (k = 0; k < numMembers; k++) {
472 if (Check_SidesTouch(sChecking, sbuf[k])) {
473 const mapbrush_t* newMembersBrush = sChecking->brush;
474 sbuf[numMembers++] = sChecking; /* add to the array of members */
475 sChecking->isCompositeMember = true;
476
477 /* add this brush's nearList to the todo list, as the compostite may propagate through it */
478 for (l = 0; l < newMembersBrush->numNear;l++) {
479 mapbrush_t* nearListBrush = newMembersBrush->nearBrushes[l];
480
481 if (!Check_IsOptimisable(nearListBrush))
482 continue; /* do not propogate across clips etc */
483
484 /* only add them to the todo list if they are not on the done list
485 * as a brush cannot have parallel sides, this also ensures the same side
486 * is not added to a composite more than once */
487 for (int m = 0; m < numDone; m++) {
488 if (nearListBrush == bDone[m])
489 goto skip_add_brush_to_todo_list;
490 }
491 bTodo[numTodo++] = nearListBrush;
492
493 skip_add_brush_to_todo_list:
494 ; /* there must be a statement after the label, ";" will do */
495 }
496 goto next_brush_todo; /* need not test any more sides of this brush, if a member is found */
497 }
498 }
499 }
500 }
501 next_brush_todo:
502 ;
503 }
504
505 if (numMembers > 1) { /* composite found */
506 side_t** sidesInNewComposite = Mem_AllocTypeN(side_t* , numMembers);
507
508 if (!sidesInNewComposite)
509 Sys_Error("Check_FindCompositeSides: out of memory");
510
511 /* this was not done before for the first side, as we did not know it would have at least 2 members */
512 iSide->isCompositeMember = true;
513
514 compositeSides[numCompositeSides].numMembers = numMembers;
515 compositeSides[numCompositeSides].memberSides = sidesInNewComposite;
516
517 for (j = 0; j < numMembers; j++) {
518 compositeSides[numCompositeSides].memberSides[j] = sbuf[j];
519 }
521 }
522 }
523 }
524
525 Check_Printf(VERB_EXTRA, false, -1, -1, "%i composite sides found\n", numCompositeSides);
526
527 done = true;
528}
529
539static int Check_EdgePlaneIntersection (const vec3_t vert1, const vec3_t vert2, const plane_t* plane, vec3_t intersection)
540{
541 vec3_t direction; /* a vector in the direction of the line */
542 vec3_t lineToPlane; /* a line from vert1 on the line to a point on the plane */
543 float sinVal; /* sine of angle to plane, cosine of angle to normal */
544 float param; /* param in line equation line = vert1 + param * (vert2 - vert1) */
545 float length; /* length of the edge */
546
547 VectorSubtract(vert2, vert1, direction);/*< direction points from vert1 to vert2 */
548 length = VectorLength(direction);
549 if (length < DIST_EPSILON)
550 return false;
551 sinVal = DotProduct(direction, plane->normal) / length;
552 if (fabs(sinVal) < SIN_EPSILON)
553 return false;
554 VectorSubtract(plane->planeVector[0], vert1, lineToPlane);
555 param = DotProduct(plane->normal, lineToPlane) / DotProduct(plane->normal, direction);
556
557 /* direction should point from vert1 to intersection */
558 VectorMul(param, direction, direction);
559 VectorAdd(vert1, direction, intersection);
560 /* param is now the distance along the edge from vert1 */
561 param = param * length;
562 return (param > CH_DIST_EPSILON) && (param < (length - CH_DIST_EPSILON));
563}
564
569static bool Check_WindingIntersects (const winding_t* winding, const mapbrush_t* brush)
570{
571 vec3_t intersection;
572 int vi, bi;
573
574 for (bi = 0; bi < brush->numsides; bi++) {
575 for (vi = 0; vi < winding->numpoints; vi++) {
576 const int val = vi + 1;
577 const int vj = (winding->numpoints == val) ? 0 : val;
578 if (Check_EdgePlaneIntersection(winding->p[vi], winding->p[vj], &mapplanes[brush->original_sides[bi].planenum], intersection))
579 if (Check_IsPointInsideBrush(intersection, brush, PIB_INCL_SURF_EXCL_EDGE))
580 return true;
581 }
582 }
583 return false;
584}
585
590{
591 /* initialise mapbrush_t.nearBrushes */
593
594 for (int i = 0; i < nummapbrushes; i++) {
595 const mapbrush_t* iBrush = &mapbrushes[i];
596
597 if (!Check_IsOptimisable(iBrush))
598 continue;
599
600 for (int j = 0; j < iBrush->numNear; j++) {
601 const mapbrush_t* jBrush = iBrush->nearBrushes[j];
602
603 if (!Check_IsOptimisable(jBrush))
604 continue;
605
606 /* check each side of i for intersection with brush j */
607 for (int is = 0; is < iBrush->numsides; is++) {
608 const winding_t* winding = (iBrush->original_sides[is].winding);
609 if (Check_WindingIntersects(winding, jBrush)) {
610 Check_Printf(VERB_CHECK, false, iBrush->entitynum, iBrush->brushnum, "intersects with brush %i (entity %i)\n", jBrush->brushnum, jBrush->entitynum);
611 break;
612 }
613 }
614 }
615 }
616}
617
627static bool Check_EdgeEdgeIntersection (const vec3_t e1p1, const vec3_t e1p2,
628 const vec3_t e2p1, const vec3_t e2p2, vec3_t intersection)
629{
630 vec3_t dir1, dir2, unitDir1, unitDir2;
631 vec3_t dirClosestApproach, from1To2, e1p1ToIntersection, e2p1ToIntersection;
632 vec3_t cross1, cross2;
633 float cosAngle, length1, length2, dist, magCross2, param1;
634 float e1p1Dist, e2p1Dist;
635
636 VectorSubtract(e1p2, e1p1, dir1);
637 VectorSubtract(e2p2, e2p1, dir2);
638 length1 = VectorLength(dir1);
639 length2 = VectorLength(dir2);
640
641 if (length1 < CH_DIST_EPSILON || length2 < CH_DIST_EPSILON)
642 return false; /* edges with no length cannot intersect */
643
644 VectorScale(dir1, 1.0f / length1, unitDir1);
645 VectorScale(dir2, 1.0f / length2, unitDir2);
646
647 cosAngle = fabs(DotProduct(unitDir1, unitDir2));
648
649 if (cosAngle >= COS_EPSILON)
650 return false; /* parallel lines either do not intersect, or are coincident */
651
652 CrossProduct(unitDir1, unitDir2, dirClosestApproach);
653 VectorNormalize(dirClosestApproach);
654
655 VectorSubtract(e2p1, e1p1, from1To2);
656 dist = fabs(DotProduct(dirClosestApproach, from1To2));
657
658 if (dist > CH_DIST_EPSILON)
659 return false; /* closest approach of skew lines is nonzero: no intersection */
660
661 CrossProduct(from1To2, dir2, cross1);
662 CrossProduct(dir1, dir2, cross2);
663 magCross2 = VectorLength(cross2);
664 param1 = DotProduct(cross1, cross2) / (magCross2 * magCross2);
665 VectorScale(dir1, param1, e1p1ToIntersection);
666 VectorAdd(e1p1, e1p1ToIntersection, intersection);
667 e1p1Dist = DotProduct(e1p1ToIntersection, unitDir1);
668
669 if (e1p1Dist < CH_DIST_EPSILON || e1p1Dist > (length1 - CH_DIST_EPSILON))
670 return false; /* intersection is not between vertices of edge 1 */
671
672 VectorSubtract(intersection, e2p1, e2p1ToIntersection);
673 e2p1Dist = DotProduct(e2p1ToIntersection, unitDir2);
674 if (e2p1Dist < CH_DIST_EPSILON || e2p1Dist > (length2 - CH_DIST_EPSILON))
675 return false; /* intersection is not between vertices of edge 2 */
676
677 return true;
678}
679
680#if 0 /* not used since z-fight test changed to calculate area of overlap instead */
688static bool Check_PointsAreCollinear (const vec3_t a, const vec3_t b, const vec3_t c)
689{
690 vec3_t d1, d2, d3, cross;
691 float d1d, d2d, d3d, offLineDist;
692
693 VectorSubtract(a, b, d1);
694 VectorSubtract(a, c, d2);
695 VectorSubtract(b, c, d3);
696
697 d1d = VectorLength(d1);
698 d2d = VectorLength(d2);
699 d3d = VectorLength(d3);
700
701 /* if 2 points are in the same place, we only have 2 points, which must be in a line */
702 if (d1d < CH_DIST_EPSILON || d2d < CH_DIST_EPSILON || d3d < CH_DIST_EPSILON)
703 return true;
704
705 if (d1d >= d2d && d1d >= d3d) {
706 CrossProduct(d2, d3, cross);
707 offLineDist = VectorLength(cross) / d1d;
708 } else if (d2d >= d1d && d2d >= d3d) {
709 CrossProduct(d1, d3, cross);
710 offLineDist = VectorLength(cross) / d2d;
711 } else { /* d3d must be the largest */
712 CrossProduct(d1, d2, cross);
713 offLineDist = VectorLength(cross) / d3d;
714 }
715
716 return offLineDist < CH_DIST_EPSILON_COLLINEAR_POINTS;
717}
718#endif
719
720static float Check_LongestEdge (const winding_t* w)
721{
722 float longestSqr = 0;
723 for (int i = 0; i < w->numpoints; i++) {
724 const int j = (i + 1) % w->numpoints;
725 const float lengthSqr = VectorDistSqr(w->p[i], w->p[j]);
726 longestSqr = longestSqr > lengthSqr ? longestSqr : lengthSqr;
727 }
728 return sqrt(longestSqr);
729}
730
731#define VERT_BUF_SIZE_DISJOINT_SIDES 21
732#define OVERLAP_AREA_TOL 0.2f
733#define OVERLAP_WIDTH_TOL 0.1f
734
744static float Check_SidesOverlap (const side_t* s1, const side_t* s2)
745{
746 vec3_t vertbuf[VERT_BUF_SIZE_DISJOINT_SIDES];/* vertices of intersection of sides. arbitrary choice of size: more than 4 is unusual */
747 int numVert = 0, i;
748 winding_t* w[2];
749 mapbrush_t* b[2];
750
751 w[0] = s1->winding; w[1] = s2->winding;
752 b[0] = s1->brush; b[1] = s2->brush;
753
754 /* test if points from first winding are in (or on) brush that is parent of second winding
755 * and vice - versa. i ^ 1 toggles */
756 for (i = 0; i < 2; i++) {
757 for (int j = 0; j < w[i]->numpoints ; j++) {
758 if (Check_IsPointInsideBrush(w[i]->p[j], b[i ^ 1], PIB_INCL_SURF)) {
759 if (numVert == VERT_BUF_SIZE_DISJOINT_SIDES) {
760 Check_Printf(VERB_CHECK, false, b[i]->entitynum, b[i]->brushnum, "warning: Check_SidesAreDisjoint buffer too small");
761 return -1.0f;
762 }
763 VectorCopy(w[i]->p[j], vertbuf[numVert]);
764 numVert++;
765 }
766 }
767 }
768
769 /* test for intersections between windings*/
770 for (i = 0; i < w[0]->numpoints; i++) {
771 const int pointIndex = (i + 1) % w[0]->numpoints;
772 for (int k = 0; k < w[1]->numpoints; k++) {
773 const int pointIndex2 = (k + 1) % w[1]->numpoints;
774 if (Check_EdgeEdgeIntersection(w[0]->p[i], w[0]->p[pointIndex], w[1]->p[k], w[1]->p[pointIndex2], vertbuf[numVert])) {
775 numVert++; /* if intersection, keep it */
776 if (numVert == VERT_BUF_SIZE_DISJOINT_SIDES) {
777 Check_Printf(VERB_CHECK, false, b[i]->entitynum, b[i]->brushnum, "warning: Check_SidesAreDisjoint buffer too small");
778 return -1.0f;
779 }
780 }
781 }
782 }
783
784 if (numVert < 3)
785 return -1.0f; /* must be at least 3 points to be not in a line */
786
787 {
788 /* make a winding, so WindingArea can be used */
789 float overlapArea, longestEdge, width;
790 winding_t* overlap = AllocWinding(numVert);
791 overlap->numpoints = numVert;
792 memcpy(overlap->p, vertbuf, numVert * sizeof(vec3_t));
793 overlapArea = WindingArea(overlap);
794#if 0
795 if (overlapArea > OVERLAP_AREA_TOL) {
796 for (int i = 0; i < numVert; i++) {
797 vec3_t* v = &vertbuf[i];
798 Com_Printf("(%f, %f, %f)\n", v[0], v[1], v[2]);
799 }
800 }
801#endif
802 /* small area, do not waste time calculating width */
803 if (overlapArea < OVERLAP_AREA_TOL) {
804 Mem_Free(overlap);
805 return -1.0f;
806 }
807
808 longestEdge = Check_LongestEdge(overlap);
809 width = overlapArea / longestEdge;
810 Mem_Free(overlap);
811 return width > OVERLAP_WIDTH_TOL ? width : -1.0f;
812 }
813
814#if 0
815 {
816 vec3_t from0to1, one, zero;
817
818 /* skip past elements 0, 1, ... if they are coincident - to avoid division by zero */
819 i = 0;
820 do {
821 i++;
822 if ((i + 1) >= numVert)
823 return false; /* not enough separated points - they must be in a line */
824 VectorSubtract(vertbuf[i], vertbuf[i - 1], from0to1);
825 VectorCopy(vertbuf[i - 1], zero);
826 VectorCopy(vertbuf[i], one);
827 } while (VectorLength(from0to1) < CH_DIST_EPSILON);
828
829 for (i++; i < numVert; i++) {
830 if (!Check_PointsAreCollinear(zero, one, vertbuf[i])) {
831 return true; /* 3 points not in a line, there is overlap */
832 }
833 }
834 }
835
836 return false; /* all points are collinear */
837#endif
838}
839
843void CheckZFighting (void)
844{
845 /* initialise mapbrush_t.nearBrushes */
847
848 /* loop through all pairs of near brushes */
849 for (int i = 0; i < nummapbrushes; i++) {
850 const mapbrush_t* iBrush = &mapbrushes[i];
851
852 if (!Check_IsOptimisable(iBrush))
853 continue; /* skip moving brushes, clips etc */
854
855 for (int j = 0; j < iBrush->numNear; j++) {
856 const mapbrush_t* jBrush = iBrush->nearBrushes[j];
857
858 if ((iBrush->contentFlags & CONTENTS_LEVEL_ALL) != (jBrush->contentFlags & CONTENTS_LEVEL_ALL))
859 continue; /* must be on the same level */
860
861 if (!Check_IsOptimisable(jBrush))
862 continue; /* skip moving brushes, clips etc */
863
864 for (int is = 0; is < iBrush->numsides; is++) {
865 const side_t* iSide = &iBrush->original_sides[is];
866
867 if (Check_SurfProp(SURF_NODRAW, iSide))
868 continue; /* skip nodraws */
869
870 if (Check_SidePointsDown(iSide))
871 continue; /* can't see these, view is always from above */
872
873 /* check each side of brush j for doing the hiding */
874 for (int js = 0; js < jBrush->numsides; js++) {
875 const side_t* jSide = &jBrush->original_sides[js];
876
877 /* skip nodraws */
878 if (Check_SurfProp(SURF_NODRAW, jSide))
879 continue;
880
881#if 0
885 if (ParallelAndCoincidentTo(iSide, jSide))
886 if (jSide->planenum != jSide->planenum)
887 Com_Printf("CheckZFighting: plane indices %i %i \n",
888 iSide->planenum, jSide->planenum);
889#endif
890
891 if (ParallelAndCoincidentTo(iSide, jSide) ) {
892 float overlapWidth = Check_SidesOverlap(iSide, jSide);
893 if ( overlapWidth > 0.0f) {
894 Check_Printf(VERB_CHECK, false, iBrush->entitynum, iBrush->brushnum,
895 "z-fighting with brush %i (entity %i). overlap width: %.3g units\n",
896 jBrush->brushnum, jBrush->entitynum, overlapWidth);
897#if 0
898 Check_SetError(iSide);
899 Check_SetError(jSide);
900#endif
901 }
902 }
903 }
904 }
905 }
906 }
907}
908
913{
914 /* initialise mapbrush_t.nearBrushes */
916
917 for (int i = 0; i < nummapbrushes; i++) {
918 mapbrush_t* iBrush = &mapbrushes[i];
919
920 /* do not check for brushes inside special (clip etc) brushes */
921 if (!Check_IsOptimisable(iBrush))
922 continue;
923
924 for (int j = 0; j < iBrush->numNear; j++) {
925 mapbrush_t* jBrush = iBrush->nearBrushes[j];
926 int numSidesInside = 0;
927
928 if (jBrush->contentFlags & CONTENTS_ORIGIN)
929 continue; /* origin brushes are allowed inside others */
930
931 for (int js = 0; js < jBrush->numsides; js++) {
932 const side_t* jSide = &jBrush->original_sides[js];
933
934 if (Check_SideIsInBrush(jSide, iBrush, PIB_INCL_SURF))
935 numSidesInside++;
936 }
937
938 if (numSidesInside == jBrush->numsides) {
939 Check_Printf(VERB_CHECK, false, jBrush->entitynum, jBrush->brushnum, "inside brush %i (entity %i)\n",
940 iBrush->brushnum, iBrush->entitynum);
941 }
942 }
943 }
944}
945
950static int Check_LevelForNodraws (const side_t* coverer, const side_t* coveree)
951{
952 return !(CONTENTS_LEVEL_ALL & ~coverer->contentFlags & coveree->contentFlags);
953}
954
955static void Check_SetNodraw (side_t* s)
956{
957 const ptrdiff_t index = s - brushsides;
959
960 Q_strncpyz(tex->name, "tex_common/nodraw", sizeof(tex->name));
961
962 /* do not actually set the flag that will be written back on -fix
963 * the texture is set, this should trigger the flag to be set
964 * in compile mode. check should behave the same as fix.
965 * The flag must be set in compile mode, as SetImpliedFlags calls are before the
966 * CheckNodraws call */
967 if (!(config.fixMap || config.performMapCheck))
969
973}
974
975#define CH_COMP_NDR_EDGE_INTSCT_BUF 21
976
984void CheckNodraws (void)
985{
986 int i, j, k, l, is, js;
987 int numSetFromSingleSide = 0, numSetPointingDown = 0, numSetFromCompositeSide = 0, iBrushNumSet = 0;
988
989 /* Initialise compositeSides[].. Note that this function
990 * calls Check_NearList to initialise mapbrush_t.nearBrushes */
992
993 /* check each brush, i, for downward sides */
994 for (i = 0; i < nummapbrushes; i++) {
995 mapbrush_t* iBrush = &mapbrushes[i];
996 iBrushNumSet = 0;
997
998 /* skip moving brushes, clips etc */
999 if (!Check_IsOptimisable(iBrush))
1000 continue;
1001
1002 /* check each side of i for pointing down */
1003 for (is = 0; is < iBrush->numsides; is++) {
1004 side_t* iSide = &iBrush->original_sides[is];
1005
1006 /* skip those that are already nodraw */
1007 if (Check_SurfProp(SURF_NODRAW, iSide))
1008 continue;
1009 /* surface lights may point downwards */
1010 else if (iSide->surfaceFlags & SURF_LIGHT)
1011 continue;
1012
1013 if (Check_SidePointsDown(iSide)) {
1014 Check_SetNodraw(iSide);
1015 numSetPointingDown++;
1016 iBrushNumSet++;
1017 }
1018
1019 }
1020 if (iBrushNumSet)
1021 Check_Printf(VERB_EXTRA, true, iBrush->entitynum, iBrush->brushnum, "set nodraw on %i sides (point down, or are close to pointing down).\n", iBrushNumSet);
1022 } /* next iBrush for downward faces that can be nodraw */
1023 if (numSetPointingDown)
1024 Check_Printf(VERB_CHECK, true, -1, -1, "total of %i nodraws set (point down, or are close to pointing down)\n", numSetPointingDown);
1025
1026 /* check each brush, i, for hidden sides */
1027 for (i = 0; i < nummapbrushes; i++) {
1028 mapbrush_t* iBrush = &mapbrushes[i];
1029 iBrushNumSet = 0;
1030
1031 /* skip moving brushes, clips etc */
1032 if (!Check_IsOptimisable(iBrush))
1033 continue;
1034
1035 /* check each brush, j, for having a side that hides one of i's faces */
1036 for (j = 0; j < iBrush->numNear; j++) {
1037 mapbrush_t* jBrush = iBrush->nearBrushes[j];
1038
1039 /* skip moving brushes, clips etc */
1040 if (!Check_IsOptimisable(jBrush))
1041 continue;
1042
1043 /* check each side of i for being hidden */
1044 for (is = 0; is < iBrush->numsides; is++) {
1045 side_t* iSide = &iBrush->original_sides[is];
1046
1047 if (!iSide->winding)
1048 continue; /* AddBrushBevels adds sides with no windings. skip these */
1049
1050 /* skip those that are already nodraw */
1051 if (Check_SurfProp(SURF_NODRAW, iSide))
1052 continue;
1053 /* surface lights may point downwards */
1054 else if (iSide->surfaceFlags & SURF_LIGHT)
1055 continue;
1056
1057 /* check each side of brush j for doing the hiding */
1058 for (js = 0; js < jBrush->numsides; js++) {
1059 const side_t* jSide = &jBrush->original_sides[js];
1060
1061 if (!jSide->winding)
1062 continue; /* AddBrushBevels adds sides with no windings. skip these */
1063
1064#if 0
1065 /* run on a largish map, this section proves that the plane indices alone cannot
1066 * cannot be relied on to test for 2 planes facing each other. */
1067 if (FacingAndCoincidentTo(iSide, jSide)) {
1068 const int minIndex = std::min(iSide->planenum, jSide->planenum);
1069 const int maxIndex = std::max(iSide->planenum, jSide->planenum);
1070 const int diff = maxIndex - minIndex, minOdd = (minIndex & 1);
1071 if ((diff != 1) || minOdd) {
1072 Com_Printf("CheckNodraws: facing and coincident plane indices %i %i diff:%i minOdd:%i\n",
1073 iSide->planenum, jSide->planenum, diff, minOdd);
1074 }
1075 }
1076#endif
1077
1078 if (Check_LevelForNodraws(jSide, iSide) &&
1079 FacingAndCoincidentTo(iSide, jSide) &&
1080 Check_SideIsInBrush(iSide, jBrush, PIB_INCL_SURF)) {
1081 Check_SetNodraw(iSide);
1082 iBrushNumSet++;
1083 numSetFromSingleSide++;
1084 }
1085 }
1086 }
1087 } /* next jBrush */
1088 if (iBrushNumSet)
1089 Check_Printf(VERB_EXTRA, true, iBrush->entitynum, iBrush->brushnum, "set nodraw on %i sides (covered by another brush).\n", iBrushNumSet);
1090
1091 iBrushNumSet = 0; /* reset to count composite side coverings */
1092
1093 /* check each composite side for hiding one of iBrush's sides */
1094 for (j = 0; j < numCompositeSides; j++) {
1095 const compositeSide_t* composite = &compositeSides[j];
1096 assert(composite);
1097 assert(composite->memberSides[0]);
1098
1099 /* check each side for being hidden */
1100 for (is = 0; is < iBrush->numsides; is++) {
1101 side_t* iSide = &iBrush->original_sides[is];
1102 winding_t* iWinding;
1103 vec3_t lastIntersection = {0, 0, 0}; /* used in innner loop, here to avoid repeated memset calls */
1104
1105 if (!FacingAndCoincidentTo(iSide, composite->memberSides[0]))
1106 continue; /* all sides in the composite are parallel, and iSide must face them to be hidden */
1107
1108 /* skip those that are already nodraw. note: this includes sides hidden by single sides
1109 * set above - this prevents duplicate nodraw reports */
1110 if (Check_SurfProp(SURF_NODRAW, iSide))
1111 continue;
1112
1113 iWinding = iSide->winding;
1114
1115 if (!iWinding)
1116 continue; /* AddBrushBevels adds sides with no windings. skip these */
1117
1118 /* to be covered each vertex of iSide must be on one of the composite side's members */
1119 for (k = 0; k < iWinding->numpoints; k++) {
1120 bool pointOnComposite = false;
1121 for (l = 0; l < composite->numMembers; l++) {
1122 if (Check_IsPointInsideBrush(iWinding->p[k], composite->memberSides[l]->brush, PIB_INCL_SURF)) {
1123
1124 /* levelflags mean this member cannot cover iSide
1125 * might be wrong to assume the composite will not cover iSide (if the members intersect)
1126 * it is _safe_ in that it will not result in an exposed nodraw */
1127 if (!Check_LevelForNodraws(composite->memberSides[l], iSide))
1128 goto next_iSide;
1129
1130 pointOnComposite = true;
1131 break;
1132 }
1133 }
1134 if (!pointOnComposite)
1135 goto next_iSide;
1136 }
1137
1138 /* search for intersections between composite and iSide */
1139 for (k = 0; k < iWinding->numpoints; k++) {
1140 vec3_t intersection;
1141 int lastIntersectionMembInd = -1;
1142 vec3_t intersections[CH_COMP_NDR_EDGE_INTSCT_BUF];
1143 bool paired[CH_COMP_NDR_EDGE_INTSCT_BUF];
1144 int numIntsct = 0;
1145
1146 OBJZERO(paired);
1147
1148 for (l = 0; l < composite->numMembers; l++) {
1149 const winding_t* mWinding = composite->memberSides[l]->winding;
1150
1151 for (int m = 0; m < mWinding->numpoints; m++) {
1152 bool intersects = Check_EdgeEdgeIntersection(
1153 iWinding->p[k], iWinding->p[(k + 1) % iWinding->numpoints],
1154 mWinding->p[m], mWinding->p[(m + 1) % mWinding->numpoints],
1155 intersection);
1156
1157 if (intersects) {
1158 bool coincident = false;
1159 /* check for coincident intersections */
1160 for (int n = 0; n < numIntsct; n++) {
1161 float distSq = VectorDistSqr(intersection, intersections[n]);
1162 if (CH_DIST_EPSILON_SQR > distSq) {
1163 paired[n] = true;
1164 coincident = true;
1165 }
1166 }
1167
1168 /* if it is not coincident, then add it to the list */
1169 if (!coincident) {
1170 VectorCopy(intersection, intersections[numIntsct]);
1171 numIntsct++;
1172 if (numIntsct >= CH_COMP_NDR_EDGE_INTSCT_BUF) {
1173 Check_Printf(VERB_LESS, false, -1, -1, "warning: CheckNodraws: buffer too small\n");
1174 return;
1175 }
1176 }
1177
1178 /* if edge k of iSide crosses side l of composite then check levelflags */
1179 /* note that as each member side is convex any line can intersect its edges a maximum of twice,
1180 * as the member sides of the composite are the inner loop, these two (if they exist) will
1181 * be found consecutively */
1182 if ((lastIntersectionMembInd == l) /* same composite as last intersection found */
1183 && (VectorDistSqr(intersection, lastIntersection) > CH_DIST_EPSILON_SQR) /* dist between this and last intersection is nonzero, indicating they are different intersections */
1184 && !Check_LevelForNodraws(composite->memberSides[l], iSide)) /* check nodraws */
1185 goto next_iSide;
1186
1187 lastIntersectionMembInd = l;
1188 VectorCopy(intersection, lastIntersection);
1189 }
1190 }
1191 }
1192
1193 /* make sure all intersections are paired. an unpaired intersection indicates
1194 * that iSide's boundary crosses out of the composite side, so iSide is not hidden */
1195 for (l = 0; l < numIntsct; l++) {
1196 if (!paired[l])
1197 goto next_iSide;
1198 }
1199
1200 }
1201
1202 /* set nodraw for iSide (covered by composite) */
1203 Check_SetNodraw(iSide);
1204 iBrushNumSet++;
1205 numSetFromCompositeSide++;
1206
1207 next_iSide:
1208 ;
1209 }
1210 } /* next composite */
1211 if (iBrushNumSet)
1212 Check_Printf(VERB_EXTRA, true, iBrush->entitynum, iBrush->brushnum, "set nodraw on %i sides (covered by a composite side).\n", iBrushNumSet);
1213 } /* next iBrush */
1214
1215 if (numSetFromSingleSide)
1216 Check_Printf(VERB_CHECK, true, -1, -1, "%i nodraws set (covered by another brush).\n", numSetFromSingleSide);
1217
1218 if (numSetFromCompositeSide)
1219 Check_Printf(VERB_CHECK, true, -1, -1, "%i nodraws set (covered by a composite side).\n", numSetFromCompositeSide);
1220
1221}
1222
1229{
1230 const side_t* sides = b->original_sides;
1231
1232 for (int i = 1; i < b->numsides; i++) {
1233 /* check for duplication and mirroring */
1234 for (int j = 0; j < i; j++) {
1235 if (sides[i].planenum == sides[j].planenum) {
1236 /* remove the second duplicate */
1237 Check_Printf(VERB_CHECK, false, b->entitynum, b->brushnum, "mirrored or duplicated\n");
1238 break;
1239 }
1240
1241 if (sides[i].planenum == (sides[j].planenum ^ 1)) {
1242 Check_Printf(VERB_CHECK, false, b->entitynum, b->brushnum, "mirror plane - brush is invalid\n");
1243 return false;
1244 }
1245 }
1246 }
1247 return true;
1248}
1249
1254{
1255 int i;
1256 const winding_t* w;
1257 vec3_t corner;
1258 vec_t d, area, volume;
1259 const plane_t* plane;
1260
1261 if (!brush)
1262 return 0;
1263
1264 /* grab the first valid point as the corner */
1265 w = nullptr;
1266 for (i = 0; i < brush->numsides; i++) {
1267 w = brush->original_sides[i].winding;
1268 if (w)
1269 break;
1270 }
1271 if (!w)
1272 return 0;
1273 VectorCopy(w->p[0], corner);
1274
1275 /* make tetrahedrons to all other faces */
1276 volume = 0;
1277 for (; i < brush->numsides; i++) {
1278 w = brush->original_sides[i].winding;
1279 if (!w)
1280 continue;
1281 plane = &mapplanes[brush->original_sides[i].planenum];
1282 d = -(DotProduct(corner, plane->normal) - plane->dist);
1283 area = WindingArea(w);
1284 volume += d * area;
1285 }
1286
1287 return volume / 3;
1288}
1289
1293void CheckMapMicro (void)
1294{
1295 for (int i = 0; i < nummapbrushes; i++) {
1296 mapbrush_t* brush = &mapbrushes[i];
1297 const float vol = Check_MapBrushVolume(brush);
1298 if (vol < config.mapMicrovol) {
1299 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "microbrush volume %f - will be deleted\n", vol);
1300 brush->skipWriteBack = true;
1301 }
1302 }
1303}
1304
1309void DisplayContentFlags (const int flags)
1310{
1311 if (!flags) {
1312 Check_Printf(VERB_CHECK, false, NUM_SAME, NUM_SAME, " no contentflags");
1313 return;
1314 }
1315#define M(x) if (flags & CONTENTS_##x) Check_Printf(VERB_CHECK, false, NUM_SAME, NUM_SAME, " " #x)
1316 M(SOLID);
1317 M(WINDOW);
1318 M(LADDER);
1319 M(WATER);
1320 M(LEVEL_1);
1321 M(LEVEL_2);
1322 M(LEVEL_3);
1323 M(LEVEL_4);
1324 M(LEVEL_5);
1325 M(LEVEL_6);
1326 M(LEVEL_7);
1327 M(LEVEL_8);
1328 M(ACTORCLIP);
1329 M(PASSABLE);
1330 M(ACTOR);
1331 M(ORIGIN);
1332 M(WEAPONCLIP);
1333 M(DEADACTOR);
1334 M(DETAIL);
1335 M(TRANSLUCENT);
1336#undef M
1337}
1338
1342static int Check_CalculateLevelFlagFill (int contentFlags)
1343{
1344 int firstSetLevel = 0, lastSetLevel = 0;
1345 int scanLevel, flagFill = 0;
1346
1347 for (scanLevel = CONTENTS_LEVEL_1; scanLevel <= CONTENTS_LEVEL_8; scanLevel <<= 1) {
1348 if (scanLevel & contentFlags) {
1349 if (!firstSetLevel) {
1350 firstSetLevel = scanLevel;
1351 } else {
1352 lastSetLevel = scanLevel;
1353 }
1354 }
1355 }
1356 for (scanLevel = firstSetLevel << 1 ; scanLevel < lastSetLevel; scanLevel <<= 1)
1357 flagFill |= scanLevel & ~contentFlags;
1358 return flagFill;
1359}
1360
1365{
1366 for (int i = 0; i < nummapbrushes; i++) {
1367 mapbrush_t* brush = &mapbrushes[i];
1368
1369 /* CheckLevelFlags should be done first, so we will boldly
1370 * assume that levelflags are the same on each face */
1371 int flagFill = Check_CalculateLevelFlagFill(brush->original_sides[0].contentFlags);
1372 if (flagFill) {
1373 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "making set levelflags continuous by setting");
1374 DisplayContentFlags(flagFill);
1375 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "\n");
1376 for (int j = 0; j < brush->numsides; j++)
1377 brush->original_sides[j].contentFlags |= flagFill;
1378 }
1379 }
1380}
1381
1386{
1387 bool setFlags;
1388 int allLevelFlagsForBrush;
1389
1390 for (int i = 0; i < nummapbrushes; i++) {
1391 mapbrush_t* brush = &mapbrushes[i];
1392 int j;
1393
1394 /* test if all faces are nodraw */
1395 bool allNodraw = true;
1396 for (j = 0; j < brush->numsides; j++) {
1397 const side_t* side = &brush->original_sides[j];
1398 assert(side);
1399
1400 if (!(Check_SurfProp(SURF_NODRAW, side))) {
1401 allNodraw = false;
1402 break;
1403 }
1404 }
1405
1406 /* proceed if some or all faces are not nodraw */
1407 if (!allNodraw) {
1408 allLevelFlagsForBrush = 0;
1409
1410 setFlags = false;
1411 /* test if some faces do not have levelflags and remember
1412 * all levelflags which are set. */
1413 for (j = 0; j < brush->numsides; j++) {
1414 const side_t* side = &brush->original_sides[j];
1415
1416 allLevelFlagsForBrush |= (side->contentFlags & CONTENTS_LEVEL_ALL);
1417
1418 if (!(side->contentFlags & (CONTENTS_ORIGIN | MASK_CLIP))) {
1419 /* check level 1 - level 8 */
1420 if (!(side->contentFlags & CONTENTS_LEVEL_ALL)) {
1421 setFlags = true;
1422 break;
1423 }
1424 }
1425 }
1426
1427 /* set the same flags for each face */
1428 if (setFlags) {
1429 const int flagsToSet = allLevelFlagsForBrush ? allLevelFlagsForBrush : CONTENTS_LEVEL_ALL;
1430 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "at least one face has no levelflags, setting %i on all faces\n", flagsToSet);
1431 for (j = 0; j < brush->numsides; j++) {
1432 side_t* side = &brush->original_sides[j];
1433 side->contentFlags |= flagsToSet;
1434 }
1435 }
1436 }
1437 }
1438}
1439
1448void SetImpliedFlags (side_t* side, brush_texture_t* tex, const mapbrush_t* brush)
1449{
1450 const char* texname = tex->name;
1451 const int initSurf = tex->surfaceFlags;
1452 const int initCont = side->contentFlags;
1453
1454 /* see discussion at Check_SetNodraw */
1455 if (!config.fixMap && !config.performMapCheck) {
1456 const char* flagsDescription = nullptr;
1457 if (Q_streq(texname, "tex_common/actorclip")) {
1459 flagsDescription = "CONTENTS_ACTORCLIP";
1460 } else if (Q_streq(texname, "tex_common/caulk")) {
1461 side->surfaceFlags |= SURF_NODRAW;
1462 tex->surfaceFlags |= SURF_NODRAW;
1463 flagsDescription = "SURF_NODRAW";
1464 } else if (Q_streq(texname, "tex_common/hint")) {
1465 side->surfaceFlags |= SURF_HINT;
1466 tex->surfaceFlags |= SURF_HINT;
1467 flagsDescription = "SURF_HINT";
1468 } else if (Q_streq(texname, "tex_common/ladder")) {
1470 side->surfaceFlags |= SURF_NODRAW;
1471 tex->surfaceFlags |= SURF_NODRAW;
1472 flagsDescription = "CONTENTS_LADDER";
1473 } else if (Q_streq(texname, "tex_common/lightclip")) {
1475 flagsDescription = "CONTENTS_LIGHTCLIP";
1476 } else if (Q_streq(texname, "tex_common/nodraw")) {
1477 /*side->contentFlags |= CONTENTS_SOLID;*/
1478 side->surfaceFlags |= SURF_NODRAW;
1479 tex->surfaceFlags |= SURF_NODRAW;
1480 flagsDescription = "SURF_NODRAW";
1481 } else if (Q_streq(texname, "tex_common/trigger")) {
1482 side->surfaceFlags |= SURF_NODRAW;
1483 tex->surfaceFlags |= SURF_NODRAW;
1484 flagsDescription = "SURF_NODRAW";
1485 } else if (Q_streq(texname, "tex_common/origin")) {
1487 flagsDescription = "CONTENTS_ORIGIN";
1488 } else if (Q_streq(texname, "tex_common/slick")) {
1489 side->contentFlags |= SURF_SLICK;
1490 flagsDescription = "SURF_SLICK";
1491 } else if (Q_streq(texname, "tex_common/weaponclip")) {
1493 flagsDescription = "CONTENTS_WEAPONCLIP";
1494 }
1495
1496 if (strstr(texname, "water")) {
1497#if 0
1498 side->surfaceFlags |= SURF_WARP;
1499 tex->surfaceFlags |= SURF_WARP;
1500#endif
1503 flagsDescription = "CONTENTS_WATER and CONTENTS_PASSABLE";
1504 }
1505
1506 /* If in check/fix mode and we have made a change, give output. */
1507 if ((side->contentFlags != initCont) || (tex->surfaceFlags != initSurf)) {
1508 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum,
1509 "%s implied by %s texture has been set\n", flagsDescription ? flagsDescription : "-", texname);
1510 }
1511 }
1512
1513 /* additional test, which does not directly depend on tex. */
1514 if (Check_SurfProp(SURF_NODRAW, side) && (tex->surfaceFlags & SURF_PHONG)) {
1515 /* nodraw never has phong set */
1516 side->surfaceFlags &= ~SURF_PHONG;
1517 tex->surfaceFlags &= ~SURF_PHONG;
1518 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum,
1519 "SURF_PHONG unset, as it has SURF_NODRAW set\n");
1520 }
1521
1522 if (side->surfaceFlags & SURF_SKIP) {
1523 side->surfaceFlags &= ~SURF_SKIP;
1524 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum,
1525 "removing legacy flag, SURF_SKIP\n");
1526 }
1527}
1528
1533{
1534 for (int i = 0; i < nummapbrushes; i++) {
1535 mapbrush_t* brush = &mapbrushes[i];
1536
1537 for (int j = 0; j < brush->numsides; j++) {
1538 side_t* side = &brush->original_sides[j];
1539 const ptrdiff_t index = side - brushsides;
1541
1542 assert(side);
1543 assert(tex);
1544
1545 /* set surface and content flags based on texture. */
1546 SetImpliedFlags(side, tex, brush);
1547 }
1548 }
1549}
1550
1556{
1557 for (int i = 0; i < nummapbrushes; i++) {
1558 mapbrush_t* brush = &mapbrushes[i];
1559
1560 for (int j = 0; j < brush->numsides; j++) {
1561 side_t* side = &brush->original_sides[j];
1562 const ptrdiff_t index = side - brushsides;
1564
1565 assert(side);
1566 assert(tex);
1567
1568 /* set textures based on flags */
1569 if (tex->name[0] == '\0') {
1570 Check_Printf(VERB_CHECK, false, brush->entitynum, brush->brushnum, " no texture assigned\n");
1571 }
1572
1573 if (Q_streq(tex->name, "tex_common/error")) {
1574 Check_Printf(VERB_CHECK, false, brush->entitynum, brush->brushnum, "error texture assigned - check this brush\n");
1575 }
1576
1577 if (Q_streq(tex->name, "nullptr")) {
1578 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "replaced nullptr with nodraw texture\n");
1579 Q_strncpyz(tex->name, "tex_common/nodraw", sizeof(tex->name));
1580 tex->surfaceFlags |= SURF_NODRAW;
1581 }
1582 if ((tex->surfaceFlags & SURF_NODRAW) && !Q_streq(tex->name, "tex_common/nodraw")) {
1583 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "set nodraw texture for SURF_NODRAW\n");
1584 tex->surfaceFlags &= ~SURF_PHONG;
1585 side->surfaceFlags &= ~SURF_PHONG;
1586 Q_strncpyz(tex->name, "tex_common/nodraw", sizeof(tex->name));
1587 }
1588 if ((tex->surfaceFlags & SURF_HINT) && !Q_streq(tex->name, "tex_common/hint")) {
1589 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "set hint texture for SURF_HINT\n");
1590 Q_strncpyz(tex->name, "tex_common/hint", sizeof(tex->name));
1591 }
1592
1593 if ((side->contentFlags & CONTENTS_WEAPONCLIP) && !Q_streq(tex->name, "tex_common/weaponclip")) {
1594 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "set weaponclip texture for CONTENTS_WEAPONCLIP\n");
1595 Q_strncpyz(tex->name, "tex_common/weaponclip", sizeof(tex->name));
1596 }
1597 if ((side->contentFlags & CONTENTS_ACTORCLIP) && !Q_streq(tex->name, "tex_common/actorclip")) {
1598 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "set actorclip texture for CONTENTS_ACTORCLIP\n");
1599 Q_strncpyz(tex->name, "tex_common/actorclip", sizeof(tex->name));
1600 }
1601 if ((side->contentFlags & CONTENTS_LIGHTCLIP) && !Q_streq(tex->name, "tex_common/lightclip")) {
1602 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "set lightclip texture for CONTENTS_LIGHTCLIP\n");
1603 Q_strncpyz(tex->name, "tex_common/lightclip", sizeof(tex->name));
1604 }
1605 if ((side->contentFlags & CONTENTS_ORIGIN) && !Q_streq(tex->name, "tex_common/origin")) {
1606 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "set origin texture for CONTENTS_ORIGIN\n");
1607 Q_strncpyz(tex->name, "tex_common/origin", sizeof(tex->name));
1608 }
1609 }
1610 }
1611}
1612
1620{
1621 int notInformedMixedFace = 1;
1622 int transferFlags = (CONTENTS_DETAIL | CONTENTS_TRANSLUCENT);
1623
1624 for (int m = 0; m < b->numsides; m++) {
1625 int contentFlagDiff = (b->original_sides[m].contentFlags ^ b->contentFlags) & transferFlags;
1626 if (contentFlagDiff) {
1627 /* only tell them once per brush */
1628 if (notInformedMixedFace) {
1629 Check_Printf(VERB_CHECK, true, b->entitynum , b->brushnum, "transferring contentflags to all faces:");
1630 DisplayContentFlags(contentFlagDiff);
1631 Check_Printf(VERB_CHECK, true, b->entitynum , b->brushnum, "\n");
1632 notInformedMixedFace = 0;
1633 }
1634 b->original_sides[m].contentFlags |= b->contentFlags ;
1635 }
1636 }
1637}
1638
1648{
1649 int nfActorclip; /* number of faces with actorclip contentflag set */
1650
1651 for (int i = 0; i < nummapbrushes; i++) {
1652 mapbrush_t* brush = &mapbrushes[i];
1653 side_t* side0;
1654 int j;
1655
1656 /* if the origin flag is set in the mapbrush_t struct, then the brushes
1657 * work is done, and we can skip the mixed face contents check for this brush */
1658 if (brush->contentFlags & CONTENTS_ORIGIN)
1659 continue;
1660
1661 side0 = &brush->original_sides[0];
1662 nfActorclip = 0;
1663
1665
1666 for (j = 0; j < brush->numsides; j++) {
1667 side_t* side = &brush->original_sides[j];
1668 assert(side);
1669
1670 nfActorclip += (side->contentFlags & CONTENTS_ACTORCLIP) ? 1 : 0;
1671
1672 if (side0->contentFlags != side->contentFlags) {
1673 const int jNotZero = side->contentFlags & ~side0->contentFlags;
1674 const int zeroNotJ = side0->contentFlags & ~side->contentFlags;
1675 Check_Printf(VERB_CHECK, false, brush->entitynum, brush->brushnum, "mixed face contents (");
1676 if (jNotZero) {
1677 Check_Printf(VERB_CHECK, false, NUM_SAME, NUM_SAME, "face %i has and face 0 has not", j);
1678 DisplayContentFlags(jNotZero);
1679 if (zeroNotJ)
1680 Check_Printf(VERB_CHECK, false, NUM_SAME, NUM_SAME, ", ");
1681 }
1682 if (zeroNotJ) {
1683 Check_Printf(VERB_CHECK, false, NUM_SAME, NUM_SAME, "face 0 has and face %i has not", j);
1684 DisplayContentFlags(zeroNotJ);
1685 }
1686 Check_Printf(VERB_CHECK, false, NUM_SAME, NUM_SAME, ")\n");
1687 }
1688 }
1689
1690 if (nfActorclip && nfActorclip != brush->numsides) {
1691 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "ACTORCLIP is not set on all of the faces: removing.\n");
1692 for (j = 0; j < brush->numsides; j++) {
1693 side_t* side = &brush->original_sides[j];
1694 const ptrdiff_t index = side - brushsides;
1696
1697 if ((side->contentFlags & CONTENTS_ACTORCLIP) && Q_streq(tex->name, "tex_common/actorclip")) {
1698 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "removing tex_common/actorclip, setting tex_common/error\n");
1699 Q_strncpyz(tex->name, "tex_common/error", sizeof(tex->name));
1700 }
1701
1703 }
1704 }
1705 }
1706}
1707
1708void CheckBrushes (void)
1709{
1710 for (int i = 0; i < nummapbrushes; i++) {
1711 mapbrush_t* brush = &mapbrushes[i];
1712
1714
1715 for (int j = 0; j < brush->numsides; j++) {
1716 side_t* side = &brush->original_sides[j];
1717
1718 assert(side);
1719
1720 if ((side->contentFlags & CONTENTS_ORIGIN) && brush->entitynum == 0) {
1721 Check_Printf(VERB_CHECK, true, brush->entitynum, brush->brushnum, "origin brush inside worldspawn - removed CONTENTS_ORIGIN\n");
1723 }
1724 }
1725 }
1726}
side_t brushsides[MAX_MAP_SIDES]
Definition map.cpp:37
int nummapbrushes
Definition map.cpp:35
brush_texture_t side_brushtextures[MAX_MAP_SIDES]
Definition map.cpp:40
plane_t mapplanes[MAX_MAP_PLANES]
Definition map.cpp:43
mapbrush_t mapbrushes[MAX_MAP_BRUSHES]
Definition map.cpp:34
entity_t entities[MAX_MAP_ENTITIES]
Definition bspfile.cpp:395
const char * ValueForKey(const entity_t *ent, const char *key)
Definition bspfile.cpp:558
static int Check_LevelForNodraws(const side_t *coverer, const side_t *coveree)
Definition check.cpp:950
static bool Check_SideIsInBrush(const side_t *side, const mapbrush_t *brush, pointInBrush_t mode)
tests the vertices in the winding of side s.
Definition check.cpp:348
#define NEARDOWN_COS
Definition check.cpp:67
static bool Check_SurfProps(const int flags, const side_t *s)
textures take priority over flags. checks if a tex marks a side as having a special property.
Definition check.cpp:222
void Check_ContainedBrushes(void)
find duplicated brushes and brushes contained inside brushes
Definition check.cpp:912
static vec_t Check_MapBrushVolume(const mapbrush_t *brush)
Definition check.cpp:1253
static float Check_LongestEdge(const winding_t *w)
Definition check.cpp:720
static bool Check_IsPointInsideBrush(const vec3_t point, const mapbrush_t *brush, const pointInBrush_t mode)
tests if a point is in a map brush.
Definition check.cpp:152
compositeSide_t compositeSides[MAX_MAP_SIDES/2]
Definition check.cpp:37
static bool FacingAndCoincidentTo(const side_t *side1, const side_t *side2)
calculates whether side1 faces side2 and touches.
Definition check.cpp:106
#define COS_EPSILON
Definition check.cpp:48
pointInBrush_t
wether the surface of a brush is included when testing if a point is in a brush determines how epsilo...
Definition check.cpp:58
@ PIB_INCL_SURF
Definition check.cpp:61
@ PIB_INCL_SURF_EXCL_EDGE
Definition check.cpp:60
@ PIB_EXCL_SURF
Definition check.cpp:59
@ PIB_ON_SURFACE_ONLY
Definition check.cpp:62
static bool Check_WindingIntersects(const winding_t *winding, const mapbrush_t *brush)
tests the lines joining the vertices in the winding
Definition check.cpp:569
void CheckFlagsBasedOnTextures(void)
sets content flags based on textures
Definition check.cpp:1532
static bool Check_SurfProp(const int flag, const side_t *s)
textures take priority over flags. checks if a tex marks a side as having a special property.
Definition check.cpp:190
static void Check_NearList(void)
add a list of near brushes to each mapbrush. near meaning that the bounding boxes are intersecting or...
Definition check.cpp:294
#define CH_DIST_EPSILON_SQR
Definition check.cpp:43
static bool Check_BoundingBoxIntersects(const mapbrush_t *a, const mapbrush_t *b)
Definition check.cpp:279
void CheckTexturesBasedOnFlags(void)
check that sides have textures and that where content/surface flags are set the texture is correct.
Definition check.cpp:1555
static bool Check_SidesTouch(side_t *a, side_t *b)
test if sides abut or intersect
Definition check.cpp:382
static float Check_SidesOverlap(const side_t *s1, const side_t *s2)
tests if sides overlap, for z-fighting check
Definition check.cpp:744
#define CH_DIST_EPSILON_COLLINEAR_POINTS
Definition check.cpp:45
void CheckPropagateParserContentFlags(mapbrush_t *b)
some contentlflags are set as a result of some surface flag. For example, if one face is TRANS* then ...
Definition check.cpp:1619
#define CH_COMP_NDR_EDGE_INTSCT_BUF
Definition check.cpp:975
void CheckMixedFaceContents(void)
contentflags should be the same on each face of a brush. print warnings if they are not....
Definition check.cpp:1647
#define OVERLAP_AREA_TOL
Definition check.cpp:732
void CheckNodraws(void)
check for faces which can safely be set to SURF_NODRAW because they are pressed against the faces of ...
Definition check.cpp:984
static bool ParallelAndCoincidentTo(const side_t *side1, const side_t *side2)
calculates whether side1 and side2 are on a common plane
Definition check.cpp:131
static int Check_CalculateLevelFlagFill(int contentFlags)
calculate the bits that have to be set to fill levelflags such that they are contiguous
Definition check.cpp:1342
void Check_BrushIntersection(void)
reports intersection between optimisable map brushes
Definition check.cpp:589
void CheckLevelFlags(void)
sets all levelflags, if none are set.
Definition check.cpp:1385
#define CH_DIST_EPSILON
Definition check.cpp:42
int numCompositeSides
Definition check.cpp:38
#define OVERLAP_WIDTH_TOL
Definition check.cpp:733
static void Check_SetNodraw(side_t *s)
Definition check.cpp:955
#define VERT_BUF_SIZE_DISJOINT_SIDES
Definition check.cpp:731
void CheckFillLevelFlags(void)
ensures set levelflags are in one contiguous block
Definition check.cpp:1364
void SetImpliedFlags(side_t *side, brush_texture_t *tex, const mapbrush_t *brush)
Sets surface flags dependent on assigned texture.
Definition check.cpp:1448
void CheckMapMicro(void)
report brushes from the map below 1 unit^3
Definition check.cpp:1293
static int Check_EdgePlaneIntersection(const vec3_t vert1, const vec3_t vert2, const plane_t *plane, vec3_t intersection)
calculate where an edge (defined by the vertices) intersects a plane. http://local....
Definition check.cpp:539
static bool Check_SidePointsDown(const side_t *s)
faces that are near pointing down may be set nodraw, as views are always slightly down
Definition check.cpp:73
static float Check_PointPlaneDistance(const vec3_t point, const plane_t *plane)
distance from a point to a plane.
Definition check.cpp:86
static bool Check_IsOptimisable(const mapbrush_t *b)
Definition check.cpp:253
#define M(x)
static bool Check_DuplicateBrushPlanes(const mapbrush_t *b)
Definition check.cpp:1228
#define SIN_EPSILON
Definition check.cpp:51
void CheckZFighting(void)
check all brushes for overlapping shared faces
Definition check.cpp:843
static bool Check_EdgeEdgeIntersection(const vec3_t e1p1, const vec3_t e1p2, const vec3_t e2p1, const vec3_t e2p2, vec3_t intersection)
finds point of intersection of two finite lines, if one exists
Definition check.cpp:627
void CheckBrushes(void)
Definition check.cpp:1708
void DisplayContentFlags(const int flags)
prints a list of the names of the set content flags or "no contentflags" if all bits are 0
Definition check.cpp:1309
static void Check_FindCompositeSides(void)
a composite side is a side made of sides from neighbouring brushes. the sides abut....
Definition check.cpp:406
Performs check on a loaded mapfile, and makes changes that can be saved back to the source map.
void Check_Printf(verbosityLevel_t msgVerbLevel, bool change, int entnum, int brushnum, const char *format,...)
decides wether to proceed with output based on verbosity and ufo2map's mode: check/fix/compile
Definition checklib.cpp:49
Performs check on a loaded mapfile, and makes changes that can be saved back to the source map.
#define NUM_SAME
Definition checklib.h:34
int down
Definition cl_input.cpp:66
vec3_t maxs
Definition aabb.h:258
vec3_t mins
Definition aabb.h:257
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
#define CONTENTS_ORIGIN
Definition defines.h:248
#define MAX_MAP_SIDES
Definition defines.h:383
#define CONTENTS_TRANSLUCENT
Definition defines.h:252
#define CONTENTS_WEAPONCLIP
Definition defines.h:249
#define CONTENTS_LEVEL_8
Definition defines.h:240
#define CONTENTS_DETAIL
Definition defines.h:251
#define SURF_PHONG
Definition defines.h:263
#define SURF_LIGHT
Definition defines.h:254
#define CONTENTS_LEVEL_ALL
Definition defines.h:232
#define CONTENTS_ACTORCLIP
Definition defines.h:243
#define DIST_EPSILON
Definition defines.h:377
#define SURF_NODRAW
Definition defines.h:260
#define SURF_WARP
Definition defines.h:256
#define CONTENTS_PASSABLE
Definition defines.h:244
#define SURF_SKIP
Definition defines.h:262
#define SURF_HINT
Definition defines.h:261
#define MAX_MAP_BRUSHES
Definition defines.h:135
#define SURF_SLICK
Definition defines.h:255
#define CONTENTS_LEVEL_1
Definition defines.h:233
#define MASK_CLIP
Definition defines.h:278
#define CONTENTS_WATER
Definition defines.h:226
#define CONTENTS_LADDER
Definition defines.h:225
#define CONTENTS_LIGHTCLIP
Definition defines.h:246
Handles definition of entities, parsing them from entities.ufo.
void Sys_Error(const char *error,...)
Definition g_main.cpp:421
const char int mode
Definition ioapi.h:41
vec_t VectorNormalize(vec3_t v)
Calculate unit vector for a given vec3_t.
Definition mathlib.cpp:745
vec_t VectorLength(const vec3_t v)
Calculate the length of a vector.
Definition mathlib.cpp:434
void CrossProduct(const vec3_t v1, const vec3_t v2, vec3_t cross)
binary operation on vectors in a three-dimensional space
Definition mathlib.cpp:820
static struct mdfour * m
Definition md4.cpp:35
#define Mem_Free(ptr)
Definition mem.h:35
#define Mem_AllocTypeN(type, n)
Definition mem.h:38
winding_t * AllocWinding(int points)
Allocate a new winding (polygon).
Definition polylib.cpp:38
vec_t WindingArea(const winding_t *w)
Definition polylib.cpp:81
QGL_EXTERN int GLboolean GLfloat * v
Definition r_gl.h:120
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition r_gl.h:110
QGL_EXTERN GLuint index
Definition r_gl.h:110
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
#define Q_streq(a, b)
Definition shared.h:136
#define OBJZERO(obj)
Definition shared.h:178
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
char name[MAX_TEXPATH]
Definition map.h:37
uint32_t surfaceFlags
Definition map.h:38
int numMembers
Definition check.h:34
struct side_s ** memberSides
Definition check.h:33
struct mapbrush_s ** nearBrushes
Definition map.h:89
struct side_s * original_sides
Definition map.h:84
int brushnum
Definition map.h:77
bool skipWriteBack
Definition map.h:92
int numsides
Definition map.h:83
uint32_t contentFlags
Definition map.h:79
AABB mbBox
Definition map.h:81
int entitynum
Definition map.h:76
int numNear
Definition map.h:90
Definition map.h:98
vec3_t planeVector[3]
Definition map.h:106
vec_t dist
Definition map.h:100
vec3_t normal
Definition map.h:99
Definition map.h:60
uint16_t planenum
Definition map.h:61
winding_t * winding
Definition map.h:63
uint32_t surfaceFlags
Definition map.h:66
bool isCompositeMember
Definition map.h:70
uint32_t contentFlags
Definition map.h:65
struct mapbrush_s * brush
Definition map.h:72
for storing the vertices of the side of a brush or other polygon
Definition polylib.h:30
int numpoints
Definition polylib.h:31
vec3_t p[4]
Definition polylib.h:32
static config_t config
Definition test_all.cpp:43
@ VERB_EXTRA
Definition shared.h:46
@ VERB_LESS
Definition shared.h:44
@ VERB_CHECK
Definition shared.h:42
float vec_t
Definition ufotypes.h:37
vec_t vec3_t[3]
Definition ufotypes.h:39
#define VectorMul(scalar, b, dest)
Definition vector.h:48
#define VectorSubtract(a, b, dest)
Definition vector.h:45
#define VectorCopy(src, dest)
Definition vector.h:51
#define VectorDistSqr(a, b)
Definition vector.h:68
#define VectorAdd(a, b, dest)
Definition vector.h:47
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition vector.h:44
#define VectorScale(in, scale, out)
Definition vector.h:79
#define VectorLengthSqr(a)
Definition vector.h:71