UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cmodel.cpp
Go to the documentation of this file.
1 
7 /*
8 Copyright (C) 1997-2001 Id Software, Inc.
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 
19 See the GNU General Public License for more details.
20 
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 
25 */
26 
27 #include "common.h"
28 #include "tracing.h"
29 
30 /*
31 ===============================================================================
32 GAME RELATED TRACING USING ENTITIES
33 ===============================================================================
34 */
35 
41 static void CM_CalculateWidestBoundingBox (const cBspModel_t* model, AABB& box)
42 {
43  /* Quickly calculate the bounds of this model to see if they can overlap. */
44  box.set(model->cbmBox);
45  box.shift(model->origin);
46  if (VectorNotEmpty(model->angles)) {
47  const float offset = std::max(std::max(box.getWidthX(), box.getWidthY()), box.getWidthZ()) / 2.0f;
48  box.expand(offset); /* expand the whole box by the highest extent we found */
49  }
50 }
51 
58 static bool CM_LineMissesModel (const Line& tLine, const cBspModel_t* model)
59 {
60  AABB absbox;
61  CM_CalculateWidestBoundingBox(model, absbox);
62  /* If the bounds of the extents box and the line do not overlap, then skip tracing this model. */
63  if (!absbox.canBeHitBy(tLine))
64  return true; /* impossible */
65 
66  return false; /* maybe */
67 }
68 
69 
84 trace_t CM_HintedTransformedBoxTrace (MapTile& tile, const Line& traceLine, const AABB& traceBox, const int headnode, const int contentmask, const int brushrejects, const vec3_t origin, const vec3_t angles, const vec3_t rmaShift, const float fraction)
85 {
86  vec3_t start_l, end_l;
87  vec3_t forward, right, up;
88  vec3_t temp;
89  bool rotated;
90 
91  /* subtract origin offset */
92  VectorSubtract(traceLine.start, origin, start_l);
93  VectorSubtract(traceLine.stop, origin, end_l);
94 
95  /* rotate start and end into the models frame of reference */
96  if (headnode != tile.box_headnode && VectorNotEmpty(angles)) {
97  rotated = true;
98  } else {
99  rotated = false;
100  }
101 
102  if (rotated) {
103  AngleVectors(angles, forward, right, up);
104 
105  VectorCopy(start_l, temp);
106  start_l[0] = DotProduct(temp, forward);
107  start_l[1] = -DotProduct(temp, right);
108  start_l[2] = DotProduct(temp, up);
109 
110  VectorCopy(end_l, temp);
111  end_l[0] = DotProduct(temp, forward);
112  end_l[1] = -DotProduct(temp, right);
113  end_l[2] = DotProduct(temp, up);
114  }
115 
116  /* When tracing through a model, we want to use the nodes, planes etc. as calculated by ufo2map.
117  * But nodes and planes have been shifted in case of an RMA. At least for doors we need to undo the shift. */
118  if (VectorNotEmpty(origin)) { /* only doors seem to have their origin set */
119  VectorAdd(start_l, rmaShift, start_l); /* undo the shift */
120  VectorAdd(end_l, rmaShift, end_l);
121  }
122 
123  /* sweep the box through the model */
124  boxtrace_t traceData;
125  traceData.init(&tile, contentmask, brushrejects, fraction);
126  traceData.setLineAndBox(Line(start_l, end_l), traceBox);
127  trace_t trace = TR_BoxTrace(traceData, Line(start_l, end_l), traceBox, headnode, fraction);
128  trace.mapTile = tile.idx;
129 
130  if (rotated && trace.fraction != 1.0f) {
131  vec3_t a;
133  VectorNegate(angles, a);
134  AngleVectors(a, forward, right, up);
135 
136  VectorCopy(trace.plane.normal, temp);
137  trace.plane.normal[0] = DotProduct(temp, forward);
138  trace.plane.normal[1] = -DotProduct(temp, right);
139  trace.plane.normal[2] = DotProduct(temp, up);
140  }
141 
142  VectorInterpolation(traceLine.start, traceLine.stop, trace.fraction, trace.endpos);
143 
144  return trace;
145 }
146 
151 int32_t CM_HeadnodeForBox (MapTile& tile, const AABB& box)
152 {
153  tile.box_planes[0].dist = box.maxs[0];
154  tile.box_planes[1].dist = -box.maxs[0];
155  tile.box_planes[2].dist = box.mins[0];
156  tile.box_planes[3].dist = -box.mins[0];
157  tile.box_planes[4].dist = box.maxs[1];
158  tile.box_planes[5].dist = -box.maxs[1];
159  tile.box_planes[6].dist = box.mins[1];
160  tile.box_planes[7].dist = -box.mins[1];
161  tile.box_planes[8].dist = box.maxs[2];
162  tile.box_planes[9].dist = -box.maxs[2];
163  tile.box_planes[10].dist = box.mins[2];
164  tile.box_planes[11].dist = -box.mins[2];
165 
166  assert(tile.box_headnode < MAX_MAP_NODES);
167  return tile.box_headnode;
168 }
169 
170 /* TRACING FUNCTIONS */
171 
184 bool CM_EntTestLine (mapTiles_t* mapTiles, const Line& traceLine, const int levelmask, const char** entlist)
185 {
186  /* trace against world first */
187  if (TR_TestLine(mapTiles, traceLine.start, traceLine.stop, levelmask))
188  /* We hit the world, so we didn't make it anyway... */
189  return true;
190 
191  /* no local models, so we made it. */
192  if (!entlist)
193  return false;
194 
195  for (const char** name = entlist; *name; name++) {
196  /* check whether this is really an inline model */
197  if (*name[0] != '*')
198  Com_Error(ERR_DROP, "name in the inlineList is no inline model: '%s'", *name);
199  const cBspModel_t* model = CM_InlineModel(mapTiles, *name);
200  assert(model);
201  if (model->headnode >= mapTiles->mapTiles[model->tile].numnodes + 6)
202  continue;
203 
204  /* check if we can safely exclude that the trace can hit the model */
205  if (CM_LineMissesModel(traceLine, model))
206  continue;
207 
208  const trace_t trace = CM_HintedTransformedBoxTrace(mapTiles->mapTiles[model->tile], traceLine, AABB(),
209  model->headnode, MASK_VISIBILILITY, 0, model->origin, model->angles, model->shift, 1.0f);
210  /* if we started the trace in a wall */
211  /* or the trace is not finished */
212  if (trace.startsolid || trace.fraction < 1.0f)
213  return true;
214  }
215 
216  /* not blocked */
217  return false;
218 }
219 
230 bool CM_EntTestLineDM (mapTiles_t* mapTiles, const Line& trLine, vec3_t hit, const int levelmask, const char** entlist)
231 {
232  float fraction = 2.0f;
233 
234  /* trace against world first */
235  bool blocked = TR_TestLineDM(mapTiles, trLine.start, trLine.stop, hit, levelmask);
236  if (!entlist)
237  return blocked;
238 
239  for (const char** name = entlist; *name; name++) {
240  /* check whether this is really an inline model */
241  if (*name[0] != '*') {
242  /* Let's see what the data looks like... */
243  Com_Error(ERR_DROP, "name in the inlineList is no inline model: '%s' (inlines: %p, name: %p)",
244  *name, (void*)entlist, (void*)name);
245  }
246  const cBspModel_t* model = CM_InlineModel(mapTiles, *name);
247  assert(model);
248  if (model->headnode >= mapTiles->mapTiles[model->tile].numnodes + 6)
249  continue;
250 
251  /* check if we can safely exclude that the trace can hit the model */
252  if (CM_LineMissesModel(trLine, model))
253  continue;
254 
255  const trace_t trace = CM_HintedTransformedBoxTrace(mapTiles->mapTiles[model->tile], Line(trLine.start, hit), AABB(),
256  model->headnode, MASK_ALL, 0, model->origin, model->angles, vec3_origin, fraction);
257  /* if we started the trace in a wall */
258  if (trace.startsolid) {
259  VectorCopy(trLine.start, hit);
260  return true;
261  }
262  /* trace not finished */
263  if (trace.fraction < fraction) {
264  blocked = true;
265  fraction = trace.fraction;
266  VectorCopy(trace.endpos, hit);
267  }
268  }
269 
270  /* return result */
271  return blocked;
272 }
273 
283 trace_t CM_CompleteBoxTrace (mapTiles_t* mapTiles, const Line& trLine, const AABB& box, int levelmask, int brushmask, int brushreject)
284 {
285  trace_t tr;
286  tr.fraction = 2.0f;
287  VectorCopy(trLine.stop, tr.endpos); /* optimistically set the endpos just in case we are outside of ALL the tiles, so TR_TileBoxTrace won't do it */
288 
289  /* create a box that covers the whole volume of the trace */
290  AABB moveBox;
291  moveBox.set(box, trLine);
292 
293  /* trace against all loaded map tiles */
294  for (int tile = 0; tile < mapTiles->numTiles; tile++) {
295  MapTile& myTile = mapTiles->mapTiles[tile];
296  AABB tileBox;
297  myTile.getTileBox(tileBox);
298  /* If the trace is completely outside of the tile, then skip it. */
299  if (!moveBox.doesIntersect(tileBox))
300  continue;
301  trace_t newtr = TR_TileBoxTrace(&myTile, trLine, box, levelmask, brushmask, brushreject);
302  newtr.mapTile = tile;
303 
304  /* memorize the trace with the minimal fraction */
305  if (newtr.fraction == 0.0f)
306  return newtr;
307  if (newtr.fraction < tr.fraction)
308  tr = newtr;
309  }
310  return tr;
311 }
312 
313 
327 trace_t CM_EntCompleteBoxTrace (mapTiles_t* mapTiles, const Line& traceLine, const AABB* traceBox, int levelmask, int brushmask, int brushreject, const char** list)
328 {
329  AABB lineBox;
330  lineBox.set(*traceBox, traceLine);
331  /* Now lineBox specifies the whole volume involved in the trace. */
332 
333  /* reconstruct a levelmask */
334  const vec_t minZ = lineBox.getMinZ();
335  const vec_t maxZ = lineBox.getMaxZ();
336  int newLevelMask = 0;
337  if (levelmask & TL_FLAG_ACTORCLIP) /* if the passed levelmask contains the bit for the cliplevels, */
338  newLevelMask = TL_FLAG_ACTORCLIP; /* preserve it */
339  for (int i = 0; i < PATHFINDING_HEIGHT; i++) {
340  const vec_t lower = i * UNIT_HEIGHT; /* the height bounds of the level */
341  const vec_t upper = (i + 1) * UNIT_HEIGHT;
342  if (minZ > upper || maxZ < lower)
343  continue;
344  newLevelMask |= (1 << i);
345  }
346 
347  /* trace against world first */
348  const trace_t tr = CM_CompleteBoxTrace(mapTiles, traceLine, *traceBox, newLevelMask, brushmask, brushreject);
349  if (!list || tr.fraction == 0.0f)
350  return tr;
351 
352  trace_t trace = tr;
353  for (const char** name = list; *name; name++) {
354  /* check whether this is really an inline model */
355  if (*name[0] != '*')
356  Com_Error(ERR_DROP, "name in the inlineList is no inline model: '%s'", *name);
357  const cBspModel_t* model = CM_InlineModel(mapTiles, *name);
358  assert(model);
359  if (model->headnode >= mapTiles->mapTiles[model->tile].numnodes + 6)
360  continue;
361 
362  AABB modelBox;
363  /* Quickly calculate the bounds of this model to see if they can overlap. */
364  CM_CalculateWidestBoundingBox(model, modelBox);
365 
366  /* If the bounds of the extents box and the line do not overlap, then skip tracing this model. */
367  if (!lineBox.doesIntersect(modelBox))
368  continue;
369 
370  const trace_t newtr = CM_HintedTransformedBoxTrace(mapTiles->mapTiles[model->tile], traceLine, *traceBox,
371  model->headnode, brushmask, brushreject, model->origin, model->angles, model->shift, trace.fraction);
372 
373  /* memorize the trace with the minimal fraction */
374  if (newtr.fraction == 0.0f)
375  return newtr;
376  if (newtr.fraction < trace.fraction)
377  trace = newtr;
378  }
379  return trace;
380 }
trace_t CM_EntCompleteBoxTrace(mapTiles_t *mapTiles, const Line &traceLine, const AABB *traceBox, int levelmask, int brushmask, int brushreject, const char **list)
Performs box traces against the world and all inline models, gives the hit position back...
Definition: cmodel.cpp:327
trace_t TR_BoxTrace(boxtrace_t &traceData, const Line &traceLine, const AABB &traceBox, const int headnode, const float fraction)
This function traces a line from start to end. It returns a trace_t indicating what portion of the li...
Definition: tracing.cpp:1003
#define VectorCopy(src, dest)
Definition: vector.h:51
void setLineAndBox(const Line &line, const AABB &box)
Definition: tracing.cpp:57
vec3_t origin
Definition: typedefs.h:28
void getTileBox(AABB &box)
Calculate the bounding box for the tile (in mapunits)
Definition: typedefs.h:137
bool canBeHitBy(const Line &line) const
Checks if the given line has a chance to hit our box.
Definition: aabb.h:192
static bool CM_LineMissesModel(const Line &tLine, const cBspModel_t *model)
A quick test if the trace might hit the inline model.
Definition: cmodel.cpp:58
voidpf uLong int origin
Definition: ioapi.h:45
bool doesIntersect(const AABB &other) const
Checks if the aabb touches or intersects with the given aabb.
Definition: aabb.h:183
vec3_t endpos
Definition: tracing.h:59
Definition: aabb.h:42
const vec3_t vec3_origin
Definition: mathlib.cpp:35
#define VectorNegate(src, dest)
Definition: vector.h:58
float getMaxZ() const
Definition: aabb.h:137
float dist
Definition: typedefs.h:22
float vec_t
Definition: ufotypes.h:37
TR_PLANE_TYPE plane
Definition: tracing.h:60
float getMinZ() const
Definition: aabb.h:125
trace_t TR_TileBoxTrace(TR_TILE_TYPE *myTile, const Line &traceLine, const AABB &aabb, const int levelmask, const int brushmask, const int brushreject)
Traces all submodels in the specified tile. Provides for a short circuit if the trace tries to move p...
Definition: tracing.cpp:1067
cBspPlane_t * box_planes
Definition: typedefs.h:116
#define UNIT_HEIGHT
Definition: defines.h:122
void set(const AABB &other)
Copies the values from the given aabb.
Definition: aabb.h:60
bool CM_EntTestLineDM(mapTiles_t *mapTiles, const Line &trLine, vec3_t hit, const int levelmask, const char **entlist)
Checks traces against the world and all inline models, gives the hit position back.
Definition: cmodel.cpp:230
vec3_t maxs
Definition: aabb.h:258
#define MASK_ALL
Definition: defines.h:271
#define MASK_VISIBILILITY
Definition: defines.h:277
int tile
Definition: typedefs.h:31
float fraction
Definition: tracing.h:58
#define TL_FLAG_ACTORCLIP
Definition: defines.h:359
void Com_Error(int code, const char *fmt,...)
Definition: common.cpp:417
Stores the data of a map tile, mostly the BSP stuff.
Definition: typedefs.h:85
vec3_t shift
Definition: typedefs.h:28
void expand(const float byVal)
expand the box in all directions, but clip them to the maximum boundaries
Definition: aabb.h:240
trace_t CM_HintedTransformedBoxTrace(MapTile &tile, const Line &traceLine, const AABB &traceBox, const int headnode, const int contentmask, const int brushrejects, const vec3_t origin, const vec3_t angles, const vec3_t rmaShift, const float fraction)
Handles offseting and rotation of the end points for moving and rotating entities.
Definition: cmodel.cpp:84
void AngleVectors(const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
Create the rotation matrix in order to rotate something.
Definition: mathlib.cpp:631
bool TR_TestLineDM(mapTiles_t *mapTiles, const vec3_t start, const vec3_t end, vec3_t hit, const int levelmask)
Checks traces against the world, gives hit position back.
Definition: tracing.cpp:458
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition: vector.h:44
#define ERR_DROP
Definition: common.h:211
int32_t CM_HeadnodeForBox(MapTile &tile, const AABB &box)
To keep everything totally uniform, bounding boxes are turned into small BSP trees instead of being c...
Definition: cmodel.cpp:151
void init(TR_TILE_TYPE *_tile, const int contentmask, const int brushreject, const float fraction)
Definition: tracing.cpp:46
float getWidthY() const
Definition: aabb.h:144
bool CM_EntTestLine(mapTiles_t *mapTiles, const Line &traceLine, const int levelmask, const char **entlist)
Checks traces against the world and all inline models.
Definition: cmodel.cpp:184
static transfer_t tr
Definition: line.h:31
#define VectorInterpolation(p1, p2, frac, mid)
Definition: vector.h:80
#define VectorNotEmpty(a)
Definition: vector.h:72
vec3_t stop
Definition: line.h:55
vec3_t angles
Definition: typedefs.h:28
AABB cbmBox
Definition: typedefs.h:27
cBspModel_t * CM_InlineModel(const mapTiles_t *mapTiles, const char *name)
Searches all inline models and return the cBspModel_t pointer for the given modelnumber or -name...
Definition: bsp.cpp:929
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
#define PATHFINDING_HEIGHT
15 max, adjusting above 8 will require a rewrite to the DV code
Definition: defines.h:294
#define VectorAdd(a, b, dest)
Definition: vector.h:47
float getWidthX() const
Definition: aabb.h:141
int numTiles
Definition: tracing.h:82
TR_TILE_TYPE mapTiles[MAX_MAPTILES]
Definition: tracing.h:79
QGL_EXTERN GLint i
Definition: r_gl.h:113
vec3_t start
Definition: line.h:54
int idx
Definition: typedefs.h:88
int mapTile
Definition: tracing.h:65
static void CM_CalculateWidestBoundingBox(const cBspModel_t *model, AABB &box)
Calculates the worst case bounding box for the given bsp model.
Definition: cmodel.cpp:41
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
bool TR_TestLine(mapTiles_t *mapTiles, const vec3_t start, const vec3_t end, const int levelmask)
Checks traces against the world.
Definition: tracing.cpp:310
bool startsolid
Definition: tracing.h:57
vec_t vec3_t[3]
Definition: ufotypes.h:39
definitions common between client and server, but not game lib
void shift(const vec3_t shiftVec)
shove the whole box by the given vector
Definition: aabb.h:246
int32_t headnode
Definition: typedefs.h:29
vec3_t mins
Definition: aabb.h:257
voidpf uLong offset
Definition: ioapi.h:45
int box_headnode
Definition: typedefs.h:117
#define VectorSubtract(a, b, dest)
Definition: vector.h:45
Tracing functions.
trace_t CM_CompleteBoxTrace(mapTiles_t *mapTiles, const Line &trLine, const AABB &box, int levelmask, int brushmask, int brushreject)
Traces all submodels in all tiles. Used by ufo and ufo_ded.
Definition: cmodel.cpp:283
static mapTiles_t mapTiles
float getWidthZ() const
Definition: aabb.h:147
#define MAX_MAP_NODES
Definition: defines.h:140