UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
portals.cpp
Go to the documentation of this file.
1 
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 
29 #include "bsp.h"
30 
31 
32 static int c_active_portals = 0;
33 static int c_peak_portals = 0;
34 static int c_tinyportals = 0;
35 
39 static portal_t* AllocPortal (void)
40 {
41  if (threadstate.numthreads == 1)
45 
46  return Mem_AllocType(portal_t);
47 }
48 
53 {
54  if (p->winding)
55  FreeWinding(p->winding);
56  if (threadstate.numthreads == 1)
58  Mem_Free(p);
59 }
60 
64 uint32_t VisibleContents (uint32_t contentFlags)
65 {
66  uint32_t i;
67 
68  for (i = 1; i <= LAST_VISIBLE_CONTENTS; i <<= 1)
69  if (contentFlags & i)
70  return i;
71 
72  return 0;
73 }
74 
82 static void AddPortalToNodes (portal_t* p, node_t* front, node_t* back)
83 {
84  if (p->nodes[0] || p->nodes[1])
85  Sys_Error("AddPortalToNodes: already included");
86 
87  p->nodes[0] = front;
88  p->next[0] = front->portals;
89  front->portals = p;
90 
91  p->nodes[1] = back;
92  p->next[1] = back->portals;
93  back->portals = p;
94 }
95 
103 {
104  portal_t** pp;
105 
106  /* remove reference to the current portal */
107  pp = &l->portals;
108  while (1) {
109  portal_t* t = *pp;
110  if (!t)
111  Sys_Error("RemovePortalFromNode: portal not in leaf");
112 
113  if (t == portal)
114  break;
115 
116  if (t->nodes[0] == l)
117  pp = &t->next[0];
118  else if (t->nodes[1] == l)
119  pp = &t->next[1];
120  else
121  Sys_Error("RemovePortalFromNode: portal not bounding leaf");
122  }
123 
124  if (portal->nodes[0] == l) {
125  *pp = portal->next[0];
126  portal->nodes[0] = nullptr;
127  } else if (portal->nodes[1] == l) {
128  *pp = portal->next[1];
129  portal->nodes[1] = nullptr;
130  }
131 }
132 
133 #define SIDESPACE 8
134 
137 static void MakeHeadnodePortals (tree_t* tree)
138 {
139  vec3_t bounds[2];
140  int i, j;
141  portal_t* portals[6];
142  plane_t bplanes[6];
143  node_t* node = tree->headnode;
144 
145  /* pad with some space so there will never be null volume leafs */
146  for (i = 0; i < 3; i++) {
147  bounds[0][i] = tree->aabb.mins[i] - SIDESPACE;
148  bounds[1][i] = tree->aabb.maxs[i] + SIDESPACE;
149  }
150 
152  tree->outside_node.brushlist = nullptr;
153  tree->outside_node.portals = nullptr;
154  tree->outside_node.contentFlags = 0;
155 
156  for (i = 0; i < 3; i++)
157  for (j = 0; j < 2; j++) {
158  const int n = j * 3 + i;
159  portal_t* p = AllocPortal();
160  plane_t* pl = &bplanes[n];
161 
162  OBJZERO(*pl);
163  portals[n] = p;
164 
165  if (j) {
166  pl->normal[i] = -1;
167  pl->dist = -bounds[j][i];
168  } else {
169  pl->normal[i] = 1;
170  pl->dist = bounds[j][i];
171  }
172  p->plane = *pl;
173  p->winding = BaseWindingForPlane(pl->normal, pl->dist);
174  AddPortalToNodes(p, node, &tree->outside_node);
175  }
176 
177  /* clip the basewindings by all the other planes */
178  for (i = 0; i < 6; i++) {
179  for (j = 0; j < 6; j++) {
180  if (j == i)
181  continue;
182  ChopWindingInPlace(&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
183  }
184  }
185 }
186 
187 #define BASE_WINDING_EPSILON 0.001
188 #define SPLIT_WINDING_EPSILON 0.001
189 
191 {
192  node_t* n;
193  winding_t* w;
194 
196  , mapplanes[node->planenum].dist);
197 
198  /* clip by all the parents */
199  for (n = node->parent; n && w;) {
200  const plane_t* plane = &mapplanes[n->planenum];
201 
202  if (n->children[0] == node) { /* take front */
203  ChopWindingInPlace(&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
204  } else { /* take back */
205  const vec_t dist = -plane->dist;
206  vec3_t normal;
207  VectorSubtract(vec3_origin, plane->normal, normal);
208  ChopWindingInPlace(&w, normal, dist, BASE_WINDING_EPSILON);
209  }
210  node = n;
211  n = n->parent;
212  }
213 
214  return w;
215 }
216 
217 /*============================================================ */
218 
223 static void MakeNodePortal (node_t* node)
224 {
225  portal_t* new_portal, *p;
226  winding_t* w;
227  vec3_t normal;
228  float dist = 0.0f;
229  int side = 0;
230 
231  w = BaseWindingForNode(node);
232 
233  /* clip the portal by all the other portals in the node */
234  for (p = node->portals; p && w; p = p->next[side]) {
235  if (p->nodes[0] == node) {
236  side = 0;
237  VectorCopy(p->plane.normal, normal);
238  dist = p->plane.dist;
239  } else if (p->nodes[1] == node) {
240  side = 1;
241  VectorSubtract(vec3_origin, p->plane.normal, normal);
242  dist = -p->plane.dist;
243  } else
244  Sys_Error("CutNodePortals_r: mislinked portal");
245 
246  ChopWindingInPlace(&w, normal, dist, 0.1);
247  }
248 
249  if (!w)
250  return;
251 
252  if (WindingIsTiny(w)) {
253  c_tinyportals++;
254  FreeWinding(w);
255  return;
256  }
257 
258  new_portal = AllocPortal();
259  new_portal->plane = mapplanes[node->planenum];
260  new_portal->onnode = node;
261  new_portal->winding = w;
262  AddPortalToNodes(new_portal, node->children[0], node->children[1]);
263 }
264 
265 
270 static void SplitNodePortals (node_t* node)
271 {
272  portal_t* p, *next_portal, *new_portal;
273  node_t* f, *b;
274  int side = 0;
275  plane_t* plane;
276  winding_t* frontwinding, *backwinding;
277 
278  plane = &mapplanes[node->planenum];
279  f = node->children[0];
280  b = node->children[1];
281 
282  for (p = node->portals; p; p = next_portal) {
283  if (p->nodes[0] == node)
284  side = 0;
285  else if (p->nodes[1] == node)
286  side = 1;
287  else
288  Sys_Error("SplitNodePortals: mislinked portal");
289  next_portal = p->next[side];
290 
291  node_t* other_node = p->nodes[!side];
292  RemovePortalFromNode(p, p->nodes[0]);
293  RemovePortalFromNode(p, p->nodes[1]);
294 
295  /* cut the portal into two portals, one on each side of the cut plane */
296  ClipWindingEpsilon(p->winding, plane->normal, plane->dist,
297  SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
298 
299  if (frontwinding && WindingIsTiny(frontwinding)) {
300  FreeWinding(frontwinding);
301  frontwinding = nullptr;
302  c_tinyportals++;
303  }
304 
305  if (backwinding && WindingIsTiny(backwinding)) {
306  FreeWinding(backwinding);
307  backwinding = nullptr;
308  c_tinyportals++;
309  }
310 
311  if (!frontwinding && !backwinding) { /* tiny windings on both sides */
312  continue;
313  }
314 
315  if (!frontwinding) {
316  FreeWinding(backwinding);
317  if (side == 0)
318  AddPortalToNodes(p, b, other_node);
319  else
320  AddPortalToNodes(p, other_node, b);
321  continue;
322  }
323  if (!backwinding) {
324  FreeWinding(frontwinding);
325  if (side == 0)
326  AddPortalToNodes(p, f, other_node);
327  else
328  AddPortalToNodes(p, other_node, f);
329  continue;
330  }
331 
332  /* the winding is split */
333  new_portal = AllocPortal();
334  *new_portal = *p;
335  new_portal->winding = backwinding;
336  FreeWinding(p->winding);
337  p->winding = frontwinding;
338 
339  if (side == 0) {
340  AddPortalToNodes(p, f, other_node);
341  AddPortalToNodes(new_portal, b, other_node);
342  } else {
343  AddPortalToNodes(p, other_node, f);
344  AddPortalToNodes(new_portal, other_node, b);
345  }
346  }
347 
348  node->portals = nullptr;
349 }
350 
351 
352 static void CalcNodeBounds (node_t* node)
353 {
354  int s;
355 
356  /* calc mins/maxs for both leafs and nodes */
357  node->nBox.setNegativeVolume();
358  for (portal_t* p = node->portals; p; p = p->next[s]) {
359  s = (p->nodes[1] == node);
360  if (!p->winding)
361  continue;
362  for (int i = 0; i < p->winding->numpoints; i++)
363  node->nBox.add(p->winding->p[i]);
364  }
365 }
366 
367 
368 static void MakeTreePortals_r (node_t* node)
369 {
370  CalcNodeBounds(node);
371  if (node->nBox.mins[0] >= node->nBox.maxs[0]) {
372  Com_Printf("WARNING: node without a volume\n");
373  }
374 
375  for (int i = 0; i < 3; i++) {
376  if (node->nBox.mins[i] < -MAX_WORLD_WIDTH || node->nBox.maxs[i] > MAX_WORLD_WIDTH) {
377  Com_Printf("WARNING: node with unbounded volume %i\n", (int)node->nBox.mins[i]);
378  break;
379  }
380  }
381  if (node->planenum == PLANENUM_LEAF)
382  return;
383 
384  MakeNodePortal(node);
385  SplitNodePortals(node);
386 
387  MakeTreePortals_r(node->children[0]);
388  MakeTreePortals_r(node->children[1]);
389 }
390 
392 {
393  MakeHeadnodePortals(tree);
395 }
396 
400 static void FindPortalSide (portal_t* p)
401 {
402  /* decide which content change is strongest
403  * solid > water, etc */
404  uint32_t viscontents = VisibleContents(p->nodes[0]->contentFlags ^ p->nodes[1]->contentFlags);
405  if (!viscontents)
406  return;
407 
408  int planenum = p->onnode->planenum;
409  side_t* bestside = nullptr;
410  float bestdot = 0;
411 
412  for (int j = 0; j < 2; j++) {
413  const node_t* n = p->nodes[j];
414  const plane_t* p1 = &mapplanes[p->onnode->planenum];
415  for (bspbrush_t* bb = n->brushlist; bb; bb = bb->next) {
416  const mapbrush_t* brush = bb->original;
417 
418  if (!(brush->contentFlags & viscontents))
419  continue;
420  for (int i = 0; i < brush->numsides; i++) {
421  side_t* side = &brush->original_sides[i];
422  float dot;
423  const plane_t* p2;
424 
425  if (side->bevel)
426  continue;
427  /* non-visible */
428  if (side->texinfo == TEXINFO_NODE)
429  continue;
430  /* exact match */
431  if ((side->planenum &~ 1) == planenum) {
432  bestside = &brush->original_sides[i];
433  goto gotit;
434  }
435  /* see how close the match is */
436  p2 = &mapplanes[side->planenum &~ 1];
437  dot = DotProduct(p1->normal, p2->normal);
438  if (dot > bestdot) {
439  bestdot = dot;
440  bestside = side;
441  }
442  }
443  }
444  }
445 
446 gotit:
447  if (!bestside)
448  Verb_Printf(VERB_EXTRA, "WARNING: side not found for portal\n");
449 
450  p->sidefound = true;
451  p->side = bestside;
452 }
453 
454 
455 static void MarkVisibleSides_r (node_t* node)
456 {
457  portal_t* p;
458  int s;
459 
460  if (node->planenum != PLANENUM_LEAF) {
461  MarkVisibleSides_r(node->children[0]);
462  MarkVisibleSides_r(node->children[1]);
463  return;
464  }
465 
466  /* empty leafs are never boundary leafs */
467  if (!node->contentFlags)
468  return;
469 
470  /* see if there is a visible face */
471  for (p = node->portals; p; p = p->next[!s]) {
472  s = (p->nodes[0] == node);
473  if (!p->onnode)
474  continue; /* edge of world */
475  if (!p->sidefound)
476  FindPortalSide(p);
477  if (p->side)
478  p->side->visible = true;
479  }
480 }
481 
482 void MarkVisibleSides (tree_t* tree, int startbrush, int endbrush)
483 {
484  Verb_Printf(VERB_EXTRA, "--- MarkVisibleSides ---\n");
485 
486  /* clear all the visible flags */
487  for (int i = startbrush; i < endbrush; i++) {
488  mapbrush_t* mb = &mapbrushes[i];
489  const int numsides = mb->numsides;
490  for (int j = 0; j < numsides; j++)
491  mb->original_sides[j].visible = false;
492  }
493 
494  /* set visible flags on the sides that are used by portals */
496 }
struct node_s * onnode
Definition: map.h:112
static void FindPortalSide(portal_t *p)
Finds a brush side to use for texturing the given portal.
Definition: portals.cpp:400
int32_t contentFlags
Definition: bsp.h:56
vec3_t normal
Definition: map.h:99
static void MarkVisibleSides_r(node_t *node)
Definition: portals.cpp:455
void RemovePortalFromNode(portal_t *portal, node_t *l)
Removes references to the given portal from the given node.
Definition: portals.cpp:102
#define VectorCopy(src, dest)
Definition: vector.h:51
void setNegativeVolume()
Sets mins and maxs to their starting points before using addPoint.
Definition: aabb.h:98
void Sys_Error(const char *error,...)
Definition: g_main.cpp:421
int texinfo
Definition: map.h:62
#define LAST_VISIBLE_CONTENTS
Definition: defines.h:230
bool bevel
Definition: map.h:69
static int c_active_portals
Definition: portals.cpp:32
static void MakeHeadnodePortals(tree_t *tree)
The created portals will face the global outside_node.
Definition: portals.cpp:137
plane_t plane
Definition: map.h:111
Definition: bsp.h:61
const vec3_t vec3_origin
Definition: mathlib.cpp:35
struct portal_s * next[2]
Definition: map.h:114
float vec_t
Definition: ufotypes.h:37
static void AddPortalToNodes(portal_t *p, node_t *front, node_t *back)
Links a portal into the given back and front nodes.
Definition: portals.cpp:82
void ChopWindingInPlace(winding_t **inout, const vec3_t normal, const vec_t dist, const vec_t epsilon)
Definition: polylib.cpp:299
winding_t * winding
Definition: map.h:63
int numsides
Definition: map.h:83
static void MakeTreePortals_r(node_t *node)
Definition: portals.cpp:368
static int c_peak_portals
Definition: portals.cpp:33
winding_t * winding
Definition: map.h:115
uint32_t VisibleContents(uint32_t contentFlags)
Returns the single content bit of the strongest visible content present.
Definition: portals.cpp:64
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
vec3_t maxs
Definition: aabb.h:258
static winding_t * BaseWindingForNode(node_t *node)
Definition: portals.cpp:190
int32_t planenum
Definition: bsp.h:44
#define BASE_WINDING_EPSILON
Definition: portals.cpp:187
mapbrush_t mapbrushes[MAX_MAP_BRUSHES]
Definition: map.cpp:34
Definition: map.h:98
void add(const vec3_t point)
If the point is outside the box, expand the box to accommodate it.
Definition: aabb.cpp:57
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition: vector.h:44
struct node_s * children[2]
Definition: bsp.h:51
#define OBJZERO(obj)
Definition: shared.h:178
void ClipWindingEpsilon(const winding_t *in, const vec3_t normal, const vec_t dist, const vec_t epsilon, winding_t **front, winding_t **back)
Definition: polylib.cpp:204
#define SPLIT_WINDING_EPSILON
Definition: portals.cpp:188
struct node_s * nodes[2]
Definition: map.h:113
uint16_t planenum
Definition: map.h:61
threadstate_t threadstate
Definition: threads.cpp:32
static void MakeNodePortal(node_t *node)
Create the new portal by taking the full plane winding for the cutting plane and clipping it by all o...
Definition: portals.cpp:223
struct side_s * original_sides
Definition: map.h:84
struct mapbrush_s * brush
Definition: map.h:72
#define SIDESPACE
Definition: portals.cpp:133
for storing the vertices of the side of a brush or other polygon
Definition: polylib.h:30
bool visible
Definition: map.h:67
bool WindingIsTiny(winding_t *w)
Returns true if the winding would be crunched out of existance by the vertex snapping.
Definition: polylib.cpp:407
plane_t mapplanes[MAX_MAP_PLANES]
Definition: map.cpp:43
struct side_s * side
Definition: map.h:118
static int c_tinyportals
Definition: portals.cpp:34
void Verb_Printf(const verbosityLevel_t importance, const char *format,...) __attribute__((format(__printf__
Definition: map.h:60
struct portal_s * portals
Definition: bsp.h:58
struct bspbrush_s * next
Definition: bspbrush.h:36
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
uint32_t contentFlags
Definition: map.h:65
void FreePortal(portal_t *p)
Definition: portals.cpp:52
#define PLANENUM_LEAF
Definition: defines.h:45
struct node_s * headnode
Definition: bsp.h:62
AABB nBox
Definition: bsp.h:46
QGL_EXTERN GLint i
Definition: r_gl.h:113
struct node_s * parent
Definition: bsp.h:45
bspbrush_t * brushlist
Definition: bsp.h:55
vec_t dist
Definition: map.h:100
AABB aabb
Definition: bsp.h:64
Definition: map.h:75
#define Mem_Free(ptr)
Definition: mem.h:35
void MarkVisibleSides(tree_t *tree, int startbrush, int endbrush)
Definition: portals.cpp:482
winding_t * BaseWindingForPlane(const vec3_t normal, const vec_t dist)
Definition: polylib.cpp:116
vec_t vec3_t[3]
Definition: ufotypes.h:39
#define MAX_WORLD_WIDTH
-MAX_WORLD_WIDTH up tp +MAX_WORLD_WIDTH
Definition: defines.h:288
static void SplitNodePortals(node_t *node)
Move or split the portals that bound node so that the node's children have portals instead of node...
Definition: portals.cpp:270
#define Mem_AllocType(type)
Definition: mem.h:39
vec3_t mins
Definition: aabb.h:257
Definition: map.h:110
struct node_s outside_node
Definition: bsp.h:63
Definition: bsp.h:42
static void CalcNodeBounds(node_t *node)
Definition: portals.cpp:352
static portal_t * AllocPortal(void)
Definition: portals.cpp:39
#define TEXINFO_NODE
Definition: defines.h:48
bool sidefound
Definition: map.h:117
#define VectorSubtract(a, b, dest)
Definition: vector.h:45
uint32_t contentFlags
Definition: map.h:79
void MakeTreePortals(tree_t *tree)
Definition: portals.cpp:391
void FreeWinding(winding_t *w)
Definition: polylib.cpp:46
#define ON_EPSILON
Definition: defines.h:374