UFO: Alien Invasion
Doxygen documentation generating
check.cpp
Go to the documentation of this file.
1 
6 /*
7 All original material Copyright (C) 2002-2023 UFO: Alien Invasion.
8 
9 Copyright (C) 1997-2001 Id Software, Inc.
10 
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 as published by the Free Software Foundation; either version 2
14 of the License, or (at your option) any later version.
15 
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 
20 See the GNU General Public License for more details.
21 
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, 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"
32 #include "../../../shared/entitiesdef.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
48 #define COS_EPSILON 0.9999f
49 
51 #define SIN_EPSILON 0.0001f
52 
58 typedef enum {
64 
67 #define NEARDOWN_COS 0.985
68 
73 static 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 
86 static 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 
106 static 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 
131 static 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 
152 static 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 
190 static 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");
199  case CONTENTS_WEAPONCLIP:
200  return Q_streq(tex->name, "tex_common/weaponclip");
201  case CONTENTS_ACTORCLIP:
202  return Q_streq(tex->name, "tex_common/actorclip");
203  case CONTENTS_LIGHTCLIP:
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 
222 static 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 
253 static 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 
279 static 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 
294 static 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 
348 static 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
362 
366 static 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 
382 static 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 
406 static 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 
423  Check_NearList();
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 
515  compositeSides[numCompositeSides].memberSides = sidesInNewComposite;
516 
517  for (j = 0; j < numMembers; 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 
539 static 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 
569 static 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 */
592  Check_NearList();
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 
627 static 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 */
681 
688 static 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 
720 static 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 
744 static 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 
843 void CheckZFighting (void)
844 {
845  /* initialise mapbrush_t.nearBrushes */
846  Check_NearList();
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
882 
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 */
915  Check_NearList();
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 
950 static int Check_LevelForNodraws (const side_t* coverer, const side_t* coveree)
951 {
952  return !(CONTENTS_LEVEL_ALL & ~coverer->contentFlags & coveree->contentFlags);
953 }
954 
955 static 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))
968  tex->surfaceFlags |= SURF_NODRAW;
969 
970  s->surfaceFlags &= ~SURF_PHONG;
971  tex->surfaceFlags &= ~SURF_PHONG;
973 }
974 
975 #define CH_COMP_NDR_EDGE_INTSCT_BUF 21
976 
984 void 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 
1293 void 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 
1309 void 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 
1342 static 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 
1385 void CheckLevelFlags (void)
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 
1448 void 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")) {
1469  side->contentFlags |= CONTENTS_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")) {
1486  side->contentFlags |= CONTENTS_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
1501  side->contentFlags |= CONTENTS_WATER;
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 
1702  side->contentFlags &= ~CONTENTS_ACTORCLIP;
1703  }
1704  }
1705  }
1706 }
1707 
1708 void 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");
1722  side->contentFlags &= ~CONTENTS_ORIGIN;
1723  }
1724  }
1725  }
1726 }
void CheckZFighting(void)
check all brushes for overlapping shared faces
Definition: check.cpp:843
Performs check on a loaded mapfile, and makes changes that can be saved back to the source map...
vec_t VectorLength(const vec3_t v)
Calculate the length of a vector.
Definition: mathlib.cpp:434
char name[MAX_TEXPATH]
Definition: map.h:37
vec3_t planeVector[3]
Definition: map.h:106
vec3_t maxs
Definition: aabb.h:258
Performs check on a loaded mapfile, and makes changes that can be saved back to the source map...
#define SURF_NODRAW
Definition: defines.h:260
static bool Check_IsOptimisable(const mapbrush_t *b)
Definition: check.cpp:253
#define VectorCopy(src, dest)
Definition: vector.h:51
#define Mem_AllocTypeN(type, n)
Definition: mem.h:38
#define CONTENTS_WEAPONCLIP
Definition: defines.h:249
#define OVERLAP_AREA_TOL
Definition: check.cpp:732
void Sys_Error(const char *error,...)
Definition: g_main.cpp:421
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 bool ParallelAndCoincidentTo(const side_t *side1, const side_t *side2)
calculates whether side1 and side2 are on a common plane
Definition: check.cpp:131
int brushnum
Definition: map.h:77
#define CONTENTS_LADDER
Definition: defines.h:225
static float Check_PointPlaneDistance(const vec3_t point, const plane_t *plane)
distance from a point to a plane.
Definition: check.cpp:86
static void Check_FindCompositeSides(void)
a composite side is a side made of sides from neighbouring brushes. the sides abut. these sides can cooperate to hide a face, this is used for nodraw setting. composite sides may be used for other things in the future.
Definition: check.cpp:406
#define CONTENTS_ORIGIN
Definition: defines.h:248
bool isCompositeMember
Definition: map.h:70
#define CONTENTS_PASSABLE
Definition: defines.h:244
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
#define SURF_SLICK
Definition: defines.h:255
#define NEARDOWN_COS
Definition: check.cpp:67
int numCompositeSides
Definition: check.cpp:38
side_t brushsides[MAX_MAP_SIDES]
Definition: map.cpp:37
static float Check_LongestEdge(const winding_t *w)
Definition: check.cpp:720
void CheckMixedFaceContents(void)
contentflags should be the same on each face of a brush. print warnings if they are not...
Definition: check.cpp:1647
float vec_t
Definition: ufotypes.h:37
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 CONTENTS_LEVEL_8
Definition: defines.h:240
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
static bool FacingAndCoincidentTo(const side_t *side1, const side_t *side2)
calculates whether side1 faces side2 and touches.
Definition: check.cpp:106
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
#define CH_DIST_EPSILON_SQR
Definition: check.cpp:43
#define NUM_SAME
Definition: checklib.h:34
#define CONTENTS_LEVEL_ALL
Definition: defines.h:232
static bool Check_SidesTouch(side_t *a, side_t *b)
test if sides abut or intersect
Definition: check.cpp:382
void SetImpliedFlags(side_t *side, brush_texture_t *tex, const mapbrush_t *brush)
Sets surface flags dependent on assigned texture.
Definition: check.cpp:1448
struct mapbrush_s * brush
Definition: map.h:72
brush_texture_t side_brushtextures[MAX_MAP_SIDES]
Definition: map.cpp:40
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
int nummapbrushes
Definition: map.cpp:35
void Check_BrushIntersection(void)
reports intersection between optimisable map brushes
Definition: check.cpp:589
entity_t entities[MAX_MAP_ENTITIES]
Definition: bspfile.cpp:395
uint32_t surfaceFlags
Definition: map.h:66
static bool Check_BoundingBoxIntersects(const mapbrush_t *a, const mapbrush_t *b)
Definition: check.cpp:279
#define VectorScale(in, scale, out)
Definition: vector.h:79
int numsides
Definition: map.h:83
#define CONTENTS_DETAIL
Definition: defines.h:251
#define CONTENTS_LEVEL_1
Definition: defines.h:233
void CheckFillLevelFlags(void)
ensures set levelflags are in one contiguous block
Definition: check.cpp:1364
void CheckMapMicro(void)
report brushes from the map below 1 unit^3
Definition: check.cpp:1293
mapbrush_t mapbrushes[MAX_MAP_BRUSHES]
Definition: map.cpp:34
uint16_t planenum
Definition: map.h:61
vec3_t p[4]
Definition: polylib.h:32
#define CONTENTS_WATER
Definition: defines.h:226
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
#define VectorMul(scalar, b, dest)
Definition: vector.h:48
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition: vector.h:44
static bool Check_DuplicateBrushPlanes(const mapbrush_t *b)
Definition: check.cpp:1228
vec3_t normal
Definition: map.h:99
#define OBJZERO(obj)
Definition: shared.h:178
winding_t * AllocWinding(int points)
Allocate a new winding (polygon)
Definition: polylib.cpp:38
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition: r_gl.h:110
void CheckTexturesBasedOnFlags(void)
check that sides have textures and that where content/surface flags are set the texture is correct...
Definition: check.cpp:1555
uint32_t contentFlags
Definition: map.h:65
#define MAX_MAP_SIDES
Definition: defines.h:383
uint32_t surfaceFlags
Definition: map.h:38
#define CH_COMP_NDR_EDGE_INTSCT_BUF
Definition: check.cpp:975
struct side_s ** memberSides
Definition: check.h:33
void CheckLevelFlags(void)
sets all levelflags, if none are set.
Definition: check.cpp:1385
#define SURF_PHONG
Definition: defines.h:263
vec3_t mins
Definition: aabb.h:257
winding_t * winding
Definition: map.h:63
plane_t mapplanes[MAX_MAP_PLANES]
Definition: map.cpp:43
vec_t dist
Definition: map.h:100
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&#39;s mode: check/fix/compile ...
Definition: checklib.cpp:49
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
int numNear
Definition: map.h:90
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
int entitynum
Definition: map.h:76
static void Check_SetNodraw(side_t *s)
Definition: check.cpp:955
static config_t config
Definition: test_all.cpp:43
QGL_EXTERN GLuint index
Definition: r_gl.h:110
int numMembers
Definition: check.h:34
Definition: map.h:98
#define OVERLAP_WIDTH_TOL
Definition: check.cpp:733
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
#define VectorLengthSqr(a)
Definition: vector.h:71
#define SURF_SKIP
Definition: defines.h:262
void CheckBrushes(void)
Definition: check.cpp:1708
#define VectorAdd(a, b, dest)
Definition: vector.h:47
AABB mbBox
Definition: map.h:81
vec_t WindingArea(const winding_t *w)
Definition: polylib.cpp:81
compositeSide_t compositeSides[MAX_MAP_SIDES/2]
Definition: check.cpp:37
#define SURF_LIGHT
Definition: defines.h:254
#define CONTENTS_ACTORCLIP
Definition: defines.h:243
QGL_EXTERN GLint i
Definition: r_gl.h:113
#define CH_DIST_EPSILON_COLLINEAR_POINTS
Definition: check.cpp:45
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
#define VERT_BUF_SIZE_DISJOINT_SIDES
Definition: check.cpp:731
vec_t VectorNormalize(vec3_t v)
Calculate unit vector for a given vec3_t.
Definition: mathlib.cpp:745
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
#define CONTENTS_TRANSLUCENT
Definition: defines.h:252
#define MASK_CLIP
Definition: defines.h:278
static vec_t Check_MapBrushVolume(const mapbrush_t *brush)
Definition: check.cpp:1253
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
#define CH_DIST_EPSILON
Definition: check.cpp:42
#define Mem_Free(ptr)
Definition: mem.h:35
const char int mode
Definition: ioapi.h:41
for storing the vertices of the side of a brush or other polygon
Definition: polylib.h:30
#define DIST_EPSILON
Definition: defines.h:377
vec_t vec3_t[3]
Definition: ufotypes.h:39
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
Definition: map.h:60
int numpoints
Definition: polylib.h:31
#define SURF_HINT
Definition: defines.h:261
#define SIN_EPSILON
Definition: check.cpp:51
#define Q_streq(a, b)
Definition: shared.h:136
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.wasp.uwa.edu.au/~pbourke/geometry/planeline/
Definition: check.cpp:539
const char * ValueForKey(const entity_t *ent, const char *key)
Definition: bspfile.cpp:558
bool skipWriteBack
Definition: map.h:92
void Check_ContainedBrushes(void)
find duplicated brushes and brushes contained inside brushes
Definition: check.cpp:912
#define SURF_WARP
Definition: defines.h:256
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
#define CONTENTS_LIGHTCLIP
Definition: defines.h:246
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
QGL_EXTERN int GLboolean GLfloat * v
Definition: r_gl.h:120
#define VectorDistSqr(a, b)
Definition: vector.h:68
#define MAX_MAP_BRUSHES
Definition: defines.h:135
static struct mdfour * m
Definition: md4.cpp:35
void CheckFlagsBasedOnTextures(void)
sets content flags based on textures
Definition: check.cpp:1532
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
int down
Definition: cl_input.cpp:70
#define VectorSubtract(a, b, dest)
Definition: vector.h:45
struct mapbrush_s ** nearBrushes
Definition: map.h:89
static int Check_LevelForNodraws(const side_t *coverer, const side_t *coveree)
Definition: check.cpp:950
uint32_t contentFlags
Definition: map.h:79
Definition: map.h:75
#define M(x)
static float Check_SidesOverlap(const side_t *s1, const side_t *s2)
tests if sides overlap, for z-fighting check
Definition: check.cpp:744
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
#define COS_EPSILON
Definition: check.cpp:48
struct side_s * original_sides
Definition: map.h:84