1 /**
2  * @file
3  * @brief model tracing and bounding
4  * @note collision detection code
5  */
6 
7 /*
8 All original material Copyright (C) 2002-2013 UFO: Alien Invasion.
9 
10 Copyright (C) 1997-2001 Id Software, Inc.
11 
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 as published by the Free Software Foundation; either version 2
15 of the License, or (at your option) any later version.
16 
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 
21 See the GNU General Public License for more details.
22 
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26 
27 */
28 
29 #include "tracing.h"
30 #include "common.h"
31 
32 /* TR_TILE_TYPE	and TR_PLANE_TYPE are defined in tracing.h */
33 #if defined(COMPILE_MAP)
34   #define TR_NODE_TYPE			dBspNode_t
35   #define TR_LEAF_TYPE			dBspLeaf_t
36   #define TR_BRUSHSIDE_TYPE		dBspBrushSide_t
37 #elif defined(COMPILE_UFO)
38   #define TR_NODE_TYPE			cBspNode_t
39   #define TR_LEAF_TYPE			cBspLeaf_t
40   #define TR_BRUSHSIDE_TYPE		cBspBrushSide_t
41 #else
42   #error Either COMPILE_MAP or COMPILE_UFO must be defined in order for tracing.c to work.
43 #endif
44 /** @note all the above types are declared in typedefs.h */
45 
46 /* This attempts to make the box tracing code thread safe. */
47 typedef struct boxtrace_s {
48 	vec3_t start, end;
49 	vec3_t mins, maxs;
50 	vec3_t absmins, absmaxs;
51 	vec3_t extents;
52 
53 	trace_t trace;
54 	uint32_t contents;			/**< content flags to match again - MASK_ALL to match everything */
55 	uint32_t rejects;			/**< content flags that should be rejected in a trace - ignored when MASK_ALL is given as content flags */
56 	bool ispoint;				/* optimized case */
57 
58 	TR_TILE_TYPE *tile;
59 } boxtrace_t;
60 
61 /** @note For multi-check avoidance.
62  * @todo not thread safe */
63 static int checkcount;
64 
65 /*
66 ===============================================================================
67 TRACING NODES
68 ===============================================================================
69 */
70 
71 /**
72  * @brief Converts the disk node structure into the efficient tracing structure for LineTraces
73  */
TR_MakeTracingNode(TR_TILE_TYPE * tile,tnode_t ** tnode,int32_t nodenum)74 static void TR_MakeTracingNode (TR_TILE_TYPE *tile, tnode_t**  tnode, int32_t nodenum)
75 {
76 	tnode_t* t;				/* the tracing node to build */
77 	TR_PLANE_TYPE *plane;
78 	int i;
79 	TR_NODE_TYPE *node;		/* the node we are investigating */
80 
81 	t = (*tnode)++;
82 
83 	node = tile->nodes + nodenum;
84 #ifdef COMPILE_UFO
85 	plane = node->plane;
86 #else
87 	plane = tile->planes + node->planenum;
88 #endif
89 
90 	t->type = plane->type;
91 	VectorCopy(plane->normal, t->normal);
92 	t->dist = plane->dist;
93 
94 	for (i = 0; i < 2; i++) {
95 		if (node->children[i] < 0) {	/* is it a leaf ? */
96 			const int32_t leafnum = -(node->children[i]) - 1;
97 			const TR_LEAF_TYPE *leaf = &tile->leafs[leafnum];
98 			const uint32_t contentFlags = leaf->contentFlags & ~(1 << 31);
99 			if ((contentFlags & (CONTENTS_SOLID | MASK_CLIP)) && !(contentFlags & CONTENTS_PASSABLE))
100 				t->children[i] = -node->children[i] | (1 << 31);	/* mark as 'blocking' */
101 			else
102 				t->children[i] = (1 << 31);				/* mark as 'empty leaf' */
103 		} else {										/* not a leaf */
104 			t->children[i] = *tnode - tile->tnodes;
105 			if (t->children[i] > tile->numnodes) {
106 				Com_Printf("Exceeded allocated memory for tracing structure (%i > %i)\n",
107 						t->children[i], tile->numnodes);
108 			}
109 			TR_MakeTracingNode(tile, tnode, node->children[i]);		/* recurse further down the tree */
110 		}
111 	}
112 }
113 
114 /**
115  * @sa CMod_LoadNodes
116  * @sa R_ModLoadNodes
117  */
TR_BuildTracingNode_r(TR_TILE_TYPE * tile,tnode_t ** tnode,int32_t nodenum,int level)118 void TR_BuildTracingNode_r (TR_TILE_TYPE *tile, tnode_t**  tnode, int32_t nodenum, int level)
119 {
120 	assert(nodenum < tile->numnodes + 6); /* +6 => bbox */
121 
122 	/**
123 	 *  We are checking for a leaf in the tracing node.  For ufo2map, planenum == PLANENUMLEAF.
124 	 *  For the game, plane will be nullptr.
125 	 */
126 #ifdef COMPILE_UFO
127 	if (!tile->nodes[nodenum].plane) {
128 #else
129 	if (tile->nodes[nodenum].planenum == PLANENUM_LEAF) {
130 #endif
131 		TR_NODE_TYPE *n;
132 		tnode_t* t;
133 		vec3_t c0maxs, c1mins;
134 		int i;
135 
136 		n = &tile->nodes[nodenum];
137 
138 		/* alloc new node */
139 		t = (*tnode)++;
140 
141 		/* no leafs here */
142 		if (n->children[0] < 0 || n->children[1] < 0)
143 #ifdef COMPILE_UFO
144 			Com_Error(ERR_DROP, "Unexpected leaf");
145 #else
146 			Sys_Error("Unexpected leaf");
147 #endif
148 
149 		VectorCopy(tile->nodes[n->children[0]].maxs, c0maxs);
150 		VectorCopy(tile->nodes[n->children[1]].mins, c1mins);
151 
152 #if 0
153 		Com_Printf("(%i %i : %i %i) (%i %i : %i %i)\n",
154 			(int)tile->nodes[n->children[0]].mins[0], (int)tile->nodes[n->children[0]].mins[1],
155 			(int)tile->nodes[n->children[0]].maxs[0], (int)tile->nodes[n->children[0]].maxs[1],
156 			(int)tile->nodes[n->children[1]].mins[0], (int)tile->nodes[n->children[1]].mins[1],
157 			(int)tile->nodes[n->children[1]].maxs[0], (int)tile->nodes[n->children[1]].maxs[1]);
158 #endif
159 
160 		for (i = 0; i < 2; i++)
161 			if (c0maxs[i] <= c1mins[i]) {
162 				/* create a separation plane */
163 				t->type = i;
164 				VectorSet(t->normal, i, (i ^ 1), 0);
165 				t->dist = (c0maxs[i] + c1mins[i]) / 2;
166 
167 				t->children[1] = *tnode - tile->tnodes;
168 				TR_BuildTracingNode_r(tile, tnode, n->children[0], level);
169 				t->children[0] = *tnode - tile->tnodes;
170 				TR_BuildTracingNode_r(tile, tnode, n->children[1], level);
171 				return;
172 			}
173 
174 		/* can't construct such a separation plane */
175 		t->type = PLANE_NONE;
176 
177 		for (i = 0; i < 2; i++) {
178 			t->children[i] = *tnode - tile->tnodes;
179 			TR_BuildTracingNode_r(tile, tnode, n->children[i], level);
180 		}
181 	} else {
182 		/* Make a lookup table */
183 		tile->cheads[tile->numcheads].cnode = nodenum;
184 		tile->cheads[tile->numcheads].level = level;
185 		tile->numcheads++;
186 		assert(tile->numcheads <= MAX_MAP_NODES);
187 		/* Make the tracing node. */
188 		TR_MakeTracingNode(tile, tnode, nodenum);
189 	}
190 }
191 
192 
193 /*
194 ===============================================================================
195 LINE TRACING - TEST FOR BRUSH PRESENCE
196 ===============================================================================
197 */
198 
199 
200 /**
201  * @param[in] tile The map tile containing the structures to be traced.
202  * @param[in] nodenum Node index
203  * @param[in] start The position to start the trace.
204  * @param[in] end The position where the trace ends.
205  * @return zero if the line is not blocked, else a positive value
206  * @sa TR_TestLineDist_r
207  * @sa CM_TestLine
208  */
209 int TR_TestLine_r (TR_TILE_TYPE *tile, int32_t nodenum, const vec3_t start, const vec3_t end)
210 {
211 	tnode_t* tnode;
212 	float front, back;
213 	int r;
214 
215 	/* negative numbers indicate leaf nodes. Empty leaf nodes are marked as (1 << 31).
216 	 * Turning off that bit makes us return 0 or the positive node number to indicate blocking. */
217 	if (nodenum & (1 << 31))
218 		return nodenum & ~(1 << 31);
219 
220 	tnode = &tile->tnodes[nodenum];
221 	assert(tnode);
222 	switch (tnode->type) {
223 	case PLANE_X:
224 	case PLANE_Y:
225 	case PLANE_Z:
226 		front = start[tnode->type] - tnode->dist;
227 		back = end[tnode->type] - tnode->dist;
228 		break;
229 	case PLANE_NONE:
230 		r = TR_TestLine_r(tile, tnode->children[0], start, end);
231 		if (r)
232 			return r;
233 		return TR_TestLine_r(tile, tnode->children[1], start, end);
234 	default:
235 		front = DotProduct(start, tnode->normal) - tnode->dist;
236 		back = DotProduct(end, tnode->normal) - tnode->dist;
237 		break;
238 	}
239 
240 	if (front >= -ON_EPSILON && back >= -ON_EPSILON)
241 		return TR_TestLine_r(tile, tnode->children[0], start, end);
242 	else if (front < ON_EPSILON && back < ON_EPSILON)
243 		return TR_TestLine_r(tile, tnode->children[1], start, end);
244 	else {
245 		const int side = front < 0;
246 		const float frac = front / (front - back);
247 		vec3_t mid;
248 
249 		VectorInterpolation(start, end, frac, mid);
250 
251 		r = TR_TestLine_r(tile, tnode->children[side], start, mid);
252 		if (r)
253 			return r;
254 		return TR_TestLine_r(tile, tnode->children[!side], mid, end);
255 	}
256 }
257 
258 
259 /**
260  * @brief Tests to see if a line intersects any brushes in a tile.
261  * @param[in] tile The map tile containing the structures to be traced.
262  * @param[in] start The position to start the trace.
263  * @param[in] end The position where the trace ends.
264  * @param[in] levelmask
265  * @return true if the line is blocked
266  * @note This function uses levels and levelmasks.  The levels are as following:
267  * 0-255: brushes are assigned to a level based on their assigned viewing levels.  A brush with
268  *    no levels assigned will be stuck in 0, a brush viewable from all 8 levels will be in 255, and
269  *    so on.  Each brush will only appear in one level.
270  * 256: weaponclip-level
271  * 257: actorclip-level
272  *
273  * The levelmask is used to determine which levels AND which, if either, clip to trace through.
274  * The mask bits are as follows:
275  * 0x0FF: Level bits.  If any bits are set then a brush's level ANDed with the levelmask, then
276  *     that level is traced.  It could possibly be used to speed up traces.
277  * 0x100: Actorclip bit.  If this bit is set, the actorclip level will be traced.
278  * 0x200: Weaponclip bit.  If this bit is set, the weaponclip level will be traced.
279  */
280 static bool TR_TileTestLine (TR_TILE_TYPE *tile, const vec3_t start, const vec3_t end, const int levelmask)
281 {
282 	const int corelevels = (levelmask & TL_FLAG_REGULAR_LEVELS);
283 	int i;
284 
285 	/* loop over all theads */
286 	for (i = 0; i < tile->numtheads; i++) {
287 		const int level = tile->theadlevel[i];
288 		if (level && corelevels && !(level & corelevels))
289 			continue;
290 		if (level == LEVEL_LIGHTCLIP)	/* lightclips are only used in ufo2map, and it does not use this function */
291 			continue;
292 		if (level == LEVEL_ACTORCLIP && !(levelmask & TL_FLAG_ACTORCLIP))
293 			continue;
294 		if (level == LEVEL_WEAPONCLIP && !(levelmask & TL_FLAG_WEAPONCLIP))
295 			continue;
296 		if (TR_TestLine_r(tile, tile->thead[i], start, end))
297 			return true;
298 	}
299 	return false;
300 }
301 
302 /**
303  * @brief Checks traces against the world
304  * @param[in] mapTiles List of tiles the current (RMA-)map is composed of
305  * @param[in] start The position to start the trace.
306  * @param[in] end The position where the trace ends.
307  * @param[in] levelmask Indicates which special levels, if any, to include in the trace.
308  * @note Special levels are LEVEL_ACTORCLIP and LEVEL_WEAPONCLIP.
309  * @sa TR_TestLine_r
310  * @return false if not blocked
311  */
312 bool TR_TestLine (mapTiles_t* mapTiles, const vec3_t start, const vec3_t end, const int levelmask)
313 {
314 	int tile;
315 
316 	for (tile = 0; tile < mapTiles->numTiles; tile++) {
317 		if (TR_TileTestLine(&mapTiles->mapTiles[tile], start, end, levelmask))
318 			return true;
319 	}
320 	return false;
321 }
322 
323 
324 /*
325 ===============================================================================
326 LINE TRACING - TEST FOR BRUSH LOCATION
327 ===============================================================================
328 */
329 
330 
331 /**
332  * @param[in] tile The map tile containing the structures to be traced.
333  * @param[in] nodenum Node index
334  * @param[in] start The position to start the trace.
335  * @param[in] end The position where the trace ends.
336  * @param[in,out] tr_end used to hold the point on a line that an obstacle is encountered.
337  * @sa TR_TestLine_r
338  * @sa TR_TestLineDM
339  */
340 static int TR_TestLineDist_r (TR_TILE_TYPE *tile, int32_t nodenum, const vec3_t start, const vec3_t end, vec3_t tr_end)
341 {
342 	tnode_t* tnode;
343 	float front, back;
344 	vec3_t mid;
345 	float frac;
346 	int side;
347 	int r;
348 
349 	if (nodenum & (1 << 31)) {
350 		r = nodenum & ~(1 << 31);
351 		if (r)
352 			VectorCopy(start, tr_end);
353 		return r;				/* leaf node */
354 	}
355 
356 	tnode = &tile->tnodes[nodenum];
357 	assert(tnode);
358 	switch (tnode->type) {
359 	case PLANE_X:
360 	case PLANE_Y:
361 	case PLANE_Z:
362 		front = start[tnode->type] - tnode->dist;
363 		back = end[tnode->type] - tnode->dist;
364 		break;
365 	case PLANE_NONE:
366 		r = TR_TestLineDist_r(tile, tnode->children[0], start, end, tr_end);
367 		if (r)
368 			VectorCopy(tr_end, mid);
369 		side = TR_TestLineDist_r(tile, tnode->children[1], start, end, tr_end);
370 		if (side && r) {
371 			if (VectorNearer(mid, tr_end, start)) {
372 				VectorCopy(mid, tr_end);
373 				return r;
374 			} else {
375 				return side;
376 			}
377 		}
378 
379 		if (r) {
380 			VectorCopy(mid, tr_end);
381 			return r;
382 		}
383 
384 		return side;
385 	default:
386 		front = (start[0] * tnode->normal[0] + start[1] * tnode->normal[1] + start[2] * tnode->normal[2]) - tnode->dist;
387 		back = (end[0] * tnode->normal[0] + end[1] * tnode->normal[1] + end[2] * tnode->normal[2]) - tnode->dist;
388 		break;
389 	}
390 
391 	if (front >= -ON_EPSILON && back >= -ON_EPSILON)
392 		return TR_TestLineDist_r(tile, tnode->children[0], start, end, tr_end);
393 
394 	if (front < ON_EPSILON && back < ON_EPSILON)
395 		return TR_TestLineDist_r(tile, tnode->children[1], start, end, tr_end);
396 
397 	side = front < 0;
398 
399 	frac = front / (front - back);
400 
401 	VectorInterpolation(start, end, frac, mid);
402 
403 	r = TR_TestLineDist_r(tile, tnode->children[side], start, mid, tr_end);
404 	if (r)
405 		return r;
406 	return TR_TestLineDist_r(tile, tnode->children[!side], mid, end, tr_end);
407 }
408 
409 /**
410  * @brief Checks traces against the world, gives hit position back
411  * @param[in] tile The map tile containing the structures to be traced.
412  * @param[in] start The position to start the trace.
413  * @param[in] end The position where the trace ends.
414  * @param[out] hit The position where the trace hits a object or the 'end' position if nothing is in the line.
415  * @param[in] levelmask The bitmask of the levels (1<<[0-7]) to trace for
416  * @sa TR_TestLineDM
417  * @sa CL_ActorMouseTrace
418  * @return false if no connection between start and end - 1 otherwise
419  */
420 static bool TR_TileTestLineDM (TR_TILE_TYPE *tile, const vec3_t start, const vec3_t end, vec3_t hit, const int levelmask)
421 {
422 #ifdef COMPILE_MAP
423 	const int corelevels = (levelmask & TL_FLAG_REGULAR_LEVELS);
424 #endif
425 	vec3_t tr_end;
426 	int i;
427 
428 	VectorCopy(end, hit);
429 
430 	for (i = 0; i < tile->numtheads; i++) {
431 #ifdef COMPILE_MAP
432 		const int level = tile->theadlevel[i];
433 		if (level && corelevels && !(level & levelmask))
434 			continue;
435 /*		if (level == LEVEL_LIGHTCLIP)
436 			continue;*/
437 		if (level == LEVEL_ACTORCLIP && !(levelmask & TL_FLAG_ACTORCLIP))
438 			continue;
439 		if (level == LEVEL_WEAPONCLIP && !(levelmask & TL_FLAG_WEAPONCLIP))
440 			continue;
441 #endif
442 		if (TR_TestLineDist_r(tile, tile->thead[i], start, end, tr_end))
443 			if (VectorNearer(tr_end, hit, start))
444 				VectorCopy(tr_end, hit);
445 	}
446 
447 	if (VectorCompareEps(hit, end, EQUAL_EPSILON))
448 		return false;
449 	else
450 		return true;
451 }
452 
453 
454 /**
455  * @brief Checks traces against the world, gives hit position back
456  * @param[in] mapTiles List of tiles the current (RMA-)map is composed of
457  * @param[in] start The position to start the trace.
458  * @param[in] end The position where the trace ends.
459  * @param[out] hit The position where the trace hits a object or the 'end' position if nothing is in the line.
460  * @param[in] levelmask Indicates which special levels, if any, to include in the trace.
461  * @sa TR_TestLineDM
462  * @sa CL_ActorMouseTrace
463  * @return false if no connection between start and end - 1 otherwise
464  */
465 bool TR_TestLineDM (mapTiles_t* mapTiles, const vec3_t start, const vec3_t end, vec3_t hit, const int levelmask)
466 {
467 	int tile;
468 	vec3_t t_end;
469 
470 	VectorCopy(end, hit);
471 	VectorCopy(end, t_end);
472 
473 	for (tile = 0; tile < mapTiles->numTiles; tile++) {
474 		if (TR_TileTestLineDM(&mapTiles->mapTiles[tile], start, end, t_end, levelmask)) {
475 			if (VectorNearer(t_end, hit, start))
476 				VectorCopy(t_end, hit);
477 		}
478 	}
479 
480 	if (VectorCompareEps(hit, end, EQUAL_EPSILON))
481 		return false;
482 	else
483 		return true;
484 }
485 
486 
487 /*
488 ===============================================================================
489 BOX TRACING
490 ===============================================================================
491 */
492 
493 
494 /**
495  * @brief Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH
496  */
497 int TR_BoxOnPlaneSide (const vec3_t mins, const vec3_t maxs, const TR_PLANE_TYPE *plane)
498 {
499 	int side, i;
500 	vec3_t corners[2];
501 	vec_t dist1, dist2;
502 
503 	/* axial planes are easy */
504 	if (AXIAL(plane)) {
505 		side = 0;
506 		if (maxs[plane->type] > plane->dist + PLANESIDE_EPSILON)
507 			side |= PSIDE_FRONT;
508 		if (mins[plane->type] < plane->dist - PLANESIDE_EPSILON)
509 			side |= PSIDE_BACK;
510 		return side;
511 	}
512 
513 	/* create the proper leading and trailing verts for the box */
514 
515 	for (i = 0; i < 3; i++) {
516 		if (plane->normal[i] < 0) {
517 			corners[0][i] = mins[i];
518 			corners[1][i] = maxs[i];
519 		} else {
520 			corners[1][i] = mins[i];
521 			corners[0][i] = maxs[i];
522 		}
523 	}
524 
525 	dist1 = DotProduct(plane->normal, corners[0]) - plane->dist;
526 	dist2 = DotProduct(plane->normal, corners[1]) - plane->dist;
527 	side = 0;
528 	if (dist1 >= PLANESIDE_EPSILON)
529 		side = PSIDE_FRONT;
530 	if (dist2 < PLANESIDE_EPSILON)
531 		side |= PSIDE_BACK;
532 
533 	return side;
534 }
535 
536 typedef struct leaf_check_s {
537 	int leaf_count, leaf_maxcount;
538 	int32_t* leaf_list;
539 	int32_t leaf_topnode;
540 } leaf_check_t;
541 
542 /**
543  * @brief Fills in a list of all the leafs touched
544  * call with topnode set to the headnode, returns with topnode
545  * set to the first node that splits the box
546  */
547 static void TR_BoxLeafnums_r (boxtrace_t* traceData, int32_t nodenum, leaf_check_t* lc)
548 {
549 	TR_TILE_TYPE *myTile = traceData->tile;
550 
551 	while (1) {
552 		if (nodenum <= LEAFNODE) {
553 			if (lc->leaf_count >= lc->leaf_maxcount) {
554 /*				Com_Printf("CM_BoxLeafnums_r: overflow\n"); */
555 				return;
556 			}
557 			lc->leaf_list[lc->leaf_count++] = -1 - nodenum;
558 			return;
559 		}
560 
561 		assert(nodenum < myTile->numnodes + 6); /* +6 => bbox */
562 		TR_NODE_TYPE *node = &myTile->nodes[nodenum];
563 
564 		TR_PLANE_TYPE *plane;
565 #ifdef COMPILE_UFO
566 		plane = node->plane;
567 #else
568 		plane = myTile->planes + node->planenum;
569 #endif
570 
571 		int s = TR_BoxOnPlaneSide(traceData->absmins, traceData->absmaxs, plane);
572 		if (s == PSIDE_FRONT)
573 			nodenum = node->children[0];
574 		else if (s == PSIDE_BACK)
575 			nodenum = node->children[1];
576 		else {					/* go down both */
577 			if (lc->leaf_topnode == LEAFNODE)
578 				lc->leaf_topnode = nodenum;
579 			TR_BoxLeafnums_r(traceData, node->children[0], lc);
580 			nodenum = node->children[1];
581 		}
582 	}
583 }
584 
585 /**
586  * @param[in] traceData both parameters and results of the trace
587  * @param[in] headnode if < 0 we are in a leaf node
588  */
589 static int TR_BoxLeafnums_headnode (boxtrace_t* traceData, int32_t* list, int listsize, int32_t headnode, int32_t* topnode)
590 {
591 	leaf_check_t lc;
592 	lc.leaf_list = list;
593 	lc.leaf_count = 0;
594 	lc.leaf_maxcount = listsize;
595 	lc.leaf_topnode = LEAFNODE;
596 
597 	assert(headnode < traceData->tile->numnodes + 6); /* +6 => bbox */
598 	TR_BoxLeafnums_r(traceData, headnode, &lc);
599 
600 	if (topnode)
601 		*topnode = lc.leaf_topnode;
602 
603 	return lc.leaf_count;
604 }
605 
606 
607 /**
608  * @param[in,out] traceData both parameters and results of the trace
609  * @param[in] brush the brush that is being examined
610  * @param[in] leaf the leafnode the brush that is being examined belongs to
611  * @brief This function checks to see if any sides of a brush intersect the line from p1 to p2 or are located within
612  *  the perpendicular bounding box from mins to maxs originating from the line. It also check to see if the line
613  *  originates from inside the brush, terminates inside the brush, or is completely contained within the brush.
614  */
615 static void TR_ClipBoxToBrush (boxtrace_t* traceData, cBspBrush_t* brush, TR_LEAF_TYPE *leaf)
616 {
617 	int i, j;
618 	TR_PLANE_TYPE *clipplane;
619 	int clipplanenum;
620 	float dist;
621 	float enterfrac, leavefrac;
622 	vec3_t ofs;
623 	bool getout, startout;
624 #ifdef COMPILE_UFO
625 	TR_BRUSHSIDE_TYPE *leadside = nullptr;
626 #endif
627 	TR_TILE_TYPE *myTile = traceData->tile;
628 
629 	enterfrac = -1.0;
630 	leavefrac = 1.0;
631 	clipplane = nullptr;
632 
633 	if (!brush || !brush->numsides)
634 		return;
635 
636 	getout = false;
637 	startout = false;
638 	clipplanenum = 0;
639 
640 	for (i = 0; i < brush->numsides; i++) {
641 		TR_BRUSHSIDE_TYPE *side = &myTile->brushsides[brush->firstbrushside + i];
642 #ifdef COMPILE_UFO
643 		TR_PLANE_TYPE *plane = side->plane;
644 #else
645 		TR_PLANE_TYPE *plane = myTile->planes + side->planenum;
646 #endif
647 
648 		/** @todo special case for axial */
649 		if (!traceData->ispoint) {	/* general box case */
650 			/* push the plane out appropriately for mins/maxs */
651 			for (j = 0; j < 3; j++) {
652 				if (plane->normal[j] < 0)
653 					ofs[j] = traceData->maxs[j];
654 				else
655 					ofs[j] = traceData->mins[j];
656 			}
657 			dist = DotProduct(ofs, plane->normal);
658 			dist = plane->dist - dist;
659 		} else {				/* special point case */
660 			dist = plane->dist;
661 		}
662 
663 		float d1 = DotProduct(traceData->start, plane->normal) - dist;
664 		float d2 = DotProduct(traceData->end, plane->normal) - dist;
665 
666 		if (d2 > 0)
667 			getout = true;		/* endpoint is not in solid */
668 		if (d1 > 0)
669 			startout = true;	/* startpoint is not in solid */
670 
671 		/* if completely in front of face, no intersection */
672 		if (d1 > 0 && d2 >= d1)
673 			return;
674 
675 		if (d1 <= 0 && d2 <= 0)
676 			continue;
677 
678 		/* crosses face */
679 		if (d1 > d2) {			/* enter */
680 			const float f = (d1 - DIST_EPSILON) / (d1 - d2);
681 			if (f > enterfrac) {
682 				enterfrac = f;
683 				clipplane = plane;
684 #ifdef COMPILE_MAP
685 				clipplanenum = side->planenum;
686 #else
687 				leadside = side;
688 #endif
689 			}
690 		} else {				/* leave */
691 			const float f = (d1 + DIST_EPSILON) / (d1 - d2);
692 			if (f < leavefrac)
693 				leavefrac = f;
694 		}
695 	}
696 
697 	if (!startout) {			/* original point was inside brush */
698 		traceData->trace.startsolid = true;
699 		if (!getout)
700 			traceData->trace.allsolid = true;
701 		traceData->trace.leafnum = leaf - myTile->leafs;
702 		return;
703 	}
704 	if (enterfrac < leavefrac) {
705 		if (enterfrac > -1 && enterfrac < traceData->trace.fraction) {
706 			if (enterfrac < 0)
707 				enterfrac = 0;
708 			traceData->trace.fraction = enterfrac;
709 			traceData->trace.plane = *clipplane;
710 			traceData->trace.planenum = clipplanenum;
711 #ifdef COMPILE_UFO
712 			traceData->trace.surface = leadside->surface;
713 #endif
714 			traceData->trace.contentFlags = brush->contentFlags;
715 			traceData->trace.leafnum = leaf - myTile->leafs;
716 		}
717 	}
718 }
719 
720 /**
721  * @sa CM_TraceToLeaf
722  */
723 static void TR_TestBoxInBrush (boxtrace_t* traceData, cBspBrush_t* brush)
724 {
725 	int i, j;
726 	TR_PLANE_TYPE *plane;
727 	vec3_t ofs;
728 	TR_TILE_TYPE *myTile = traceData->tile;
729 
730 	if (!brush || !brush->numsides)
731 		return;
732 
733 	for (i = 0; i < brush->numsides; i++) {
734 		TR_BRUSHSIDE_TYPE *side = &myTile->brushsides[brush->firstbrushside + i];
735 #ifdef COMPILE_UFO
736 		plane = side->plane;
737 #else
738 		plane = myTile->planes + side->planenum;
739 #endif
740 
741 		/** @todo special case for axial */
742 		/* general box case */
743 		/* push the plane out appropriately for mins/maxs */
744 		for (j = 0; j < 3; j++) {
745 			if (plane->normal[j] < 0)
746 				ofs[j] = traceData->maxs[j];
747 			else
748 				ofs[j] = traceData->mins[j];
749 		}
750 		float dist = DotProduct(ofs, plane->normal);
751 		dist = plane->dist - dist;
752 
753 		float d1 = DotProduct(traceData->start, plane->normal) - dist;
754 
755 		/* if completely in front of face, no intersection */
756 		if (d1 > 0)
757 			return;
758 	}
759 
760 	/* inside this brush */
761 	traceData->trace.startsolid = traceData->trace.allsolid = true;
762 	traceData->trace.fraction = 0;
763 	traceData->trace.contentFlags = brush->contentFlags;
764 }
765 
766 
767 /**
768  * @param[in] traceData both parameters and results of the trace
769  * @param[in] leafnum the leaf index that we are looking in for a hit against
770  * @sa CM_ClipBoxToBrush
771  * @sa CM_TestBoxInBrush
772  * @sa CM_RecursiveHullCheck
773  * @brief This function checks if the specified leaf matches any mask specified in traceData.contents. and does
774  *  not contain any mask specified in traceData.rejects  If so, each brush in the leaf is examined to see if it
775  *  is intersected by the line drawn in TR_RecursiveHullCheck or is within the bounding box set in trace_mins and
776  *  trace_maxs with the origin on the line.
777  */
778 static void TR_TraceToLeaf (boxtrace_t* traceData, int32_t leafnum)
779 {
780 	int k;
781 	TR_LEAF_TYPE *leaf;
782 	TR_TILE_TYPE *myTile = traceData->tile;
783 
784 	assert(leafnum > LEAFNODE);
785 	assert(leafnum <= myTile->numleafs);
786 
787 	leaf = &myTile->leafs[leafnum];
788 
789 	if (traceData->contents != MASK_ALL && (!(leaf->contentFlags & traceData->contents) || (leaf->contentFlags & traceData->rejects)))
790 		return;
791 
792 	/* trace line against all brushes in the leaf */
793 	for (k = 0; k < leaf->numleafbrushes; k++) {
794 		const int brushnum = myTile->leafbrushes[leaf->firstleafbrush + k];
795 		cBspBrush_t* b = &myTile->brushes[brushnum];
796 
797 		if (b->checkcount == checkcount)
798 			continue;			/* already checked this brush in another leaf */
799 		b->checkcount = checkcount;
800 
801 		if (traceData->contents != MASK_ALL && (!(b->contentFlags & traceData->contents) || (b->contentFlags & traceData->rejects)))
802 			continue;
803 
804 		TR_ClipBoxToBrush(traceData, b, leaf);
805 		if (!traceData->trace.fraction)
806 			return;
807 	}
808 }
809 
810 
811 /**
812  * @sa CM_TestBoxInBrush
813  */
814 static void TR_TestInLeaf (boxtrace_t* traceData, int32_t leafnum)
815 {
816 	int k;
817 	const TR_LEAF_TYPE *leaf;
818 	TR_TILE_TYPE *myTile = traceData->tile;
819 
820 	assert(leafnum > LEAFNODE);
821 	assert(leafnum <= myTile->numleafs);
822 
823 	leaf = &myTile->leafs[leafnum];
824 	/* If this leaf contains no flags we need to look for, then skip it. */
825 	if (!(leaf->contentFlags & traceData->contents)) /* || (leaf->contentFlags & traceData->rejects) */
826 		return;
827 
828 	/* trace line against all brushes in the leaf */
829 	for (k = 0; k < leaf->numleafbrushes; k++) {
830 		const int brushnum = myTile->leafbrushes[leaf->firstleafbrush + k];
831 		cBspBrush_t* b = &myTile->brushes[brushnum];
832 		if (b->checkcount == checkcount)
833 			continue;			/* already checked this brush in another leaf */
834 		b->checkcount = checkcount;
835 
836 		if (!(traceData->contents && (b->contentFlags & traceData->contents)) || (b->contentFlags & traceData->rejects))
837 			continue;
838 		TR_TestBoxInBrush(traceData, b);
839 		if (!traceData->trace.fraction)
840 			return;
841 	}
842 }
843 
844 
845 /**
846  * @param[in] traceData both parameters and results of the trace
847  * @param[in] nodenum the node index that we are looking in for a hit
848  * @param[in] p1f based on the original line, the fraction traveled to reach the start vector
849  * @param[in] p2f based on the original line, the fraction traveled to reach the end vector
850  * @param[in] p1 start vector
851  * @param[in] p2 end vector
852  * @sa CM_BoxTrace
853  * @sa CM_TraceToLeaf
854  * @brief This recursive function traces through the bsp tree looking to see if a brush can be
855  *  found that intersects the line from p1 to p2, including a bounding box (plane) offset that
856  *  is perpendicular to the line.  If the node of the tree is a leaf, the leaf is checked.  If
857  *  not, it is determined which side(s) of the tree need to be traversed, and this function is
858  *  called again.  The bounding box mentioned earlier is set in TR_BoxTrace, and propagated
859  *  using trace_extents.  Trace_extents is specifically how far from the line a bsp node needs
860  *  to be in order to be included or excluded in the search.
861  */
862 static void TR_RecursiveHullCheck (boxtrace_t* traceData, int32_t nodenum, float p1f, float p2f, const vec3_t p1, const vec3_t p2)
863 {
864 	TR_NODE_TYPE *node;
865 	TR_PLANE_TYPE *plane;
866 	float t1, t2, offset;
867 	float frac, frac2;
868 	int side;
869 	float midf;
870 	vec3_t mid;
871 	TR_TILE_TYPE *myTile = traceData->tile;
872 
873 	if (traceData->trace.fraction <= p1f)
874 		return;					/* already hit something nearer */
875 
876 	/* if < 0, we are in a leaf node */
877 	if (nodenum <= LEAFNODE) {
878 		TR_TraceToLeaf(traceData, LEAFNODE - nodenum);
879 		return;
880 	}
881 
882 	assert(nodenum < MAX_MAP_NODES);
883 
884 	/* find the point distances to the seperating plane
885 	 * and the offset for the size of the box */
886 	node = myTile->nodes + nodenum;
887 
888 #ifdef COMPILE_UFO
889 	plane = node->plane;
890 #else
891 	assert(node->planenum < MAX_MAP_PLANES);
892 	plane = myTile->planes + node->planenum;
893 #endif
894 
895 	if (AXIAL(plane)) {
896 		const int type = plane->type;
897 		t1 = p1[type] - plane->dist;
898 		t2 = p2[type] - plane->dist;
899 		offset = traceData->extents[type];
900 	} else {
901 		t1 = DotProduct(plane->normal, p1) - plane->dist;
902 		t2 = DotProduct(plane->normal, p2) - plane->dist;
903 		if (traceData->ispoint)
904 			offset = 0;
905 		else
906 			offset = fabs(traceData->extents[0] * plane->normal[0])
907 				+ fabs(traceData->extents[1] * plane->normal[1])
908 				+ fabs(traceData->extents[2] * plane->normal[2]);
909 	}
910 
911 	/* see which sides we need to consider */
912 	if (t1 >= offset && t2 >= offset) {
913 		TR_RecursiveHullCheck(traceData, node->children[0], p1f, p2f, p1, p2);
914 		return;
915 	} else if (t1 < -offset && t2 < -offset) {
916 		TR_RecursiveHullCheck(traceData, node->children[1], p1f, p2f, p1, p2);
917 		return;
918 	}
919 
920 	/* put the crosspoint DIST_EPSILON pixels on the near side */
921 	/** @note t1 and t2 refer to the distance the endpoints of the line are from the bsp dividing plane for this node.
922 	 * Additionally, frac and frac2 are the fractions of the CURRENT PIECE of the line that was being tested, and will
923 	 * add to (approximately) 1.  When midf is calculated, frac and frac2 are converted to reflect the fraction of the
924 	 * WHOLE line being traced.  However, the interpolated vector is based on the CURRENT endpoints so uses frac and
925 	 * frac2 to find the actual splitting point.
926 	 */
927 	if (t1 < t2) {
928 		const float idist = 1.0 / (t1 - t2);
929 		side = 1;
930 		frac2 = (t1 + offset + DIST_EPSILON) * idist;
931 		frac = (t1 - offset + DIST_EPSILON) * idist;
932 	} else if (t1 > t2) {
933 		const float idist = 1.0 / (t1 - t2);
934 		side = 0;
935 		frac2 = (t1 - offset - DIST_EPSILON) * idist;
936 		frac = (t1 + offset + DIST_EPSILON) * idist;
937 	} else {
938 		side = 0;
939 		frac = 1;
940 		frac2 = 0;
941 	}
942 
943 	/* move up to the node */
944 	if (frac < 0)
945 		frac = 0;
946 	else if (frac > 1)
947 		frac = 1;
948 
949 	midf = p1f + (p2f - p1f) * frac;
950 	VectorInterpolation(p1, p2, frac, mid);
951 	TR_RecursiveHullCheck(traceData, node->children[side], p1f, midf, p1, mid);
952 
953 	/* go past the node */
954 	if (frac2 < 0)
955 		frac2 = 0;
956 	else if (frac2 > 1)
957 		frac2 = 1;
958 
959 	midf = p1f + (p2f - p1f) * frac2;
960 	VectorInterpolation(p1, p2, frac2, mid);
961 	TR_RecursiveHullCheck(traceData, node->children[side ^ 1], midf, p2f, mid, p2);
962 }
963 
964 /**
965  * @brief This function traces a line from start to end.  It returns a trace_t indicating what portion of the line
966  * can be traveled from start to end before hitting a brush that meets the criteria in brushmask.  The point that
967  * this line intersects that brush is also returned.
968  * There is a special case when start and end are the same vector.  In this case, the bounding box formed by mins
969  * and maxs offset from start is examined for any brushes that meet the criteria.  The first brush found inside
970  * the bounding box is returned.
971  * There is another special case when mins and maxs are both origin vectors (0, 0, 0).  In this case, the
972  * @param[in] start trace start vector
973  * @param[in] end trace end vector
974  * @param[in] traceBox The box we shove through the world
975  * @param[in] tile Tile to check (normally 0 - except in assembled maps)
976  * @param[in] headnode if < 0 we are in a leaf node
977  * @param[in] contentmask brushes the trace should stop at (see MASK_*)
978  * @param[in] brushreject brushes the trace should ignore (see MASK_*)
979  * @param[in] fraction The furthest distance needed to trace before we stop.
980  * @sa TR_RecursiveHullCheck
981  * @sa TR_BoxLeafnums_headnode
982  */
983 trace_t TR_BoxTrace (TR_TILE_TYPE *tile, const vec3_t start, const vec3_t end, const AABB &traceBox, const int headnode, const int contentmask, const int brushreject, const float fraction)
984 {
985 	vec3_t offset, amins, amaxs, astart, aend;
986 	boxtrace_t traceData;
987 
988 	checkcount++;	/* for multi-check avoidance */
989 
990 #ifdef COMPILE_UFO
991 	if (headnode >= tile->numnodes + 6)
992 		Com_Error(ERR_DROP, "headnode (%i) is out of bounds: %i", headnode, tile->numnodes + 6);
993 #else
994 	assert(headnode < tile->numnodes + 6); /* +6 => bbox */
995 #endif
996 
997 	/* fill in a default trace */
998 	traceData.trace.init();
999 	traceData.trace.fraction = std::min(fraction, 1.0f); /* Use 1 or fraction, whichever is lower. */
1000 	traceData.trace.surface = nullptr;
1001 
1002 	if (!tile->numnodes)		/* map not loaded */
1003 		return traceData.trace;
1004 
1005 	/* Optimize the trace by moving the line to be traced across into the origin of the box trace. */
1006 	/* Calculate the offset needed to center the trace about the line */
1007 	traceBox.getCenter(offset);
1008 
1009 	/* Now remove the offset from bmin and bmax (effectively centering the trace box about the origin of the line)
1010 	 * and add the offset to the trace line (effectively repositioning the trace box at the desired coordinates) */
1011 	VectorSubtract(traceBox.mins, offset, amins);
1012 	VectorSubtract(traceBox.maxs, offset, amaxs);
1013 	VectorAdd(start, offset, astart);
1014 	VectorAdd(end, offset, aend);
1015 
1016 	traceData.contents = contentmask;
1017 	traceData.rejects = brushreject;
1018 	traceData.tile = tile;
1019 	VectorCopy(astart, traceData.start);
1020 	VectorCopy(aend, traceData.end);
1021 	VectorCopy(amins, traceData.mins);
1022 	VectorCopy(amaxs, traceData.maxs);
1023 
1024 	/* check for position test special case */
1025 	if (VectorEqual(astart, aend)) {
1026 		int32_t leafs[MAX_LEAFS];
1027 		int numleafs;
1028 		int32_t topnode;
1029 		int i;
1030 
1031 		VectorAdd(astart, amaxs, traceData.absmaxs);
1032 		VectorAdd(astart, amins, traceData.absmins);
1033 		/* Removed because if creates a buffer- is this needed?
1034 		for (i = 0; i < 3; i++) {
1035 			/ * expand the box by 1 * /
1036 			traceData.absmins[i] -= 1;
1037 			traceData.absmaxs[i] += 1;
1038 		}
1039 		*/
1040 
1041 		numleafs = TR_BoxLeafnums_headnode(&traceData, leafs, MAX_LEAFS, headnode, &topnode);
1042 		for (i = 0; i < numleafs; i++) {
1043 			TR_TestInLeaf(&traceData, leafs[i]);
1044 			if (traceData.trace.allsolid)
1045 				break;
1046 		}
1047 		VectorCopy(start, traceData.trace.endpos);
1048 		return traceData.trace;
1049 	}
1050 
1051 	/* check for point special case */
1052 	if (VectorEmpty(amins) && VectorEmpty(amaxs)) {
1053 		traceData.ispoint = true;
1054 		VectorClear(traceData.extents);
1055 	} else {
1056 		traceData.ispoint = false;
1057 		VectorCopy(amaxs, traceData.extents);
1058 	}
1059 
1060 	/* general sweeping through world */
1061 	/** @todo Would Interpolating aend to traceData.fraction and passing traceData.fraction instead of 1.0 make this faster? */
1062 	TR_RecursiveHullCheck(&traceData, headnode, 0.0, 1.0, astart, aend);
1063 
1064 	if (traceData.trace.fraction >= 1.0) {
1065 		VectorCopy(aend, traceData.trace.endpos);
1066 	} else {
1067 		VectorInterpolation(traceData.start, traceData.end, traceData.trace.fraction, traceData.trace.endpos);
1068 	}
1069 	/* Now un-offset the end position. */
1070 	VectorSubtract(traceData.trace.endpos, offset, traceData.trace.endpos);
1071 	return traceData.trace;
1072 }
1073 
1074 /**
1075  * @brief Traces all submodels in the specified tile.  Provides for a short
1076  *   circuit if the trace tries to move past fraction to save time.
1077  * @param[in] myTile The tile being traced
1078  * @param[in] start trace start vector
1079  * @param[in] end trace end vector
1080  * @param[in] aabb The box we are moving through the world
1081  * @param[in] levelmask Selects which submodels get scanned.
1082  * @param[in] brushmask brushes the trace should stop at (see MASK_*)
1083  * @param[in] brushreject brushes the trace should ignore (see MASK_*)
1084  */
1085 trace_t TR_TileBoxTrace (TR_TILE_TYPE *myTile, const vec3_t start, const vec3_t end, const AABB &aabb, const int levelmask, const int brushmask, const int brushreject)
1086 {
1087 	int i;
1088 	cBspHead_t* h;
1089 	trace_t tr;
1090 
1091 	/* ensure that the first trace is set in every case */
1092 	tr.fraction = 2.0f;
1093 
1094 	/* trace against all loaded map tiles */
1095 	for (i = 0, h = myTile->cheads; i < myTile->numcheads; i++, h++) {
1096 		/* This code uses levelmask to limit by maplevel.  Supposedly maplevels 1-255
1097 		 * are bitmasks of game levels 1-8.  0 is a special case repeat of 255.
1098 		 * However a levelmask including 0x100 is usually included so the CLIP levels are
1099 		 * examined. */
1100 		if (h->level && h->level <= LEVEL_LASTVISIBLE && levelmask && !(h->level & levelmask))
1101 			continue;
1102 
1103 		assert(h->cnode < myTile->numnodes + 6); /* +6 => bbox */
1104 		const trace_t newtr = TR_BoxTrace(myTile, start, end, aabb, h->cnode, brushmask, brushreject, tr.fraction);
1105 
1106 		/* memorize the trace with the minimal fraction */
1107 		if (newtr.fraction == 0.0)
1108 			return newtr;
1109 		if (newtr.fraction < tr.fraction)
1110 			tr = newtr;
1111 	}
1112 	return tr;
1113 }
1114 
1115 #ifdef COMPILE_MAP
1116 
1117 /**
1118  * @param[in] start trace start vector
1119  * @param[in] end trace end vector
1120  * @param[in] mins box mins
1121  * @param[in] maxs box maxs
1122  * @param[in] levelmask Selects which submodels get scanned.
1123  * @param[in] brushmask brushes the trace should stop at (see MASK_*)
1124  * @param[in] brushreject brushes the trace should ignore (see MASK_*)
1125  * @brief Traces all submodels in the first tile.  Used by ufo2map.
1126  */
1127 trace_t TR_SingleTileBoxTrace (mapTiles_t* mapTiles, const Line &traceLine, const AABB* traceBox, const int levelmask, const int brushmask, const int brushreject)
1128 {
1129 	/* Trace the whole line against the first tile. */
1130 	trace_t tr = TR_TileBoxTrace(&mapTiles->mapTiles[0], traceLine.start, traceLine.stop, *traceBox, levelmask, brushmask, brushreject);
1131 	tr.mapTile = 0;
1132 	return tr;
1133 }
1134 #endif
1135