UFO: Alien Invasion
Loading...
Searching...
No Matches
tracing.cpp
Go to the documentation of this file.
1
6
7/*
8All original material Copyright (C) 2002-2025 UFO: Alien Invasion.
9
10Copyright (C) 1997-2001 Id Software, Inc.
11
12This program is free software; you can redistribute it and/or
13modify it under the terms of the GNU General Public License
14as published by the Free Software Foundation; either version 2
15of the License, or (at your option) any later version.
16
17This program is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20
21See the GNU General Public License for more details.
22
23You should have received a copy of the GNU General Public License
24along with this program; if not, write to the Free Software
25Foundation, 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
45
46void boxtrace_s::init (TR_TILE_TYPE* _tile, const int contentmask, const int brushreject, const float fraction)
47{
48 trace.init();
49 trace.surface = nullptr;
50 contents = contentmask;
51 rejects = brushreject;
52 tile = _tile;
53 trace.fraction = std::min(fraction, 1.0f);
54}
55
56/* Optimize the trace by moving the line to be traced across into the origin of the box trace. */
57void boxtrace_s::setLineAndBox(const Line& line, const AABB& box)
58{
59 /* Calculate the offset needed to center the trace about the line */
60 box.getCenter(offset);
61
62 /* Now remove the offset from bmin and bmax (effectively centering the trace box about the origin of the line)
63 * and add the offset to the trace line (effectively repositioning the trace box at the desired coordinates) */
64 VectorSubtract(box.mins, offset, this->mins);
65 VectorSubtract(box.maxs, offset, this->maxs);
66 VectorAdd(line.start, offset, this->start);
67 VectorAdd(line.stop, offset, this->end);
68}
69
72static int checkcount;
73
74/*
75===============================================================================
76TRACING NODES
77===============================================================================
78*/
79
83static void TR_MakeTracingNode (TR_TILE_TYPE* tile, tnode_t** tnode, int32_t nodenum)
84{
85 tnode_t* t = (*tnode)++; /* the tracing node to build */
86 TR_NODE_TYPE* node = tile->nodes + nodenum; /* the node we are investigating */
87
88#ifdef COMPILE_UFO
89 const TR_PLANE_TYPE* plane = node->plane;
90#else
91 const TR_PLANE_TYPE* plane = tile->planes + node->planenum;
92#endif
93
94 t->type = plane->type;
95 VectorCopy(plane->normal, t->normal);
96 t->dist = plane->dist;
97
98 for (int i = 0; i < 2; i++) {
99 if (node->children[i] < 0) { /* is it a leaf ? */
100 const int32_t leafnum = -(node->children[i]) - 1;
101 const TR_LEAF_TYPE* leaf = &tile->leafs[leafnum];
102 const uint32_t contentFlags = leaf->contentFlags & ~(1 << 31);
103 if ((contentFlags & (CONTENTS_SOLID | MASK_CLIP)) && !(contentFlags & CONTENTS_PASSABLE))
104 t->children[i] = -node->children[i] | (1 << 31); /* mark as 'blocking' */
105 else
106 t->children[i] = (1 << 31); /* mark as 'empty leaf' */
107 } else { /* not a leaf */
108 t->children[i] = *tnode - tile->tnodes;
109 if (t->children[i] > tile->numnodes) {
110 Com_Printf("Exceeded allocated memory for tracing structure (%i > %i)\n",
111 t->children[i], tile->numnodes);
112 }
113 TR_MakeTracingNode(tile, tnode, node->children[i]); /* recurse further down the tree */
114 }
115 }
116}
117
122void TR_BuildTracingNode_r (TR_TILE_TYPE* tile, tnode_t** tnode, int32_t nodenum, int level)
123{
124 assert(nodenum < tile->numnodes + 6); /* +6 => bbox */
125
130#ifdef COMPILE_UFO
131 if (!tile->nodes[nodenum].plane) {
132#else
133 if (tile->nodes[nodenum].planenum == PLANENUM_LEAF) {
134#endif
135 TR_NODE_TYPE* n = &tile->nodes[nodenum];
136
137 /* alloc new node */
138 tnode_t* t = (*tnode)++;
139
140 /* no leafs here */
141 if (n->children[0] < 0 || n->children[1] < 0)
142#ifdef COMPILE_UFO
143 Com_Error(ERR_DROP, "Unexpected leaf");
144#else
145 Sys_Error("Unexpected leaf");
146#endif
147
148 vec3_t c0maxs, c1mins;
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 (int 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 (int 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===============================================================================
195LINE TRACING - TEST FOR BRUSH PRESENCE
196===============================================================================
197*/
198
199
209int TR_TestLine_r (TR_TILE_TYPE* tile, int32_t nodenum, const vec3_t start, const vec3_t end)
210{
211 float front, back;
212 int r;
213
214 /* negative numbers indicate leaf nodes. Empty leaf nodes are marked as (1 << 31).
215 * Turning off that bit makes us return 0 or the positive node number to indicate blocking. */
216 if (nodenum & (1 << 31))
217 return nodenum & ~(1 << 31);
218
219 const tnode_t* tnode = &tile->tnodes[nodenum];
220 assert(tnode);
221 switch (tnode->type) {
222 case PLANE_X:
223 case PLANE_Y:
224 case PLANE_Z:
225 front = start[tnode->type] - tnode->dist;
226 back = end[tnode->type] - tnode->dist;
227 break;
228 case PLANE_NONE:
229 r = TR_TestLine_r(tile, tnode->children[0], start, end);
230 if (r)
231 return r;
232 return TR_TestLine_r(tile, tnode->children[1], start, end);
233 default:
234 front = DotProduct(start, tnode->normal) - tnode->dist;
235 back = DotProduct(end, tnode->normal) - tnode->dist;
236 break;
237 }
238
239 if (front >= -ON_EPSILON && back >= -ON_EPSILON)
240 return TR_TestLine_r(tile, tnode->children[0], start, end);
241 else if (front < ON_EPSILON && back < ON_EPSILON)
242 return TR_TestLine_r(tile, tnode->children[1], start, end);
243 else {
244 const int side = front < 0;
245 const float frac = front / (front - back);
246 vec3_t mid;
247
248 VectorInterpolation(start, end, frac, mid);
249
250 r = TR_TestLine_r(tile, tnode->children[side], start, mid);
251 if (r)
252 return r;
253 return TR_TestLine_r(tile, tnode->children[!side], mid, end);
254 }
255}
256
257
279static bool TR_TileTestLine (TR_TILE_TYPE* tile, const vec3_t start, const vec3_t end, const int levelmask)
280{
281 const int corelevels = (levelmask & TL_FLAG_REGULAR_LEVELS);
282
283 /* loop over all theads */
284 for (int i = 0; i < tile->numtheads; i++) {
285 const int level = tile->theadlevel[i];
286 if (level && corelevels && !(level & corelevels))
287 continue;
288 if (level == LEVEL_LIGHTCLIP) /* lightclips are only used in ufo2map, and it does not use this function */
289 continue;
290 if (level == LEVEL_ACTORCLIP && !(levelmask & TL_FLAG_ACTORCLIP))
291 continue;
292 if (level == LEVEL_WEAPONCLIP && !(levelmask & TL_FLAG_WEAPONCLIP))
293 continue;
294 if (TR_TestLine_r(tile, tile->thead[i], start, end))
295 return true;
296 }
297 return false;
298}
299
310bool TR_TestLine (mapTiles_t* mapTiles, const vec3_t start, const vec3_t end, const int levelmask)
311{
312 for (int tile = 0; tile < mapTiles->numTiles; tile++) {
313 if (TR_TileTestLine(&mapTiles->mapTiles[tile], start, end, levelmask))
314 return true;
315 }
316 return false;
317}
318
319
320/*
321===============================================================================
322LINE TRACING - TEST FOR BRUSH LOCATION
323===============================================================================
324*/
325
326
336static int TR_TestLineDist_r (TR_TILE_TYPE* tile, int32_t nodenum, const vec3_t start, const vec3_t end, vec3_t tr_end)
337{
338 float front, back;
339 vec3_t mid;
340 int side;
341 int r;
342
343 if (nodenum & (1 << 31)) {
344 r = nodenum & ~(1 << 31);
345 if (r)
346 VectorCopy(start, tr_end);
347 return r; /* leaf node */
348 }
349
350 const tnode_t* tnode = &tile->tnodes[nodenum];
351 assert(tnode);
352 switch (tnode->type) {
353 case PLANE_X:
354 case PLANE_Y:
355 case PLANE_Z:
356 front = start[tnode->type] - tnode->dist;
357 back = end[tnode->type] - tnode->dist;
358 break;
359 case PLANE_NONE:
360 r = TR_TestLineDist_r(tile, tnode->children[0], start, end, tr_end);
361 if (r)
362 VectorCopy(tr_end, mid);
363 side = TR_TestLineDist_r(tile, tnode->children[1], start, end, tr_end);
364 if (side && r) {
365 if (VectorNearer(mid, tr_end, start)) {
366 VectorCopy(mid, tr_end);
367 return r;
368 } else {
369 return side;
370 }
371 }
372
373 if (r) {
374 VectorCopy(mid, tr_end);
375 return r;
376 }
377
378 return side;
379 default:
380 front = (start[0] * tnode->normal[0] + start[1] * tnode->normal[1] + start[2] * tnode->normal[2]) - tnode->dist;
381 back = (end[0] * tnode->normal[0] + end[1] * tnode->normal[1] + end[2] * tnode->normal[2]) - tnode->dist;
382 break;
383 }
384
385 if (front >= -ON_EPSILON && back >= -ON_EPSILON)
386 return TR_TestLineDist_r(tile, tnode->children[0], start, end, tr_end);
387
388 if (front < ON_EPSILON && back < ON_EPSILON)
389 return TR_TestLineDist_r(tile, tnode->children[1], start, end, tr_end);
390
391 side = front < 0;
392
393 const float frac = front / (front - back);
394
395 VectorInterpolation(start, end, frac, mid);
396
397 r = TR_TestLineDist_r(tile, tnode->children[side], start, mid, tr_end);
398 if (r)
399 return r;
400 return TR_TestLineDist_r(tile, tnode->children[!side], mid, end, tr_end);
401}
402
414static bool TR_TileTestLineDM (TR_TILE_TYPE* tile, const vec3_t start, const vec3_t end, vec3_t hit, const int levelmask)
415{
416#ifdef COMPILE_MAP
417 const int corelevels = (levelmask & TL_FLAG_REGULAR_LEVELS);
418#endif
419
420 VectorCopy(end, hit);
421
422 for (int i = 0; i < tile->numtheads; i++) {
423#ifdef COMPILE_MAP
424 const int level = tile->theadlevel[i];
425 if (level && corelevels && !(level & levelmask))
426 continue;
427/* if (level == LEVEL_LIGHTCLIP)
428 continue;*/
429 if (level == LEVEL_ACTORCLIP && !(levelmask & TL_FLAG_ACTORCLIP))
430 continue;
431 if (level == LEVEL_WEAPONCLIP && !(levelmask & TL_FLAG_WEAPONCLIP))
432 continue;
433#endif
434 vec3_t tr_end;
435 if (TR_TestLineDist_r(tile, tile->thead[i], start, end, tr_end))
436 if (VectorNearer(tr_end, hit, start))
437 VectorCopy(tr_end, hit);
438 }
439
440 if (VectorCompareEps(hit, end, EQUAL_EPSILON))
441 return false;
442 else
443 return true;
444}
445
446
458bool TR_TestLineDM (mapTiles_t* mapTiles, const vec3_t start, const vec3_t end, vec3_t hit, const int levelmask)
459{
460 vec3_t t_end;
461
462 VectorCopy(end, hit);
463 VectorCopy(end, t_end);
464
465 for (int tile = 0; tile < mapTiles->numTiles; tile++) {
466 if (TR_TileTestLineDM(&mapTiles->mapTiles[tile], start, end, t_end, levelmask)) {
467 if (VectorNearer(t_end, hit, start))
468 VectorCopy(t_end, hit);
469 }
470 }
471
472 if (VectorCompareEps(hit, end, EQUAL_EPSILON))
473 return false;
474 else
475 return true;
476}
477
478void mapTiles_s::getTilesAt (int x ,int y, byte& fromTile1, byte& fromTile2, byte& fromTile3)
479{
480#if defined(COMPILE_UFO)
481 for (int i = 0; i < numTiles; i++) {
482 if ( mapTiles[i].wpMins[0] > x
483 || mapTiles[i].wpMaxs[0] - 1 < x /* the -1 is a temporary fix for wpMaxs being off by 1 */
484 || mapTiles[i].wpMins[1] > y
485 || mapTiles[i].wpMaxs[1] - 1 < y)
486 continue;
487 /* this tile exists at x/y, so store it */
488 if (!fromTile1)
489 fromTile1 = mapTiles[i].idx + 1; /* tile number, not index */
490 else if (!fromTile2)
491 fromTile2 = mapTiles[i].idx + 1; /* tile number, not index */
492 else
493 fromTile3 = 99; /* stacking of more than two tiles is not supported */
494 }
495#endif
496}
497
498void mapTiles_s::getTileOverlap (const byte tile1, const byte tile2, int& minZ, int& maxZ)
499{
500#if defined(COMPILE_UFO)
501 const int lowZ1 = mapTiles[tile1 - 1].wpMins[2];
502 const int lowZ2 = mapTiles[tile2 - 1].wpMins[2];
503 const int highZ1 = mapTiles[tile1 - 1].wpMaxs[2];
504 const int highZ2 = mapTiles[tile2 - 1].wpMaxs[2];
505 minZ = std::max(lowZ1, lowZ2);
506 if (minZ > 0)
507 minZ--; /* routing needs to start one level below the actual overlap */
508 maxZ = std::min(highZ1, highZ2);
509 maxZ++; /* ... and one level above */
510#endif
511}
512
513void mapTiles_s::printTilesAt (int x ,int y)
514{
515#if defined(COMPILE_UFO)
516 byte fromTile1 = 0; /* tile number, not index */
517 byte fromTile2 = 0;
518 byte fromTile3 = 0;
519 getTilesAt(x ,y, fromTile1, fromTile2, fromTile3);
520 if (fromTile1) {
521 const MapTile& tile = mapTiles[fromTile1 - 1];
522 Com_Printf("Tilenames: %s (%i %i) (%i %i)", tile.name, tile.wpMins[0], tile.wpMins[1], tile.wpMaxs[0], tile.wpMaxs[1]);
523 }
524 if (fromTile2)
525 Com_Printf(", %s", mapTiles[fromTile2 - 1].name);
526 if (fromTile3)
527 Com_Printf(", %s", mapTiles[fromTile3 - 1].name);
528 Com_Printf("\n");
529#endif
530}
531
532/*
533===============================================================================
534BOX TRACING
535===============================================================================
536*/
537
538
542int TR_BoxOnPlaneSide (const vec3_t mins, const vec3_t maxs, const TR_PLANE_TYPE* plane)
543{
544 /* axial planes are easy */
545 if (AXIAL(plane)) {
546 int side = 0;
547 if (maxs[plane->type] > plane->dist + PLANESIDE_EPSILON)
548 side |= PSIDE_FRONT;
549 if (mins[plane->type] < plane->dist - PLANESIDE_EPSILON)
550 side |= PSIDE_BACK;
551 return side;
552 }
553
554 /* create the proper leading and trailing verts for the box */
555
556 vec3_t corners[2];
557 for (int i = 0; i < 3; i++) {
558 if (plane->normal[i] < 0) {
559 corners[0][i] = mins[i];
560 corners[1][i] = maxs[i];
561 } else {
562 corners[1][i] = mins[i];
563 corners[0][i] = maxs[i];
564 }
565 }
566
567 vec_t dist1 = DotProduct(plane->normal, corners[0]) - plane->dist;
568 vec_t dist2 = DotProduct(plane->normal, corners[1]) - plane->dist;
569 int side = 0;
570 if (dist1 >= PLANESIDE_EPSILON)
571 side = PSIDE_FRONT;
572 if (dist2 < PLANESIDE_EPSILON)
573 side |= PSIDE_BACK;
574
575 return side;
576}
577
578typedef struct leaf_check_s {
580 int32_t* leaf_list;
583
589static void TR_BoxLeafnums_r (boxtrace_t* traceData, int32_t nodenum, leaf_check_t* lc)
590{
591 const TR_TILE_TYPE* myTile = traceData->tile;
592
593 while (1) {
594 if (nodenum <= LEAFNODE) {
595 if (lc->leaf_count >= lc->leaf_maxcount) {
596/* Com_Printf("CM_BoxLeafnums_r: overflow\n"); */
597 return;
598 }
599 lc->leaf_list[lc->leaf_count++] = -1 - nodenum;
600 return;
601 }
602
603 assert(nodenum < myTile->numnodes + 6); /* +6 => bbox */
604 const TR_NODE_TYPE* node = &myTile->nodes[nodenum];
605
606#ifdef COMPILE_UFO
607 const TR_PLANE_TYPE* plane = node->plane;
608#else
609 const TR_PLANE_TYPE* plane = myTile->planes + node->planenum;
610#endif
611
612 const int s = TR_BoxOnPlaneSide(traceData->absmins, traceData->absmaxs, plane);
613 if (s == PSIDE_FRONT)
614 nodenum = node->children[0];
615 else if (s == PSIDE_BACK)
616 nodenum = node->children[1];
617 else { /* go down both */
618 if (lc->leaf_topnode == LEAFNODE)
619 lc->leaf_topnode = nodenum;
620 TR_BoxLeafnums_r(traceData, node->children[0], lc);
621 nodenum = node->children[1];
622 }
623 }
624}
625
633static int TR_BoxLeafnums_headnode (boxtrace_t* traceData, int32_t* list, int listsize, int32_t headnode)
634{
635 leaf_check_t lc;
636 lc.leaf_list = list;
637 lc.leaf_count = 0;
638 lc.leaf_maxcount = listsize;
640
641 assert(headnode < traceData->tile->numnodes + 6); /* +6 => bbox */
642 TR_BoxLeafnums_r(traceData, headnode, &lc);
643
644 return lc.leaf_count;
645}
646
647
656static void TR_ClipBoxToBrush (boxtrace_t* traceData, cBspBrush_t* brush, TR_LEAF_TYPE* leaf)
657{
658 if (!brush || !brush->numsides)
659 return;
660
661#ifdef COMPILE_UFO
662 const TR_BRUSHSIDE_TYPE* leadside = nullptr;
663#endif
664 const TR_TILE_TYPE* myTile = traceData->tile;
665
666 float enterfrac = -1.0;
667 float leavefrac = 1.0;
668 bool getout = false;
669 bool startout = false;
670
671 const TR_PLANE_TYPE* clipplane = nullptr;
672 int clipplanenum = 0;
673
674 float dist;
675 vec3_t ofs;
676 for (int i = 0; i < brush->numsides; i++) {
677 const TR_BRUSHSIDE_TYPE* side = &myTile->brushsides[brush->firstbrushside + i];
678#ifdef COMPILE_UFO
679 const TR_PLANE_TYPE* plane = side->plane;
680#else
681 const TR_PLANE_TYPE* plane = myTile->planes + side->planenum;
682#endif
683
685 if (!traceData->ispoint) { /* general box case */
686 /* push the plane out appropriately for mins/maxs */
687 for (int j = 0; j < 3; j++) {
688 if (plane->normal[j] < 0)
689 ofs[j] = traceData->maxs[j];
690 else
691 ofs[j] = traceData->mins[j];
692 }
693 dist = DotProduct(ofs, plane->normal);
694 dist = plane->dist - dist;
695 } else { /* special point case */
696 dist = plane->dist;
697 }
698
699 const float d1 = DotProduct(traceData->start, plane->normal) - dist;
700 const float d2 = DotProduct(traceData->end, plane->normal) - dist;
701
702 if (d2 > 0)
703 getout = true; /* endpoint is not in solid */
704 if (d1 > 0)
705 startout = true; /* startpoint is not in solid */
706
707 /* if completely in front of face, no intersection */
708 if (d1 > 0 && d2 >= d1)
709 return;
710
711 if (d1 <= 0 && d2 <= 0)
712 continue;
713
714 /* crosses face */
715 if (d1 > d2) { /* enter */
716 const float f = (d1 - DIST_EPSILON) / (d1 - d2);
717 if (f > enterfrac) {
718 enterfrac = f;
719 clipplane = plane;
720#ifdef COMPILE_MAP
721 clipplanenum = side->planenum;
722#else
723 leadside = side;
724#endif
725 }
726 } else { /* leave */
727 const float f = (d1 + DIST_EPSILON) / (d1 - d2);
728 if (f < leavefrac)
729 leavefrac = f;
730 }
731 }
732
733 if (!startout) { /* original point was inside brush */
734 traceData->trace.startsolid = true;
735 if (!getout)
736 traceData->trace.allsolid = true;
737 traceData->trace.leafnum = leaf - myTile->leafs;
738 return;
739 }
740 if (enterfrac < leavefrac) {
741 if (enterfrac > -1 && enterfrac < traceData->trace.fraction) {
742 if (enterfrac < 0)
743 enterfrac = 0;
744 traceData->trace.fraction = enterfrac;
745 traceData->trace.plane = *clipplane;
746 traceData->trace.planenum = clipplanenum;
747#ifdef COMPILE_UFO
748 traceData->trace.surface = leadside->surface;
749#endif
750 traceData->trace.contentFlags = brush->brushContentFlags;
751 traceData->trace.leafnum = leaf - myTile->leafs;
752 }
753 }
754}
755
759static void TR_TestBoxInBrush (boxtrace_t* traceData, cBspBrush_t* brush)
760{
761 vec3_t ofs;
762 const TR_TILE_TYPE* myTile = traceData->tile;
763
764 if (!brush || !brush->numsides)
765 return;
766
767 for (int i = 0; i < brush->numsides; i++) {
768 const TR_BRUSHSIDE_TYPE* side = &myTile->brushsides[brush->firstbrushside + i];
769#ifdef COMPILE_UFO
770 const TR_PLANE_TYPE* plane = side->plane;
771#else
772 const TR_PLANE_TYPE* plane = myTile->planes + side->planenum;
773#endif
774
776 /* general box case */
777 /* push the plane out appropriately for mins/maxs */
778 for (int j = 0; j < 3; j++) {
779 if (plane->normal[j] < 0)
780 ofs[j] = traceData->maxs[j];
781 else
782 ofs[j] = traceData->mins[j];
783 }
784 const float dist = plane->dist - DotProduct(ofs, plane->normal);
785
786 float d1 = DotProduct(traceData->start, plane->normal) - dist;
787
788 /* if completely in front of face, no intersection */
789 if (d1 > 0)
790 return;
791 }
792
793 /* inside this brush */
794 traceData->trace.startsolid = traceData->trace.allsolid = true;
795 traceData->trace.fraction = 0;
796 traceData->trace.contentFlags = brush->brushContentFlags;
797}
798
799
811static void TR_TraceToLeaf (boxtrace_t* traceData, int32_t leafnum)
812{
813 TR_TILE_TYPE* myTile = traceData->tile;
814
815 assert(leafnum > LEAFNODE);
816 assert(leafnum <= myTile->numleafs);
817
818 TR_LEAF_TYPE* leaf = &myTile->leafs[leafnum];
819
820 if (traceData->contents != MASK_ALL && (!(leaf->contentFlags & traceData->contents) || (leaf->contentFlags & traceData->rejects)))
821 return;
822
823 /* trace line against all brushes in the leaf */
824 for (int k = 0; k < leaf->numleafbrushes; k++) {
825 const int brushnum = myTile->leafbrushes[leaf->firstleafbrush + k];
826 cBspBrush_t* b = &myTile->brushes[brushnum];
827
828 if (b->checkcount == checkcount)
829 continue; /* already checked this brush in another leaf */
831
832 if (traceData->contents != MASK_ALL && (!(b->brushContentFlags & traceData->contents) || (b->brushContentFlags & traceData->rejects)))
833 continue;
834
835 TR_ClipBoxToBrush(traceData, b, leaf);
836 if (!traceData->trace.fraction)
837 return;
838 }
839}
840
841
845static void TR_TestInLeaf (boxtrace_t* traceData, int32_t leafnum)
846{
847 TR_TILE_TYPE* myTile = traceData->tile;
848
849 assert(leafnum > LEAFNODE);
850 assert(leafnum <= myTile->numleafs);
851
852 const TR_LEAF_TYPE* leaf = &myTile->leafs[leafnum];
853 /* If this leaf contains no flags we need to look for, then skip it. */
854 if (!(leaf->contentFlags & traceData->contents)) /* || (leaf->contentFlags & traceData->rejects) */
855 return;
856
857 /* trace line against all brushes in the leaf */
858 for (int k = 0; k < leaf->numleafbrushes; k++) {
859 const int brushnum = myTile->leafbrushes[leaf->firstleafbrush + k];
860 cBspBrush_t* b = &myTile->brushes[brushnum];
861 if (b->checkcount == checkcount)
862 continue; /* already checked this brush in another leaf */
864
865 if (!(traceData->contents && (b->brushContentFlags & traceData->contents)) || (b->brushContentFlags & traceData->rejects))
866 continue;
867 TR_TestBoxInBrush(traceData, b);
868 if (!traceData->trace.fraction)
869 return;
870 }
871}
872
873
891static void TR_RecursiveHullCheck (boxtrace_t* traceData, int32_t nodenum, float p1f, float p2f, const vec3_t p1, const vec3_t p2)
892{
893 if (traceData->trace.fraction <= p1f)
894 return; /* already hit something nearer */
895
896 /* if < 0, we are in a leaf node */
897 if (nodenum <= LEAFNODE) {
898 TR_TraceToLeaf(traceData, LEAFNODE - nodenum);
899 return;
900 }
901
902 assert(nodenum < MAX_MAP_NODES);
903
904 /* find the point distances to the seperating plane
905 * and the offset for the size of the box */
906 const TR_TILE_TYPE* myTile = traceData->tile;
907 const TR_NODE_TYPE* node = myTile->nodes + nodenum;
908
909#ifdef COMPILE_UFO
910 const TR_PLANE_TYPE* plane = node->plane;
911#else
912 assert(node->planenum < MAX_MAP_PLANES);
913 const TR_PLANE_TYPE* plane = myTile->planes + node->planenum;
914#endif
915
916 float t1, t2, offset;
917 if (AXIAL(plane)) {
918 const int type = plane->type;
919 t1 = p1[type] - plane->dist;
920 t2 = p2[type] - plane->dist;
921 offset = traceData->extents[type];
922 } else {
923 t1 = DotProduct(plane->normal, p1) - plane->dist;
924 t2 = DotProduct(plane->normal, p2) - plane->dist;
925 if (traceData->ispoint)
926 offset = 0;
927 else
928 offset = fabs(traceData->extents[0] * plane->normal[0])
929 + fabs(traceData->extents[1] * plane->normal[1])
930 + fabs(traceData->extents[2] * plane->normal[2]);
931 }
932
933 /* see which sides we need to consider */
934 if (t1 >= offset && t2 >= offset) {
935 TR_RecursiveHullCheck(traceData, node->children[0], p1f, p2f, p1, p2);
936 return;
937 } else if (t1 < -offset && t2 < -offset) {
938 TR_RecursiveHullCheck(traceData, node->children[1], p1f, p2f, p1, p2);
939 return;
940 }
941
942 /* put the crosspoint DIST_EPSILON pixels on the near side */
949 float frac, frac2;
950 int side;
951 if (t1 < t2) {
952 const float idist = 1.0 / (t1 - t2);
953 side = 1;
954 frac2 = (t1 + offset + DIST_EPSILON) * idist;
955 frac = (t1 - offset + DIST_EPSILON) * idist;
956 } else if (t1 > t2) {
957 const float idist = 1.0 / (t1 - t2);
958 side = 0;
959 frac2 = (t1 - offset - DIST_EPSILON) * idist;
960 frac = (t1 + offset + DIST_EPSILON) * idist;
961 } else {
962 side = 0;
963 frac = 1;
964 frac2 = 0;
965 }
966
967 /* move up to the node */
968 if (frac < 0)
969 frac = 0;
970 else if (frac > 1)
971 frac = 1;
972
973 float midf = p1f + (p2f - p1f) * frac;
974 vec3_t mid;
975 VectorInterpolation(p1, p2, frac, mid);
976 TR_RecursiveHullCheck(traceData, node->children[side], p1f, midf, p1, mid);
977
978 /* go past the node */
979 if (frac2 < 0)
980 frac2 = 0;
981 else if (frac2 > 1)
982 frac2 = 1;
983
984 midf = p1f + (p2f - p1f) * frac2;
985 VectorInterpolation(p1, p2, frac2, mid);
986 TR_RecursiveHullCheck(traceData, node->children[side ^ 1], midf, p2f, mid, p2);
987}
988
1003trace_t TR_BoxTrace (boxtrace_t& traceData, const Line& traceLine, const AABB& traceBox, const int headnode, const float fraction)
1004{
1005 checkcount++; /* for multi-check avoidance */
1006
1007#ifdef COMPILE_UFO
1008 if (headnode >= traceData.tile->numnodes + 6)
1009 Com_Error(ERR_DROP, "headnode (%i) is out of bounds: %i", headnode, traceData.tile->numnodes + 6);
1010#else
1011 assert(headnode < traceData.tile->numnodes + 6); /* +6 => bbox */
1012#endif
1013
1014 if (!traceData.tile->numnodes) /* map not loaded */
1015 return traceData.trace;
1016
1017 /* check for position test special case */
1018 if (VectorEqual(traceData.start, traceData.end)) {
1019 int32_t leafs[MAX_LEAFS];
1020
1021 VectorAdd(traceData.start, traceData.maxs, traceData.absmaxs);
1022 VectorAdd(traceData.start, traceData.mins, traceData.absmins);
1023
1024 const int numleafs = TR_BoxLeafnums_headnode(&traceData, leafs, MAX_LEAFS, headnode);
1025 for (int i = 0; i < numleafs; i++) {
1026 TR_TestInLeaf(&traceData, leafs[i]);
1027 if (traceData.trace.allsolid)
1028 break;
1029 }
1030 VectorCopy(traceLine.start, traceData.trace.endpos);
1031 return traceData.trace;
1032 }
1033
1034 /* check for point special case */
1035 if (VectorEmpty(traceData.mins) && VectorEmpty(traceData.maxs)) {
1036 traceData.ispoint = true;
1037 VectorClear(traceData.extents);
1038 } else {
1039 traceData.ispoint = false;
1040 VectorCopy(traceData.maxs, traceData.extents);
1041 }
1042
1043 /* general sweeping through world */
1045 TR_RecursiveHullCheck(&traceData, headnode, 0.0f, 1.0f, traceData.start, traceData.end);
1046
1047 if (traceData.trace.fraction >= 1.0f) {
1048 VectorCopy(traceData.end, traceData.trace.endpos);
1049 } else {
1050 VectorInterpolation(traceData.start, traceData.end, traceData.trace.fraction, traceData.trace.endpos);
1051 }
1052 /* Now un-offset the end position. */
1053 VectorSubtract(traceData.trace.endpos, traceData.offset, traceData.trace.endpos);
1054 return traceData.trace;
1055}
1056
1067trace_t TR_TileBoxTrace (TR_TILE_TYPE* myTile, const Line& traceLine, const AABB& aabb, const int levelmask, const int brushmask, const int brushreject)
1068{
1069 int i;
1070 cBspHead_t* h;
1071 trace_t tr;
1072
1073 /* ensure that the first trace is set in every case */
1074 tr.fraction = 2.0f;
1075
1076 boxtrace_t traceData;
1077 traceData.init(myTile, brushmask, brushreject, tr.fraction);
1078 traceData.setLineAndBox(traceLine, aabb);
1079 /* trace against all loaded map tiles */
1080 for (i = 0, h = myTile->cheads; i < myTile->numcheads; i++, h++) {
1081 /* This code uses levelmask to limit by maplevel. Supposedly maplevels 1-255
1082 * are bitmasks of game levels 1-8. 0 is a special case repeat of 255.
1083 * However a levelmask including 0x100 is usually included so the CLIP levels are
1084 * examined. */
1085 if (h->level && h->level <= LEVEL_LASTVISIBLE && levelmask && !(h->level & levelmask))
1086 continue;
1087
1088 assert(h->cnode < myTile->numnodes + 6); /* +6 => bbox */
1089 const trace_t newtr = TR_BoxTrace(traceData, traceLine, aabb, h->cnode, tr.fraction);
1090
1091 /* memorize the trace with the minimal fraction */
1092 if (newtr.fraction == 0.0)
1093 return newtr;
1094 if (newtr.fraction < tr.fraction)
1095 tr = newtr;
1096 }
1097 return tr;
1098}
1099
1100#ifdef COMPILE_MAP
1101
1112trace_t TR_SingleTileBoxTrace (mapTiles_t* mapTiles, const Line& traceLine, const AABB* traceBox, const int levelmask, const int brushmask, const int brushreject)
1113{
1114 /* Trace the whole line against the first tile. */
1115 trace_t tr = TR_TileBoxTrace(&mapTiles->mapTiles[0], traceLine, *traceBox, levelmask, brushmask, brushreject);
1116 tr.mapTile = 0;
1117 return tr;
1118}
1119#endif
Definition aabb.h:42
vec3_t maxs
Definition aabb.h:258
vec3_t mins
Definition aabb.h:257
void getCenter(vec3_t center) const
Calculates the center of the bounding box.
Definition aabb.h:155
Definition line.h:31
vec3_t start
Definition line.h:54
vec3_t stop
Definition line.h:55
Stores the data of a map tile, mostly the BSP stuff.
Definition typedefs.h:85
char name[MAX_QPATH]
Definition typedefs.h:87
ipos3_t wpMaxs
Definition typedefs.h:131
ipos3_t wpMins
Definition typedefs.h:130
void Com_Error(int code, const char *fmt,...)
Definition common.cpp:459
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
definitions common between client and server, but not game lib
#define ERR_DROP
Definition common.h:211
static transfer_t tr
#define ON_EPSILON
Definition defines.h:374
#define TL_FLAG_ACTORCLIP
Definition defines.h:359
#define PLANE_Y
Definition defines.h:192
#define MASK_ALL
Definition defines.h:271
#define TL_FLAG_REGULAR_LEVELS
Definition defines.h:358
#define PLANE_X
Definition defines.h:191
#define LEAFNODE
Definition defines.h:44
#define MAX_LEAFS
Definition defines.h:152
#define DIST_EPSILON
Definition defines.h:377
#define LEVEL_LIGHTCLIP
Definition defines.h:349
#define LEVEL_LASTVISIBLE
Definition defines.h:348
#define CONTENTS_SOLID
Definition defines.h:223
#define MAX_MAP_NODES
Definition defines.h:140
#define PLANENUM_LEAF
Definition defines.h:45
#define CONTENTS_PASSABLE
Definition defines.h:244
#define PLANE_Z
Definition defines.h:193
#define PSIDE_BACK
Definition defines.h:368
#define PLANESIDE_EPSILON
Definition defines.h:38
#define LEVEL_WEAPONCLIP
Definition defines.h:351
#define MAX_MAP_PLANES
Definition defines.h:139
#define MASK_CLIP
Definition defines.h:278
#define TL_FLAG_WEAPONCLIP
Definition defines.h:360
#define PSIDE_FRONT
Definition defines.h:367
#define LEVEL_ACTORCLIP
Definition defines.h:352
#define PLANE_NONE
Definition defines.h:199
#define AXIAL(p)
Definition defines.h:201
level_locals_t level
Definition g_main.cpp:38
void Sys_Error(const char *error,...)
Definition g_main.cpp:421
voidpf uLong offset
Definition ioapi.h:45
bool VectorNearer(const vec3_t v1, const vec3_t v2, const vec3_t comp)
Checks whether the given vector v1 is closer to comp as the vector v2.
Definition mathlib.cpp:219
int VectorCompareEps(const vec3_t v1, const vec3_t v2, float epsilon)
Compare two vectors that may have an epsilon difference but still be the same vectors.
Definition mathlib.cpp:413
#define EQUAL_EPSILON
Definition mathlib.h:40
QGL_EXTERN GLfloat f
Definition r_gl.h:114
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLint GLenum type
Definition r_gl.h:94
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
vec3_t absmaxs
Definition tracing.h:97
uint32_t contents
Definition tracing.h:102
bool ispoint
Definition tracing.h:104
TR_TILE_TYPE * tile
Definition tracing.h:106
vec3_t offset
Definition tracing.h:99
vec3_t end
Definition tracing.h:95
vec3_t maxs
Definition tracing.h:96
trace_t trace
Definition tracing.h:101
vec3_t start
Definition tracing.h:95
void setLineAndBox(const Line &line, const AABB &box)
void init(TR_TILE_TYPE *_tile, const int contentmask, const int brushreject, const float fraction)
vec3_t extents
Definition tracing.h:98
vec3_t absmins
Definition tracing.h:97
vec3_t mins
Definition tracing.h:96
uint32_t rejects
Definition tracing.h:103
int checkcount
Definition typedefs.h:63
int firstbrushside
Definition typedefs.h:62
uint32_t brushContentFlags
Definition typedefs.h:60
int numsides
Definition typedefs.h:61
int level
Definition typedefs.h:79
int cnode
Definition typedefs.h:78
int32_t * leaf_list
Definition tracing.cpp:580
int32_t leaf_topnode
Definition tracing.cpp:581
int leaf_maxcount
Definition tracing.cpp:579
Data for line tracing (?).
Definition typedefs.h:69
int32_t children[2]
Definition typedefs.h:73
int type
Definition typedefs.h:70
float dist
Definition typedefs.h:72
vec3_t normal
Definition typedefs.h:71
vec3_t endpos
Definition tracing.h:59
cBspSurface_t * surface
Definition tracing.h:61
int planenum
Definition tracing.h:62
float fraction
Definition tracing.h:58
uint32_t contentFlags
Definition tracing.h:63
TR_PLANE_TYPE plane
Definition tracing.h:60
bool startsolid
Definition tracing.h:57
int32_t leafnum
Definition tracing.h:64
bool allsolid
Definition tracing.h:56
static mapTiles_t mapTiles
static ipos3_t wpMins
world min and max values converted from vec to pos
Definition routing.cpp:35
static ipos3_t wpMaxs
Definition routing.cpp:35
static void TR_TestInLeaf(boxtrace_t *traceData, int32_t leafnum)
Definition tracing.cpp:845
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
static void TR_TestBoxInBrush(boxtrace_t *traceData, cBspBrush_t *brush)
Definition tracing.cpp:759
static bool TR_TileTestLineDM(TR_TILE_TYPE *tile, 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:414
static bool TR_TileTestLine(TR_TILE_TYPE *tile, const vec3_t start, const vec3_t end, const int levelmask)
Tests to see if a line intersects any brushes in a tile.
Definition tracing.cpp:279
static int TR_BoxLeafnums_headnode(boxtrace_t *traceData, int32_t *list, int listsize, int32_t headnode)
Fill a list of leafnodes that the trace touches.
Definition tracing.cpp:633
static int checkcount
Definition tracing.cpp:72
void TR_BuildTracingNode_r(TR_TILE_TYPE *tile, tnode_t **tnode, int32_t nodenum, int level)
Definition tracing.cpp:122
static void TR_RecursiveHullCheck(boxtrace_t *traceData, int32_t nodenum, float p1f, float p2f, const vec3_t p1, const vec3_t p2)
This recursive function traces through the bsp tree looking to see if a brush can be found that inter...
Definition tracing.cpp:891
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
static void TR_TraceToLeaf(boxtrace_t *traceData, int32_t leafnum)
This function checks if the specified leaf matches any mask specified in traceData....
Definition tracing.cpp:811
static void TR_ClipBoxToBrush(boxtrace_t *traceData, cBspBrush_t *brush, TR_LEAF_TYPE *leaf)
This function checks to see if any sides of a brush intersect the line from p1 to p2 or are located w...
Definition tracing.cpp:656
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
static void TR_BoxLeafnums_r(boxtrace_t *traceData, int32_t nodenum, leaf_check_t *lc)
Fills in a list of all the leafs touched call with topnode set to the headnode, returns with topnode ...
Definition tracing.cpp:589
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
int TR_TestLine_r(TR_TILE_TYPE *tile, int32_t nodenum, const vec3_t start, const vec3_t end)
Definition tracing.cpp:209
int TR_BoxOnPlaneSide(const vec3_t mins, const vec3_t maxs, const TR_PLANE_TYPE *plane)
Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH.
Definition tracing.cpp:542
static void TR_MakeTracingNode(TR_TILE_TYPE *tile, tnode_t **tnode, int32_t nodenum)
Converts the disk node structure into the efficient tracing structure for LineTraces.
Definition tracing.cpp:83
static int TR_TestLineDist_r(TR_TILE_TYPE *tile, int32_t nodenum, const vec3_t start, const vec3_t end, vec3_t tr_end)
Definition tracing.cpp:336
Tracing functions.
float vec_t
Definition ufotypes.h:37
vec_t vec3_t[3]
Definition ufotypes.h:39
#define VectorClear(a)
Definition vector.h:55
#define VectorEqual(a, b)
Definition vector.h:65
#define VectorInterpolation(p1, p2, frac, mid)
Definition vector.h:80
#define VectorSubtract(a, b, dest)
Definition vector.h:45
#define VectorCopy(src, dest)
Definition vector.h:51
#define VectorEmpty(a)
Definition vector.h:73
#define VectorAdd(a, b, dest)
Definition vector.h:47
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition vector.h:44
#define VectorSet(v, x, y, z)
Definition vector.h:59