UFO: Alien Invasion
Loading...
Searching...
No Matches
routing.cpp
Go to the documentation of this file.
1
5
6/*
7All original material Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9Copyright (C) 1997-2001 Id Software, Inc.
10
11This program is free software; you can redistribute it and/or
12modify it under the terms of the GNU General Public License
13as published by the Free Software Foundation; either version 2
14of the License, or (at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
20See the GNU General Public License for more details.
21
22You should have received a copy of the GNU General Public License
23along with this program; if not, write to the Free Software
24Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26*/
27
28#include "tracing.h"
29#include "routing.h"
30#include "common.h"
31
32/*
33===============================================================================
34MAP TRACING DEBUGGING TABLES
35===============================================================================
36*/
37
38bool debugTrace = false;
39
40/*
41==========================================================
42 LOCAL CONSTANTS
43==========================================================
44*/
45
46#define RT_NO_OPENING -1
47
48/* Width of the box required to stand in a cell by an actor's feet. */
49#define halfMicrostepSize (PATHFINDING_MICROSTEP_SIZE / 2 - DIST_EPSILON)
50/* This is a template for the extents of the box used by an actor's feet. */
52
53/* Width of the box required to stand in a cell by an actor's torso. */
54#define half1x1Width (UNIT_SIZE * 1 / 2 - WALL_SIZE - DIST_EPSILON)
55#define half2x2Width (UNIT_SIZE * 2 / 2 - WALL_SIZE - DIST_EPSILON)
56/* These are templates for the extents of the box used by an actor's torso. */
59
60/*
61==========================================================
62 LOCAL TYPES
63==========================================================
64*/
65#if defined(COMPILE_MAP)
66 #define RT_COMPLETEBOXTRACE_SIZE(mapTiles, b, list, lvl) TR_SingleTileBoxTrace((mapTiles), Line(), (b), (lvl), MASK_NO_LIGHTCLIP, 0)
67 #define RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, line, b, list) TR_SingleTileBoxTrace((mapTiles), (line), (b), TRACE_ALL_LEVELS, MASK_IMPASSABLE, MASK_PASSABLE)
68
69#elif defined(COMPILE_UFO)
70 #define RT_COMPLETEBOXTRACE_SIZE(mapTiles, b, list, lvl) CM_EntCompleteBoxTrace((mapTiles), Line(), (b), (lvl), MASK_NO_LIGHTCLIP, 0, (list))
71 #define RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, line, b, list) CM_EntCompleteBoxTrace((mapTiles), (line), (b), TRACE_ALL_LEVELS, MASK_IMPASSABLE, MASK_PASSABLE, (list))
72
73#else
74 #error Either COMPILE_MAP or COMPILE_UFO must be defined in order for tracing.c to work.
75#endif
76
90
92{
93 this->mapTiles = mapTiles;
94 this->actorSize = actorSize;
95 this->list = list;
96}
97
98static inline void RT_StepupSet (RoutingData* rtd, const int x, const int y, const int z, const int dir, const int val)
99{
100 rtd->routing.setStepup(rtd->actorSize, x, y, z, dir, val);
101}
102
103static inline void RT_ConnSetNoGo (RoutingData* rtd, const int x, const int y, const int z, const int dir)
104{
105 rtd->routing.setConn(rtd->actorSize, x, y, z, dir, 0);
106 rtd->routing.setStepup(rtd->actorSize, x, y, z, dir, PATHFINDING_NO_STEPUP);
107}
108
113typedef struct place_s {
115 int floor;
117 int floorZ;
118 bool usable;
119
120 inline bool isUsable (void) const
121 {
122 return usable;
123 }
124} place_t;
125
126static inline void RT_PlaceInit (const Routing& routing, const actorSizeEnum_t actorSize, place_t* p, const int x, const int y, const int z)
127{
128 p->cell[0] = x;
129 p->cell[1] = y;
130 p->cell[2] = z;
131 const int relCeiling = routing.getCeiling(actorSize, p->cell);
132 p->floor = routing.getFloor(actorSize, x, y, z) + z * CELL_HEIGHT;
133 p->ceiling = relCeiling + z * CELL_HEIGHT;
134 p->floorZ = std::max(0, p->floor / CELL_HEIGHT) ;
135 p->usable = (relCeiling && p->floor > -1 && p->ceiling - p->floor >= PATHFINDING_MIN_OPENING) ? true : false;
136}
137
138static inline bool RT_PlaceDoesIntersectEnough (const place_t* p, const place_t* other)
139{
140 return (std::min(p->ceiling, other->ceiling) - std::max(p->floor, other->floor) >= PATHFINDING_MIN_OPENING);
141}
142
149static inline int RT_PlaceIsShifted (const place_t* p, const place_t* other)
150{
151 if (!p->isUsable() || !other->isUsable())
152 return 0;
153 if (p->floor < other->floor && p->ceiling < other->ceiling)
154 return 1; /* stepping up */
155 if (p->floor > other->floor && p->ceiling > other->ceiling)
156 return 2; /* stepping down */
157 return 0;
158}
159
164typedef struct opening_s {
165 int size;
166 int base;
167 int stepup;
169} opening_t;
170
171/*
172==========================================================
173 GRID ORIENTED MOVEMENT AND SCANNING
174==========================================================
175*/
176
177#ifdef DEBUG
189static void RT_DumpMap (const Routing& routing, actorSizeEnum_t actorSize, int lx, int ly, int lz, int hx, int hy, int hz)
190{
191 Com_Printf("\nRT_DumpMap (%i %i %i) (%i %i %i)\n", lx, ly, lz, hx, hy, hz);
192 for (int z = hz; z >= lz; --z) {
193 Com_Printf("\nLayer %i:\n ", z);
194 for (int x = lx; x <= hx; ++x) {
195 Com_Printf("%9i", x);
196 }
197 Com_Printf("\n");
198 for (int y = hy; y >= ly; --y) {
199 Com_Printf("%3i ", y);
200 for (int x = lx; x <= hx; ++x) {
201 Com_Printf("%s%s%s%s "
202 , RT_CONN_NX(routing, actorSize, x, y, z) ? "w" : " "
203 , RT_CONN_PY(routing, actorSize, x, y, z) ? "n" : " "
204 , RT_CONN_NY(routing, actorSize, x, y, z) ? "s" : " "
205 , RT_CONN_PX(routing, actorSize, x, y, z) ? "e" : " "
206 );
207 }
208 Com_Printf("\n");
209 }
210 }
211}
212
217void RT_DumpWholeMap (mapTiles_t* mapTiles, const Routing& routing)
218{
219 AABB mapBox;
220 RT_GetMapSize(mapTiles, mapBox);
221
222 /* convert the coords */
223 pos3_t start, end;
224 VecToPos(mapBox.mins, start);
225 VecToPos(mapBox.maxs, end);
226 start[0]--;
227 start[1]--;
228
229 /* Dump the client map */
230 RT_DumpMap(routing, ACTOR_SIZE_NORMAL, start[0], start[1], start[2], end[0], end[1], end[2]);
231}
232#endif
233
237bool RT_CanActorStandHere (const Routing& routing, const int actorSize, const pos3_t pos)
238{
239 if (routing.getCeiling(actorSize, pos) - routing.getFloor(actorSize, pos) >= PLAYER_STANDING_HEIGHT / QUANT)
240 return true;
241 else
242 return false;
243}
244
254{
255 const vec3_t normal = {UNIT_SIZE / 2, UNIT_SIZE / 2, UNIT_HEIGHT / 2};
256 pos3_t start, end, test;
257
258 /* Initialize start, end, and normal */
259 VectorClear(start);
261
262 for (int i = 0; i < 3; i++) {
263 AABB box;
264 /* Lower positive boundary */
265 while (end[i] > start[i]) {
266 /* Adjust ceiling */
267 VectorCopy(start, test);
268 test[i] = end[i];
269 /* Prep boundary box */
270 PosToVec(test, box.mins);
271 VectorSubtract(box.mins, normal, box.mins);
272 PosToVec(end, box.maxs);
273 VectorAdd(box.maxs, normal, box.maxs);
274 /* Test for stuff in a small box, if there is something then exit while */
275 const trace_t trace = RT_COMPLETEBOXTRACE_SIZE(mapTiles, &box, nullptr, TRACE_VISIBLE_LEVELS);
276 if (trace.fraction < 1.0)
277 break;
278 /* There is nothing, lower the boundary. */
279 end[i]--;
280 }
281
282 /* Raise negative boundary */
283 while (end[i] > start[i]) {
284 /* Adjust ceiling */
285 VectorCopy(end, test);
286 test[i] = start[i];
287
288 /* Prep boundary box */
289 PosToVec(start, box.mins);
290 VectorSubtract(box.mins, normal, box.mins);
291 PosToVec(test, box.maxs);
292 VectorAdd(box.maxs, normal, box.maxs);
293 box.maxs[i] -= DIST_EPSILON; /* stay away from the upper bound just a little bit, so we don't catch a touching brush */
294
295 /* Test for stuff in the box, if there is something then exit while */
296 const trace_t trace = RT_COMPLETEBOXTRACE_SIZE(mapTiles, &box, nullptr, TRACE_VISIBLE_LEVELS);
297 if (trace.fraction < 1.0)
298 break;
299 /* There is nothing, raise the boundary. */
300 start[i]++;
301 }
302 }
303
304 /* Com_Printf("Extents: (%i, %i, %i) to (%i, %i, %i)\n", start[0], start[1], start[2], end[0], end[1], end[2]); */
305
306 /* convert to vectors */
307 PosToVec(start, mapBox.mins);
308 PosToVec(end, mapBox.maxs);
309
310 /* Stretch to the exterior edges of our extents */
311 VectorSubtract(mapBox.mins, normal, mapBox.mins);
312 VectorAdd(mapBox.maxs, normal, mapBox.maxs);
313}
314
315
316/*
317===============================================================================
318NEW MAP TRACING FUNCTIONS
319===============================================================================
320*/
321
331bool RT_AllCellsBelowAreFilled (const Routing& routing, const int actorSize, const pos3_t pos)
332{
333 /* the -1 level is considered solid ground */
334 if (pos[2] == 0)
335 return true;
336
337 for (int i = 0; i < pos[2]; i++) {
338 if (routing.getCeiling(actorSize, pos[0], pos[1], i) != 0)
339 return false;
340 }
341 return true;
342}
343
360int RT_CheckCell (mapTiles_t* mapTiles, Routing& routing, const int actorSize, const int x, const int y, const int z, const char** list)
361{
362 /* Width of the box required to stand in a cell by an actor's torso. */
363 const float halfActorWidth = UNIT_SIZE * actorSize / 2 - WALL_SIZE - DIST_EPSILON;
364 /* This is a template for the extents of the box used by an actor's legs. */
365 const AABB legBox(-halfMicrostepSize, -halfMicrostepSize, 0,
367 /* This is a template for the extents of the box used by an actor's torso. */
368 const AABB torsoBox(-halfActorWidth, -halfActorWidth, QuantToModel(PATHFINDING_LEGROOMHEIGHT),
369 halfActorWidth, halfActorWidth, QuantToModel(PATHFINDING_MIN_OPENING) - DIST_EPSILON * 2);
370 /* This is a template for the ceiling trace after an actor's torso space has been found. */
371 const AABB ceilBox(-halfActorWidth, -halfActorWidth, 0,
372 halfActorWidth, halfActorWidth, 0);
373
374 vec3_t start, end; /* Start and end of the downward traces. */
375 vec3_t tstart, tend; /* Start and end of the upward traces. */
376 AABB box; /* Holds the exact bounds to be traced for legs and torso. */
377 pos3_t pos;
378 float bottom, top; /* Floor and ceiling model distances from the cell base. (in mapunits) */
379
380 assert(actorSize > ACTOR_SIZE_INVALID && actorSize <= ACTOR_MAX_SIZE);
381 /* x and y cannot exceed PATHFINDING_WIDTH - actorSize */
382 assert((x >= 0) && (x <= PATHFINDING_WIDTH - actorSize));
383 assert((y >= 0) && (y <= PATHFINDING_WIDTH - actorSize));
384 assert(z < PATHFINDING_HEIGHT);
385
386 /* calculate tracing coordinates */
387 VectorSet(pos, x, y, z);
388 SizedPosToVec(pos, actorSize, end); /* end is now at the center of the cells the actor occupies. */
389
390 /* prepare fall down check */
391 VectorCopy(end, start);
392 /*
393 * Adjust these points so that start to end is from the top of the cell to the bottom of the model.
394 */
395#ifdef DEBUG
396 float initial = start[2] + UNIT_HEIGHT / 2; /* This is the top-most starting point in this cell. */
397#endif
398 start[2] += UNIT_HEIGHT / 2 - QUANT; /* This one QUANT unit below initial. */
399 end[2] = -UNIT_HEIGHT * 2; /* To the bottom of the model! (Plus some for good measure) */
400
401 /*
402 * Trace for a floor. Steps:
403 * 1. Start at the top of the designated cell and scan toward the model's base.
404 * 2. If we do not find a brush, then this cell is bottomless and not enterable.
405 * 3. We have found an upward facing brush. Scan up PATHFINDING_LEGROOMHEIGHT height.
406 * 4. If we find anything, then this space is too small of an opening. Restart just below our current floor.
407 * 5. Trace up towards the model ceiling with a box as large as the actor. The first obstruction encountered
408 * marks the ceiling. If there are no obstructions, the model ceiling is the ceiling.
409 * 6. If the opening between the floor and the ceiling is not at least PATHFINDING_MIN_OPENING tall, then
410 * restart below the current floor.
411 */
412 for (;;) { /* Loop forever, we will exit if we hit the model bottom or find a valid floor. */
413
414 /* Check if a previous iteration has brought us too close to the bottom of the model. */
415 if (start[2] <= QuantToModel(PATHFINDING_MIN_OPENING)) {
416 /* There is no useable brush underneath this starting point. */
417 routing.setFilled(actorSize, x, y, 0, z); /* mark all cells to the model base as filled. */
418 return 0; /* return (a z-value of)0 to indicate we just scanned the model bottom. */
419 }
420
421 trace_t tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, Line(start, end), &footBox, list);
422 if (tr.fraction >= 1.0) { /* If there is no brush underneath this starting point, */
423 routing.setFilled(actorSize, x, y, 0, z); /* mark all cells to the model base as filled. */
424 return 0; /* return (a z-value of)0 to indicate we just scanned the model bottom. */
425 }
426
427 /* We have hit a brush that faces up and can be stood on. A potential floor. Look for a ceiling. */
428 bottom = tr.endpos[2]; /* record the floor position. */
429
430#ifdef DEBUG
431 assert(initial > bottom);
432#endif
433
434 /* Record the hit position in tstart for later use. */
435 VectorCopy(tr.endpos, tstart);
436
437 /* Prep the start and end of the "leg room" test. */
438 box.set(legBox);
439 box.shift(tstart); /* Now box has the lower and upper required foot space extent */
440
441 tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, Line(), &box, list);
442 if (tr.fraction < 1.0) {
443 /*
444 * There is a premature obstruction. We can't use this as a floor.
445 * Check under start. We need to have at least the minimum amount of clearance from our ceiling,
446 * So start at that point.
447 */
448 start[2] = bottom - QuantToModel(PATHFINDING_MIN_OPENING);
449 /* Restart with the new start[] value */
450 continue;
451 }
452
453 /* Prep the start and end of the "torso room" test. */
454 box.set(torsoBox);
455 box.shift(tstart); /* Now box has the lower and upper required torso space extent */
456
457 tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, Line(), &box, list);
458 if (tr.fraction < 1.0) {
459 /*
460 * There is a premature obstruction. We can't use this as a floor.
461 * Check under start. We need to have at least the minimum amount of clearance from our ceiling,
462 * So start at that point.
463 */
464 start[2] = bottom - QuantToModel(PATHFINDING_MIN_OPENING);
465 /* Restart */
466 continue;
467 }
468
469 /*
470 * If we are here, then the immediate floor is unobstructed MIN_OPENING units high.
471 * This is a valid floor. Find the actual ceiling.
472 */
473
474 tstart[2] = box.maxs[2]; /* The box trace for height starts at the top of the last trace. */
475 VectorCopy(tstart, tend);
476 tend[2] = PATHFINDING_HEIGHT * UNIT_HEIGHT; /* tend now reaches the model ceiling. */
477
478 tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, Line(tstart, tend), &ceilBox, list);
479
480 /* We found the ceiling. */
481 top = tr.endpos[2];
482
483 /*
484 * There is one last possibility:
485 * If our found ceiling is above the cell we started the scan in, then we may have scanned up through another
486 * floor (one sided brush). If this is the case, we set the ceiling to QUANT below the floor of the above
487 * ceiling if it is lower than our found ceiling.
488 */
489 if (tr.endpos[2] > (z + 1) * UNIT_HEIGHT) {
490 const float topf = (z + 1) * UNIT_HEIGHT + QuantToModel(routing.getFloor(actorSize, x, y, z + 1) - 1);
491 top = std::min(tr.endpos[2], topf);
492 }
493
494 /* We found the ceiling. */
495 top = tr.endpos[2];
496
497 /* exit the infinite while loop */
498 break;
499 }
500
501 UFO_assert(bottom <= top, "\nassert(bottom <= top): x=%i y=%i bottom=%f top=%f\n", x, y, bottom, top);
502
503 /* top and bottom are absolute model heights. Find the actual cell z coordinates for these heights.
504 * ...but before rounding, give back the DIST_EPSILON that was added by the trace.
505 * Actually, we have to give back two DIST_EPSILON to prevent rounding issues */
506 bottom -= 2 * DIST_EPSILON;
507 top += 2 * DIST_EPSILON;
508 const int bottomQ = ModelFloorToQuant(bottom); /* Convert to QUANT units to ensure the floor is rounded up to the correct value. */
509 const int topQ = ModelCeilingToQuant(top); /* Convert to QUANT units to ensure the floor is rounded down to the correct value. */
510 const int fz = floorf(bottomQ / CELL_HEIGHT); /* Ensure we round down to get the bottom-most affected cell */
512 const int cz = std::min(z, (int)(floorf((topQ - 1) / CELL_HEIGHT))); /* Use the lower of z or the calculated ceiling */
513
514 assert(fz <= cz);
515
516 /* Last, update the floors and ceilings of cells from (x, y, fz) to (x, y, cz) */
517 for (int i = fz; i <= cz; i++) {
518 /* Round up floor to keep feet out of model. */
519 routing.setFloor(actorSize, x, y, i, bottomQ - i * CELL_HEIGHT);
520 /* Round down ceiling to heep head out of model. Also offset by floor and max at 255. */
521 routing.setCeiling(actorSize, x, y, i, topQ - i * CELL_HEIGHT);
522 }
523
524 /* Also, update the floors of any filled cells immediately above the ceiling up to our original cell. */
525 routing.setFilled(actorSize, x, y, cz + 1, z);
526
527 /* Return the lowest z coordinate that we updated floors for. */
528 return fz;
529}
530
531
541static int RT_FillPassageData (RoutingData* rtd, const int dir, const int x, const int y, const int z, const int openingSize, const int openingBase, const int stepup)
542{
543 const int openingTop = openingBase + openingSize;
544
545 /* Final interpretation:
546 * We now have the floor and the ceiling of the passage traveled between the two cells.
547 * This span may cover many cells vertically. We can use this to our advantage:
548 * +Like in the floor tracing, we can assign the direction value for multiple cells and
549 * skip some scans.
550 * +The value of each current cell will list the max allowed height of an actor in the passageway,
551 * which also can be used to see if an actor can fly upward.
552 * +The allowed height will be based off the floor in the cell or the bottom of the cell; we do not
553 * want super tall characters to fly through ceilings.
554 * +To see if an actor can fly down, we check the cells on level down to see if the diagonal movement
555 * can be made and that both have ceilings above the current level.
556 */
557
559 const int fz = z;
560 int cz = ceil((float)openingTop / CELL_HEIGHT) - 1;
561 cz = std::min(PATHFINDING_HEIGHT - 1, cz);
562
563 /* last chance- if cz < z, then bail (and there is an error with the ceiling data somewhere */
564 if (cz < z) {
565 /* We can't go this way. */
566 RT_ConnSetNoGo(rtd, x, y, z, dir); /* Passage found but below current cell */
567 return z;
568 }
569
570 /* Passage found */
571
572 assert(fz <= z && z <= cz);
573
574 /* Last, update the connections of cells from (x, y, fz) to (x, y, cz) for direction dir */
575 for (int i = fz; i <= cz; i++) {
576 /* Offset from the floor or the bottom of the current cell, whichever is higher. */
577 const int oh = openingTop - std::max(openingBase, i * CELL_HEIGHT);
578 /* Only if > 0 */
579 assert (oh >= 0);
580 rtd->routing.setConn(rtd->actorSize, x, y, i, dir, oh);
581 /* The stepup is 0 for all cells that are not at the floor. */
582 RT_StepupSet(rtd, x, y, i, dir, 0);
583 }
584
585 RT_StepupSet(rtd, x, y, z, dir, stepup);
586
587 /*
588 * Return the highest z coordinate scanned- cz if fz==cz, z==cz, or the floor in cz is negative.
589 * Otherwise cz - 1 to recheck cz in case there is a floor in cz with its own ceiling.
590 */
591 if (fz == cz || z == cz || rtd->routing.getFloor(rtd->actorSize, x, y, cz) < 0)
592 return cz;
593 return cz - 1;
594}
595
603static trace_t RT_ObstructedTrace (const RoutingData* rtd, const Line& traceLine, int hi, int lo)
604{
605 AABB box;
606 const float halfActorWidth = UNIT_SIZE * rtd->actorSize / 2 - WALL_SIZE - DIST_EPSILON;
607
608 /* Configure the box trace extents. The box is relative to the original floor. */
609 VectorSet(box.maxs, halfActorWidth, halfActorWidth, QuantToModel(hi) - DIST_EPSILON);
610 VectorSet(box.mins, -halfActorWidth, -halfActorWidth, QuantToModel(lo) + DIST_EPSILON);
611
612 /* perform the trace, then return true if the trace was obstructed. */
613 return RT_COMPLETEBOXTRACE_PASSAGE(rtd->mapTiles, traceLine, &box, rtd->list);
614}
615
616
626static int RT_FindOpeningFloorFrac (const RoutingData* rtd, const vec3_t start, const vec3_t end, const float frac, const int startingHeight)
627{
628 vec3_t mstart, mend;
629 const AABB* box = (rtd->actorSize == ACTOR_SIZE_NORMAL ? &actor1x1Box : &actor2x2Box);
630
631 /* Position mstart and mend at the fraction point */
632 VectorInterpolation(start, end, frac, mstart);
633 VectorCopy(mstart, mend);
634 mstart[2] = QuantToModel(startingHeight) + (QUANT / 2); /* Set at the starting height, plus a little more to keep us off a potential surface. */
635 mend[2] = -QUANT; /* Set below the model. */
636
637 const trace_t tr = RT_COMPLETEBOXTRACE_PASSAGE(rtd->mapTiles, Line(mstart, mend), box, rtd->list);
638
639 /* OK, now we have the floor height value in tr.endpos[2].
640 * Divide by QUANT and round up.
641 */
642 return ModelFloorToQuant(tr.endpos[2] - DIST_EPSILON);
643}
644
645
655static int RT_FindOpeningCeilingFrac (const RoutingData* rtd, const vec3_t start, const vec3_t end, const float frac, const int startingHeight)
656{
657 vec3_t mstart, mend;
658 const AABB* box = (rtd->actorSize == ACTOR_SIZE_NORMAL ? &actor1x1Box : &actor2x2Box);
659
660 /* Position mstart and mend at the midpoint */
661 VectorInterpolation(start, end, frac, mstart);
662 VectorCopy(mstart, mend);
663 mstart[2] = QuantToModel(startingHeight) - (QUANT / 2); /* Set at the starting height, minus a little more to keep us off a potential surface. */
664 mend[2] = UNIT_HEIGHT * PATHFINDING_HEIGHT + QUANT; /* Set above the model. */
665
666 const trace_t tr = RT_COMPLETEBOXTRACE_PASSAGE(rtd->mapTiles, Line(mstart, mend), box, rtd->list);
667
668 /* OK, now we have the floor height value in tr.endpos[2].
669 * Divide by QUANT and round down. */
670 return ModelCeilingToQuant(tr.endpos[2] + DIST_EPSILON);
671}
672
673
683static int RT_FindOpeningFloor (const RoutingData* rtd, const vec3_t start, const vec3_t end, const int startingHeight, const int floorLimit)
684{
685 /* Look for additional space below init_bottom, down to lowest_bottom. */
686 int midfloor;
687
688 if (start[0] == end[0] || start[1] == end[1]) {
689 /* For orthogonal dirs, find the height at the midpoint. */
690 midfloor = RT_FindOpeningFloorFrac(rtd, start, end, 0.5, startingHeight);
691 } else {
692 /* If this is diagonal, trace the 1/3 and 2/3 points instead. */
693 /* 1/3 point */
694 midfloor = RT_FindOpeningFloorFrac(rtd, start, end, 0.33, startingHeight);
695
696 /* 2/3 point */
697 const int midfloor2 = RT_FindOpeningFloorFrac(rtd, start, end, 0.66, startingHeight);
698 midfloor = std::max(midfloor, midfloor2);
699 }
700
701 /* return the highest floor. */
702 return std::max(floorLimit, midfloor);
703}
704
705
715static int RT_FindOpeningCeiling (const RoutingData* rtd, const vec3_t start, const vec3_t end, const int startingHeight, const int ceilLimit)
716{
717 int midceil;
718
719 if (start[0] == end[0] || start[1] == end[1]) {
720 /* For orthogonal dirs, find the height at the midpoint. */
721 midceil = RT_FindOpeningCeilingFrac(rtd, start, end, 0.5, startingHeight);
722 } else {
723 /* If this is diagonal, trace the 1/3 and 2/3 points instead. */
724 /* 1/3 point */
725 midceil = RT_FindOpeningCeilingFrac(rtd, start, end, 0.33, startingHeight);
726
727 /* 2/3 point */
728 const int midceil2 = RT_FindOpeningCeilingFrac(rtd, start, end, 0.66, startingHeight);
729 midceil = std::min(midceil, midceil2);
730 }
731
732 /* return the lowest ceiling. */
733 return std::min(ceilLimit, midceil);
734}
735
736
737static int RT_CalcNewZ (const RoutingData* rtd, const int ax, const int ay, const int top, const int hi)
738{
739 int temp_z = floorf((hi - 1) / CELL_HEIGHT);
740 temp_z = std::min(temp_z, PATHFINDING_HEIGHT - 1);
741 int adj_lo = rtd->routing.getFloor(rtd->actorSize, ax, ay, temp_z) + temp_z * CELL_HEIGHT;
742 if (adj_lo > hi) {
743 temp_z--;
744 adj_lo = rtd->routing.getFloor(rtd->actorSize, ax, ay, temp_z) + temp_z * CELL_HEIGHT;
745 }
751 if (adj_lo >= 0 && top - adj_lo >= PATHFINDING_MIN_OPENING - PATHFINDING_MIN_STEPUP) {
752 return floorf(adj_lo / CELL_HEIGHT); /* Found floor in destination cell */
753 }
754
755 return RT_NO_OPENING; /* Skipping found floor in destination cell- not enough opening */
756}
757
758
774static int RT_TraceOpening (const RoutingData* rtd, const vec3_t start, const vec3_t end, const int ax, const int ay, const int bottom, const int top, int lo, int hi, int* foundLow, int* foundHigh)
775{
776 const trace_t tr = RT_ObstructedTrace(rtd, Line(start, end), hi, lo);
777 if (tr.fraction >= 1.0) {
778 lo = RT_FindOpeningFloor(rtd, start, end, lo, bottom);
779 hi = RT_FindOpeningCeiling(rtd, start, end, hi, top);
780 if (hi - lo >= PATHFINDING_MIN_OPENING) {
781 if (lo == -1) {
782 /* Bailing- no floor in destination cell. */
783 *foundLow = *foundHigh = 0;
784 return RT_NO_OPENING;
785 }
786 /* This opening works, use it! */
787 *foundLow = lo;
788 *foundHigh = hi;
789 /* Find the floor for the highest adjacent cell in this passage. */
790 const int tempZ = RT_CalcNewZ(rtd, ax, ay, top, hi);
791 if (tempZ != RT_NO_OPENING)
792 return tempZ;
793 }
794 }
795 *foundLow = *foundHigh = hi;
796 return RT_NO_OPENING;
797}
798
799
812static int RT_FindOpening (RoutingData* rtd, const place_t* from, const int ax, const int ay, const int bottom, const int top, int* foundLow, int* foundHigh)
813{
814 if (bottom == -1) {
815 /* Bailing- no floor in current cell. */
816 *foundLow = *foundHigh = 0;
817 return RT_NO_OPENING;
818 }
819
820 vec3_t start, end;
821 pos3_t pos;
822
823 /* Initialize the starting vector */
824 SizedPosToVec(from->cell, rtd->actorSize, start);
825
826 /* Initialize the ending vector */
827 VectorSet(pos, ax, ay, from->cell[2]);
828 SizedPosToVec(pos, rtd->actorSize, end);
829
830 /* Initialize the z component of both vectors */
831 start[2] = end[2] = 0;
832
833 /* ----- sky trace ------ */
834 /* shortcut: if both ceilings are the sky, we can check for walls
835 * AND determine the bottom of the passage in just one trace */
837 && from->cell[2] * CELL_HEIGHT + rtd->routing.getCeiling(rtd->actorSize, ax, ay, from->cell[2]) >= PATHFINDING_HEIGHT * CELL_HEIGHT) {
838 vec3_t sky, earth;
839 const AABB* box = (rtd->actorSize == ACTOR_SIZE_NORMAL ? &actor1x1Box : &actor2x2Box);
840 int tempBottom;
841
842 VectorInterpolation(start, end, 0.5, sky); /* center it halfway between the cells */
843 VectorCopy(sky, earth);
844 sky[2] = UNIT_HEIGHT * PATHFINDING_HEIGHT; /* Set to top of model. */
845 earth[2] = QuantToModel(bottom);
846
847 const trace_t tr = RT_COMPLETEBOXTRACE_PASSAGE(rtd->mapTiles, Line(sky, earth), box, rtd->list);
848 tempBottom = ModelFloorToQuant(tr.endpos[2]);
849 if (tempBottom <= bottom + PATHFINDING_MIN_STEPUP) {
850 const int hi = bottom + PATHFINDING_MIN_OPENING;
851 /* Found opening with sky trace. */
852 *foundLow = tempBottom;
853 *foundHigh = CELL_HEIGHT * PATHFINDING_HEIGHT;
854 return RT_CalcNewZ(rtd, ax, ay, top, hi);
855 }
856 }
857 /* Warning: never try to make this an 'else if', or 'arched entry' situations will fail !! */
858
859 /* ----- guaranteed opening trace ------ */
860 /* Now calculate the "guaranteed" opening, if any. If the opening from
861 * the floor to the ceiling is not too tall, there must be a section that
862 * will always be vacant if there is a usable passage of any size and at
863 * any height. */
864 int tempZ;
865 if (top - bottom < PATHFINDING_MIN_OPENING * 2) {
866 const int lo = top - PATHFINDING_MIN_OPENING;
867 const int hi = bottom + PATHFINDING_MIN_OPENING;
868
869 tempZ = RT_TraceOpening(rtd, start, end, ax, ay, bottom, top, lo, hi, foundLow, foundHigh);
870 } else {
871 /* ----- brute force trace ------ */
872 /* There is no "guaranteed" opening, brute force search. */
873 int lo = bottom;
874 tempZ = 0;
875 while (lo <= top - PATHFINDING_MIN_OPENING) {
876 /* Check for a 1 QUANT opening. */
877 tempZ = RT_TraceOpening(rtd, start, end, ax, ay, bottom, top, lo, lo + 1, foundLow, foundHigh);
878 if (tempZ != RT_NO_OPENING)
879 break;
880 /* Credit to Duke: We skip the minimum opening, as if there is a
881 * viable opening, even one slice above, that opening would be open. */
883 }
884 }
885 return tempZ;
886}
887
888
900static int RT_MicroTrace (RoutingData* rtd, const place_t* from, const int ax, const int ay, const int az, const int stairwaySituation, opening_t* opening)
901{
902 /* OK, now we have a viable shot across. Run microstep tests now. */
903 /* Now calculate the stepup at the floor using microsteps. */
904 const int top = opening->base + opening->size;
905 signed char bases[UNIT_SIZE / PATHFINDING_MICROSTEP_SIZE + 1];
906 /* Shortcut the value of UNIT_SIZE / PATHFINDING_MICROSTEP_SIZE. */
907 const int steps = UNIT_SIZE / PATHFINDING_MICROSTEP_SIZE;
908 vec3_t start, end;
909 pos3_t pos;
910
911 /* First prepare the two known end values. */
912 bases[0] = from->floor;
913 const int floorVal = rtd->routing.getFloor(rtd->actorSize, ax, ay, az);
914 bases[steps] = std::max(0, floorVal) + az * CELL_HEIGHT;
915
916 /* Initialize the starting vector */
917 SizedPosToVec(from->cell, rtd->actorSize, start);
918
919 /* Initialize the ending vector */
920 VectorSet(pos, ax, ay, az);
921 SizedPosToVec(pos, rtd->actorSize, end);
922
923 /* Now prep the z values for start and end. */
924 start[2] = QuantToModel(opening->base) + 1;
925 end[2] = -QUANT;
926
927 /* Memorize the start and end x,y points */
928 const float sx = start[0];
929 const float sy = start[1];
930 const float ex = end[0];
931 const float ey = end[1];
932
933 int newBottom = std::max(bases[0], bases[steps]);
934 /* Now calculate the rest of the microheights. */
935 for (int i = 1; i < steps; i++) {
936 start[0] = end[0] = sx + (ex - sx) * (i / (float)steps);
937 start[1] = end[1] = sy + (ey - sy) * (i / (float)steps);
938
939 /* perform the trace, then return true if the trace was obstructed. */
940 const trace_t tr = RT_COMPLETEBOXTRACE_PASSAGE(rtd->mapTiles, Line(start, end), &footBox, rtd->list);
941 if (tr.fraction >= 1.0f) {
942 bases[i] = -1;
943 } else {
944 bases[i] = ModelFloorToQuant(tr.endpos[2] - DIST_EPSILON);
945 /* Walking through glass fix:
946 * It is possible to have an obstruction that can be skirted around diagonally
947 * because the microtraces are so tiny. But, we have a full size trace in opening->base
948 * that apporoximates where legroom ends. If the found floor of the middle microtrace is
949 * too low, then set it to the worst case scenario floor based on base->opening.
950 */
951 if (i == floor(steps / 2.0) && bases[i] < opening->base - PATHFINDING_MIN_STEPUP) {
952 if (debugTrace)
953 Com_Printf("Adjusting middle trace- the known base is too high. \n");
954 bases[i] = opening->base - PATHFINDING_MIN_STEPUP;
955 }
956 }
957
958 if (debugTrace)
959 Com_Printf("Microstep %i from (%f, %f, %f) to (%f, %f, %f) = %i [%f]\n",
960 i, start[0], start[1], start[2], end[0], end[1], end[2], bases[i], tr.endpos[2]);\
961
962 newBottom = std::max(newBottom, (int)bases[i]);
963 }
964
965 if (debugTrace)
966 Com_Printf("z:%i az:%i bottom:%i new_bottom:%i top:%i bases[0]:%i bases[%i]:%i\n", from->cell[2], az, opening->base, newBottom, top, bases[0], steps, bases[steps]);
967
968
970 /* Now find the maximum stepup moving from (x, y) to (ax, ay). */
971 /* Initialize stepup. */
972 int current_h = bases[0];
973 int highest_h = -1;
974 int highest_i = 1;
975 opening->stepup = 0;
976 int skipped = 0;
977 for (int i = 1; i <= steps; i++) {
978 if (debugTrace)
979 Com_Printf("Tracing forward i:%i h:%i\n", i, current_h);
980 /* If there is a rise, use it. */
981 if (bases[i] >= current_h || ++skipped > PATHFINDING_MICROSTEP_SKIP) {
982 if (skipped == PATHFINDING_MICROSTEP_SKIP) {
983 i = highest_i;
984 if (debugTrace)
985 Com_Printf(" Skipped too many steps, reverting to i:%i\n", i);
986 }
987 opening->stepup = std::max(opening->stepup, bases[i] - current_h);
988 current_h = bases[i];
989 highest_h = -2;
990 highest_i = i + 1;
991 skipped = 0;
992 if (debugTrace)
993 Com_Printf(" Advancing b:%i stepup:%i\n", bases[i], opening->stepup);
994 } else {
995 /* We are skipping this step in case the actor can step over this lower step. */
996 /* Record the step in case it is the highest of the low steps. */
997 if (bases[i] > highest_h) {
998 highest_h = bases[i];
999 highest_i = i;
1000 }
1001 if (debugTrace)
1002 Com_Printf(" Skipped because we are falling, skip:%i.\n", skipped);
1003 /* If this is the last iteration, make sure we go back and get our last stepup tests. */
1004 if (i == steps) {
1006 i = highest_i - 1;
1007 if (debugTrace)
1008 Com_Printf(" Tripping skip counter to perform last tests.\n");
1009 }
1010 }
1011 }
1012
1014 /* Now find the maximum stepup moving from (x, y) to (ax, ay). */
1015 /* Initialize stepup. */
1016 current_h = bases[steps];
1017 highest_h = -1;
1018 highest_i = steps - 1;
1019 opening->invstepup = 0;
1020 skipped = 0;
1021 for (int i = steps - 1; i >= 0; i--) {
1022 if (debugTrace)
1023 Com_Printf("Tracing backward i:%i h:%i\n", i, current_h);
1024 /* If there is a rise, use it. */
1025 if (bases[i] >= current_h || ++skipped > PATHFINDING_MICROSTEP_SKIP) {
1026 if (skipped == PATHFINDING_MICROSTEP_SKIP) {
1027 i = highest_i;
1028 if (debugTrace)
1029 Com_Printf(" Skipped too many steps, reverting to i:%i\n", i);
1030 }
1031 opening->invstepup = std::max(opening->invstepup, bases[i] - current_h);
1032 current_h = bases[i];
1033 highest_h = -2;
1034 highest_i = i - 1;
1035 skipped = 0;
1036 if (debugTrace)
1037 Com_Printf(" Advancing b:%i stepup:%i\n", bases[i], opening->invstepup);
1038 } else {
1039 /* We are skipping this step in case the actor can step over this lower step. */
1040 /* Record the step in case it is the highest of the low steps. */
1041 if (bases[i] > highest_h) {
1042 highest_h = bases[i];
1043 highest_i = i;
1044 }
1045 if (debugTrace)
1046 Com_Printf(" Skipped because we are falling, skip:%i.\n", skipped);
1047 /* If this is the last iteration, make sure we go back and get our last stepup tests. */
1048 if (i == 0) {
1050 i = highest_i + 1;
1051 if (debugTrace)
1052 Com_Printf(" Tripping skip counter to perform last tests.\n");
1053 }
1054 }
1055 }
1056
1057 if (stairwaySituation) {
1058 const int middle = bases[4]; /* terrible hack by Duke. This relies on PATHFINDING_MICROSTEP_SIZE being set to 4 !! */
1059
1060 if (stairwaySituation == 1) { /* stepping up */
1061 if (bases[1] <= middle && /* if nothing in the 1st part of the passage is higher than what's at the border */
1062 bases[2] <= middle &&
1063 bases[3] <= middle ) {
1064 if (debugTrace)
1065 Com_Printf("Addition granted by ugly stair hack-stepping up.\n");
1066 return opening->base - middle;
1067 }
1068 } else if (stairwaySituation == 2) {/* stepping down */
1069 if (bases[5] <= middle && /* same for the 2nd part of the passage */
1070 bases[6] <= middle &&
1071 bases[7] <= middle ) {
1072 if (debugTrace)
1073 Com_Printf("Addition granted by ugly stair hack-stepping down.\n");
1074 return opening->base - middle;
1075 }
1076 }
1077 }
1078
1079 /* Return the confirmed passage opening. */
1080 return opening->base - newBottom;
1081}
1082
1083
1092static int RT_TraceOnePassage (RoutingData* rtd, const place_t* from, const place_t* to, opening_t* opening)
1093{
1094 int hi;
1095 const int z = from->cell[2];
1096 const int lower = std::max(from->floor, to->floor);
1097 const int upper = std::min(from->ceiling, to->ceiling);
1098 const int ax = to->cell[0];
1099 const int ay = to->cell[1];
1100
1101 RT_FindOpening(rtd, from, ax, ay, lower, upper, &opening->base, &hi);
1102 /* calc opening found so far and set stepup */
1103 opening->size = hi - opening->base;
1104 const int az = to->floorZ;
1105
1106 /* We subtract MIN_STEPUP because that is foot space-
1107 * the opening there only needs to be the microtrace
1108 * wide and not the usual dimensions.
1109 */
1111 const int srcFloor = from->floor;
1112 const int dstFloor = rtd->routing.getFloor(rtd->actorSize, ax, ay, az) + az * CELL_HEIGHT;
1113 /* if we already have enough headroom, try to skip microtracing */
1114 if (opening->size < ACTOR_MAX_HEIGHT
1115 || abs(srcFloor - opening->base) > PATHFINDING_MIN_STEPUP
1116 || abs(dstFloor - opening->base) > PATHFINDING_MIN_STEPUP) {
1117 const int stairway = RT_PlaceIsShifted(from, to);
1118 /* This returns the total opening height, as the
1119 * microtrace may reveal more passage height from the foot space. */
1120 const int bonusSize = RT_MicroTrace(rtd, from, ax, ay, az, stairway, opening);
1121 opening->base -= bonusSize;
1122 opening->size = hi - opening->base; /* re-calculate */
1123 } else {
1124 /* Skipping microtracing, just set the stepup values. */
1125 opening->stepup = std::max(0, opening->base - srcFloor);
1126 opening->invstepup = std::max(0, opening->base - dstFloor);
1127 }
1128
1129 /* Now place an upper bound on stepup */
1130 if (opening->stepup > PATHFINDING_MAX_STEPUP) {
1131 opening->stepup = PATHFINDING_NO_STEPUP;
1132 } else {
1133 /* Add rise/fall bit as needed. */
1134 if (az < z && opening->invstepup <= PATHFINDING_MAX_STEPUP)
1135 /* BIG_STEPDOWN indicates 'walking down', don't set it if we're 'falling' */
1136 opening->stepup |= PATHFINDING_BIG_STEPDOWN;
1137 else if (az > z)
1138 opening->stepup |= PATHFINDING_BIG_STEPUP;
1139 }
1140
1141 /* Now place an upper bound on stepup */
1142 if (opening->invstepup > PATHFINDING_MAX_STEPUP) {
1144 } else {
1145 /* Add rise/fall bit as needed. */
1146 if (az > z)
1148 else if (az < z)
1150 }
1151
1152 if (opening->size >= PATHFINDING_MIN_OPENING) {
1153 return opening->size;
1154 }
1155 }
1156
1157 opening->stepup = PATHFINDING_NO_STEPUP;
1159 return 0;
1160}
1161
1169static void RT_TracePassage (RoutingData* rtd, const int x, const int y, const int z, const int ax, const int ay, opening_t* opening)
1170{
1172 place_t from, to, above;
1173 const place_t* placeToCheck = nullptr;
1174
1175 RT_PlaceInit(rtd->routing, rtd->actorSize, &from, x, y, z);
1176 RT_PlaceInit(rtd->routing, rtd->actorSize, &to, ax, ay, z);
1177
1178 const int aboveCeil = (z < PATHFINDING_HEIGHT - 1) ? rtd->routing.getCeiling(rtd->actorSize, ax, ay, z + 1) + (z + 1) * CELL_HEIGHT : to.ceiling;
1179 const int lowCeil = std::min(from.ceiling, (rtd->routing.getCeiling(rtd->actorSize, ax, ay, z) == 0 || to.ceiling - from.floor < PATHFINDING_MIN_OPENING) ? aboveCeil : to.ceiling);
1180
1181 /*
1182 * First check the ceiling for the cell beneath the adjacent floor to see
1183 * if there is a potential opening. The difference between the
1184 * ceiling and the floor is at least PATHFINDING_MIN_OPENING tall, then
1185 * scan it to see if we can use it. If we can, then one of two things
1186 * will happen:
1187 * - The actual adjacent cell has no floor of its own, and we will walk
1188 * or fall into the cell below the adjacent cell anyway.
1189 * - There is a floor in the adjacent cell, but we will not be able to
1190 * walk into it anyway because there cannot be any steps if there is
1191 * a passage. An actor can walk down into the cell ONLY IF it's
1192 * negative stepup meets or exceeds the change in floor height.
1193 * No actors will be allowed to fall because they cannot temporarily
1194 * occupy the space beneath the floor in the adjacent cell to fall
1195 * (all actors in the cell must be ON TOP of the floor in the cell).
1196 * If there is no passage, then the obstruction may be used as steps to
1197 * climb up to the adjacent floor.
1198 */
1199 if (to.isUsable() && RT_PlaceDoesIntersectEnough(&from, &to)) {
1200 placeToCheck = &to;
1201 } else if (z < PATHFINDING_HEIGHT - 1) {
1202 RT_PlaceInit(rtd->routing, rtd->actorSize, &above, ax, ay, z + 1);
1203 if (above.isUsable() && RT_PlaceDoesIntersectEnough(&from, &above)) {
1204 placeToCheck = &above;
1205 }
1206 }
1207 if (!placeToCheck) {
1208 /* If we got here, then there is no opening from floor to ceiling. */
1209 opening->stepup = PATHFINDING_NO_STEPUP;
1211 opening->base = lowCeil;
1212 opening->size = 0;
1213 return;
1214 }
1215
1216 /*
1217 * Now that we got here, we know that either the opening between the
1218 * ceiling below the adjacent cell and the current floor is too small or
1219 * obstructed. Try to move onto the adjacent floor.
1220 */
1221 RT_TraceOnePassage(rtd, &from, placeToCheck, opening);
1222 if (opening->size < PATHFINDING_MIN_OPENING) {
1223 /* If we got here, then there is no useable opening from floor to ceiling. */
1224 opening->stepup = PATHFINDING_NO_STEPUP;
1226 opening->base = lowCeil;
1227 opening->size = 0;
1228 }
1229}
1230
1231
1240static int RT_UpdateConnection (RoutingData* rtd, const int x, const int y, const int ax, const int ay, const int z, const int dir)
1241{
1243 const int ceiling = rtd->routing.getCeiling(rtd->actorSize, x, y, z);
1244 const int adjCeiling = rtd->routing.getCeiling(rtd->actorSize, ax, ay, z);
1245 const int upperAdjCeiling = (z < PATHFINDING_HEIGHT - 1) ? rtd->routing.getCeiling(rtd->actorSize, ax, ay, z + 1) : adjCeiling;
1246
1247 if ((adjCeiling == 0 && upperAdjCeiling == 0) || ceiling == 0) {
1248 /* We can't go this way. */
1249 RT_ConnSetNoGo(rtd, x, y, z, dir);
1250 return z;
1251 }
1252
1257 const int absCeiling = ceiling + z * CELL_HEIGHT;
1258// const int absAdjCeiling = adjCeiling + z * CELL_HEIGHT; /* temporarily unused */
1259 const int absExtAdjCeiling = (z < PATHFINDING_HEIGHT - 1) ? adjCeiling + (z + 1) * CELL_HEIGHT : absCeiling;
1260 const int absFloor = rtd->routing.getFloor(rtd->actorSize, x, y, z) + z * CELL_HEIGHT;
1261 const int absAdjFloor = rtd->routing.getFloor(rtd->actorSize, ax, ay, z) + z * CELL_HEIGHT;
1262
1263 if (absCeiling < absAdjFloor || absExtAdjCeiling < absFloor) {
1264 /* We can't go this way. */
1265 RT_ConnSetNoGo(rtd, x, y, z, dir);
1266 return z;
1267 }
1268
1270 opening_t opening;
1271 RT_TracePassage(rtd, x, y, z, ax, ay, &opening);
1272
1276 const int newZ = RT_FillPassageData(rtd, dir, x, y, z, opening.size, opening.base, opening.stepup);
1277
1278 return newZ;
1279}
1280
1281
1292void RT_UpdateConnectionColumn (mapTiles_t* mapTiles, Routing& routing, const int actorSize, const int x, const int y, const int dir, const char** list, const int minZ, const int maxZ)
1293{
1294 int z = minZ;
1295 /* the essential data passed down the calltree */
1296 RoutingData rtd(mapTiles, routing, actorSize, list);
1297
1298 /* get the neighbor cell's coordinates */
1299 const int ax = x + dvecs[dir][0];
1300 const int ay = y + dvecs[dir][1];
1301
1302 assert(actorSize > ACTOR_SIZE_INVALID && actorSize <= ACTOR_MAX_SIZE);
1303 assert((x >= 0) && (x <= PATHFINDING_WIDTH - actorSize));
1304 assert((y >= 0) && (y <= PATHFINDING_WIDTH - actorSize));
1305
1306#ifdef DEBUG
1308 /* just a place to place a breakpoint */
1309 if (x == 126 && y == 129 && dir == 2) {
1310 z = 7;
1311 }
1312#endif
1313
1314 /* if our destination cell is out of bounds, bail. */
1315 if (ax < 0 || ax > PATHFINDING_WIDTH - actorSize || ay < 0 || ay > PATHFINDING_WIDTH - actorSize) {
1316 /* We can't go this way. */
1317 RT_ConnSetNoGo(&rtd, x, y, z, dir);
1318 return;
1319 }
1320
1321 /* Main loop */
1322 for (z = minZ; z < maxZ; z++) {
1323 /* The last z value processed by the tracing function. */
1324 const int new_z = RT_UpdateConnection(&rtd, x, y, ax, ay, z, dir);
1325 assert(new_z >= z);
1326 z = new_z;
1327 }
1328}
1329
1330void RT_WriteCSVFiles (const Routing& routing, const char* baseFilename, const GridBox& box)
1331{
1332 char filename[MAX_OSPATH], ext[MAX_OSPATH];
1333
1334 /* An elevation files- dumps the floor and ceiling levels relative to each cell. */
1335 for (int i = 1; i <= ACTOR_MAX_SIZE; i++) {
1336 strncpy(filename, baseFilename, sizeof(filename) - 1);
1337 sprintf(ext, ".%i.elevation.csv", i);
1338 Com_DefaultExtension(filename, sizeof(filename), ext);
1339 ScopedFile f;
1341 if (!f)
1342 Sys_Error("Could not open file %s.", filename);
1343 FS_Printf(&f, ",");
1344 for (int x = box.getMinX(); x <= box.getMaxX() - i + 1; x++)
1345 FS_Printf(&f, "x:%i,", x);
1346 FS_Printf(&f, "\n");
1347 for (int z = box.getMaxZ(); z >= box.getMinZ(); z--) {
1348 for (int y = box.getMaxY(); y >= box.getMinY() - i + 1; y--) {
1349 FS_Printf(&f, "z:%i y:%i,", z ,y);
1350 for (int x = box.getMinX(); x <= box.getMaxX() - i + 1; x++) {
1351 /* compare results */
1352 FS_Printf(&f, "h:%i c:%i,", routing.getFloor(i, x, y, z), routing.getCeiling(i, x, y, z));
1353 }
1354 FS_Printf(&f, "\n");
1355 }
1356 FS_Printf(&f, "\n");
1357 }
1358 }
1359
1360 /* Output the walls/passage files. */
1361 for (int i = 1; i <= ACTOR_MAX_SIZE; i++) {
1362 strncpy(filename, baseFilename, sizeof(filename) - 1);
1363 sprintf(ext, ".%i.walls.csv", i);
1364 Com_DefaultExtension(filename, sizeof(filename), ext);
1365 ScopedFile f;
1367 if (!f)
1368 Sys_Error("Could not open file %s.", filename);
1369 FS_Printf(&f, ",");
1370 for (int x = box.getMinX(); x <= box.getMaxX() - i + 1; x++)
1371 FS_Printf(&f, "x:%i,", x);
1372 FS_Printf(&f, "\n");
1373 for (int z = box.getMaxZ(); z >= box.getMinZ(); z--) {
1374 for (int y = box.getMaxY(); y >= box.getMinY() - i + 1; y--) {
1375 FS_Printf(&f, "z:%i y:%i,", z, y);
1376 for (int x = box.getMinX(); x <= box.getMaxX() - i + 1; x++) {
1377 /* compare results */
1378 FS_Printf(&f, "\"");
1379
1380 /* NW corner */
1381 FS_Printf(&f, "%3i-%3i ", RT_CONN_NX_PY(routing, i, x, y, z), RT_STEPUP_NX_PY(routing, i, x, y, z));
1382
1383 /* N side */
1384 FS_Printf(&f, "%3i-%3i ", RT_CONN_PY(routing, i, x, y, z), RT_STEPUP_PY(routing, i, x, y, z));
1385
1386 /* NE corner */
1387 FS_Printf(&f, "%3i-%3i ", RT_CONN_PX_PY(routing, i, x, y, z), RT_STEPUP_PX_PY(routing, i, x, y, z));
1388
1389 FS_Printf(&f, "\n");
1390
1391 /* W side */
1392 FS_Printf(&f, "%3i-%3i ", RT_CONN_NX(routing, i, x, y, z), RT_STEPUP_NX(routing, i, x, y, z));
1393
1394 /* Center - display floor height */
1395 FS_Printf(&f, "_%+2i_ ", routing.getFloor(i, x, y, z));
1396
1397 /* E side */
1398 FS_Printf(&f, "%3i-%3i ", RT_CONN_PX(routing, i, x, y, z), RT_STEPUP_PX(routing, i, x, y, z));
1399
1400 FS_Printf(&f, "\n");
1401
1402 /* SW corner */
1403 FS_Printf(&f, "%3i-%3i ", RT_CONN_NX_NY(routing, i, x, y, z), RT_STEPUP_NX_NY(routing, i, x, y, z));
1404
1405 /* S side */
1406 FS_Printf(&f, "%3i-%3i ", RT_CONN_NY(routing, i, x, y, z), RT_STEPUP_NY(routing, i, x, y, z));
1407
1408 /* SE corner */
1409 FS_Printf(&f, "%3i-%3i ", RT_CONN_PX_NY(routing, i, x, y, z), RT_STEPUP_PX_NY(routing, i, x, y, z));
1410
1411 FS_Printf(&f, "\",");
1412 }
1413 FS_Printf(&f, "\n");
1414 }
1415 FS_Printf(&f, "\n");
1416 }
1417 }
1418}
1419
1420#ifdef DEBUG
1430int RT_DebugSpecial (mapTiles_t* mapTiles, Routing& routing, const int actorSize, const int x, const int y, const int dir, const char** list)
1431{
1432 RoutingData rtd(mapTiles, routing, actorSize, list); /* the essential data passed down the calltree */
1433
1434 /* get the neighbor cell's coordinates */
1435 const int ax = x + dvecs[dir][0];
1436 const int ay = y + dvecs[dir][1];
1437
1439 const int new_z = RT_UpdateConnection(&rtd, x, y, ax, ay, 0, dir);
1440 return new_z;
1441}
1442
1448void RT_DebugPathDisplay (Routing& routing, actorSizeEnum_t actorSize, int x, int y, int z)
1449{
1450 Com_Printf("data at cursor XYZ(%i, %i, %i) Floor(%i) Ceiling(%i)\n", x, y, z,
1451 routing.getFloor(actorSize, x, y, z),
1452 routing.getCeiling(actorSize, x, y, z) );
1453 Com_Printf("connections ortho: (PX=%i, NX=%i, PY=%i, NY=%i))\n",
1454 RT_CONN_PX(routing, actorSize, x, y, z), /* dir = 0 */
1455 RT_CONN_NX(routing, actorSize, x, y, z), /* 1 */
1456 RT_CONN_PY(routing, actorSize, x, y, z), /* 2 */
1457 RT_CONN_NY(routing, actorSize, x, y, z) ); /* 3 */
1458 Com_Printf("connections diago: (PX_PY=%i, NX_NY=%i, NX_PY=%i, PX_NY=%i))\n",
1459 RT_CONN_PX_PY(routing, actorSize, x, y, z), /* dir = 4 */
1460 RT_CONN_NX_NY(routing, actorSize, x, y, z), /* 5 */
1461 RT_CONN_NX_PY(routing, actorSize, x, y, z), /* 6 */
1462 RT_CONN_PX_NY(routing, actorSize, x, y, z) ); /* 7 */
1463 Com_Printf("stepup ortho: (PX=%i, NX=%i, PY=%i, NY=%i))\n",
1464 RT_STEPUP_PX(routing, actorSize, x, y, z), /* dir = 0 */
1465 RT_STEPUP_NX(routing, actorSize, x, y, z), /* 1 */
1466 RT_STEPUP_PY(routing, actorSize, x, y, z), /* 2 */
1467 RT_STEPUP_NY(routing, actorSize, x, y, z) ); /* 3 */
1468}
1469
1470#endif
Definition aabb.h:42
vec3_t maxs
Definition aabb.h:258
vec3_t mins
Definition aabb.h:257
void set(const AABB &other)
Copies the values from the given aabb.
Definition aabb.h:60
void shift(const vec3_t shiftVec)
shove the whole box by the given vector
Definition aabb.h:246
pos_t getMinY() const
Definition mathlib.h:177
pos_t getMinZ() const
Definition mathlib.h:180
pos_t getMaxY() const
Definition mathlib.h:186
pos_t getMinX() const
Definition mathlib.h:174
pos_t getMaxX() const
Definition mathlib.h:183
pos_t getMaxZ() const
Definition mathlib.h:189
Definition line.h:31
RT_data_s contains the essential data that is passed to most of the RT_* functions.
Definition routing.cpp:81
mapTiles_t * mapTiles
Definition routing.cpp:83
actorSizeEnum_t actorSize
Definition routing.cpp:85
RoutingData(mapTiles_t *mapTiles, Routing &r, actorSizeEnum_t actorSize, const char **list)
Definition routing.cpp:91
const char ** list
Definition routing.cpp:86
Routing & routing
Definition routing.cpp:84
void setStepup(const int actorSize, const int x, const int y, const int z, const int dir, const int val)
Definition typedefs.h:298
void setFilled(const actorSizeEnum_t actorSize, const int x, const int y, const int lowZ, const int highZ)
Definition typedefs.h:279
signed char getFloor(const actorSizeEnum_t actorSize, const pos3_t pos) const
Definition typedefs.h:262
void setCeiling(const actorSizeEnum_t actorSize, const int x, const int y, const int z, const int val)
Definition typedefs.h:269
byte getCeiling(const int actorSize, const pos3_t pos) const
Definition typedefs.h:272
void setConn(const int actorSize, const int x, const int y, const int z, const int dir, const int val)
Definition typedefs.h:288
void setFloor(const int actorSize, const int x, const int y, const int z, const int val)
Definition typedefs.h:259
static const AABB actor1x1Box(-half1x1Width, -half1x1Width, 0, half1x1Width, half1x1Width, 0)
#define half1x1Width
Definition routing.cpp:54
void RT_GetMapSize(mapTiles_t *mapTiles, AABB &mapBox)
Calculate the map size via model data and store grid size in map_min and map_max. This is done with e...
Definition routing.cpp:253
static int RT_TraceOpening(const RoutingData *rtd, const vec3_t start, const vec3_t end, const int ax, const int ay, const int bottom, const int top, int lo, int hi, int *foundLow, int *foundHigh)
Performs actual trace to find a passage between two points given an upper and lower bound.
Definition routing.cpp:774
static int RT_PlaceIsShifted(const place_t *p, const place_t *other)
This function detects a special stairway situation, where one place is right in front of a stairway a...
Definition routing.cpp:149
bool RT_CanActorStandHere(const Routing &routing, const int actorSize, const pos3_t pos)
Check if an actor can stand(up) in the cell given by pos.
Definition routing.cpp:237
static void RT_PlaceInit(const Routing &routing, const actorSizeEnum_t actorSize, place_t *p, const int x, const int y, const int z)
Definition routing.cpp:126
static int RT_FillPassageData(RoutingData *rtd, const int dir, const int x, const int y, const int z, const int openingSize, const int openingBase, const int stepup)
Performs traces to find a passage between two points given an upper and lower bound.
Definition routing.cpp:541
int RT_CheckCell(mapTiles_t *mapTiles, Routing &routing, const int actorSize, const int x, const int y, const int z, const char **list)
This function looks to see if an actor of a given size can occupy a cell(s) and if so identifies the ...
Definition routing.cpp:360
static int RT_UpdateConnection(RoutingData *rtd, const int x, const int y, const int ax, const int ay, const int z, const int dir)
Routing Function to update the connection between two fields.
Definition routing.cpp:1240
static int RT_CalcNewZ(const RoutingData *rtd, const int ax, const int ay, const int top, const int hi)
Definition routing.cpp:737
bool debugTrace
Definition routing.cpp:38
static void RT_ConnSetNoGo(RoutingData *rtd, const int x, const int y, const int z, const int dir)
Definition routing.cpp:103
static int RT_FindOpeningCeilingFrac(const RoutingData *rtd, const vec3_t start, const vec3_t end, const float frac, const int startingHeight)
Performs a trace to find the ceiling of a passage a fraction of the way from start to end.
Definition routing.cpp:655
static int RT_MicroTrace(RoutingData *rtd, const place_t *from, const int ax, const int ay, const int az, const int stairwaySituation, opening_t *opening)
Performs small traces to find places when an actor can step up.
Definition routing.cpp:900
static int RT_FindOpeningFloorFrac(const RoutingData *rtd, const vec3_t start, const vec3_t end, const float frac, const int startingHeight)
Performs a trace to find the floor of a passage a fraction of the way from start to end.
Definition routing.cpp:626
#define halfMicrostepSize
Definition routing.cpp:49
static void RT_TracePassage(RoutingData *rtd, const int x, const int y, const int z, const int ax, const int ay, opening_t *opening)
Performs traces to find a passage between two points.
Definition routing.cpp:1169
void RT_WriteCSVFiles(const Routing &routing, const char *baseFilename, const GridBox &box)
Definition routing.cpp:1330
static trace_t RT_ObstructedTrace(const RoutingData *rtd, const Line &traceLine, int hi, int lo)
Helper function to trace for walls.
Definition routing.cpp:603
static int RT_FindOpeningCeiling(const RoutingData *rtd, const vec3_t start, const vec3_t end, const int startingHeight, const int ceilLimit)
Performs traces to find the approximate ceiling of a passage.
Definition routing.cpp:715
#define half2x2Width
Definition routing.cpp:55
static const AABB actor2x2Box(-half2x2Width, -half2x2Width, 0, half2x2Width, half2x2Width, 0)
static int RT_TraceOnePassage(RoutingData *rtd, const place_t *from, const place_t *to, opening_t *opening)
Performs traces to find a passage between two points given an upper and lower bound.
Definition routing.cpp:1092
static void RT_StepupSet(RoutingData *rtd, const int x, const int y, const int z, const int dir, const int val)
Definition routing.cpp:98
bool RT_AllCellsBelowAreFilled(const Routing &routing, const int actorSize, const pos3_t pos)
Check if pos is on solid ground.
Definition routing.cpp:331
static const AABB footBox(-halfMicrostepSize, -halfMicrostepSize, 0, halfMicrostepSize, halfMicrostepSize, 0)
static int RT_FindOpeningFloor(const RoutingData *rtd, const vec3_t start, const vec3_t end, const int startingHeight, const int floorLimit)
Performs traces to find the approximate floor of a passage.
Definition routing.cpp:683
static bool RT_PlaceDoesIntersectEnough(const place_t *p, const place_t *other)
Definition routing.cpp:138
void RT_UpdateConnectionColumn(mapTiles_t *mapTiles, Routing &routing, const int actorSize, const int x, const int y, const int dir, const char **list, const int minZ, const int maxZ)
Routing Function to update the connection between two fields.
Definition routing.cpp:1292
#define RT_NO_OPENING
Definition routing.cpp:46
static int RT_FindOpening(RoutingData *rtd, const place_t *from, const int ax, const int ay, const int bottom, const int top, int *foundLow, int *foundHigh)
Performs traces to find a passage between two points given an upper and lower bound.
Definition routing.cpp:812
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
definitions common between client and server, but not game lib
static transfer_t tr
#define QUANT
Definition defines.h:126
#define PATHFINDING_MIN_OPENING
Definition defines.h:323
#define PATHFINDING_MICROSTEP_SKIP
The number of microsteps that can be stepped over by an actor. Used to allow an actor to stepup when ...
Definition defines.h:328
#define WALL_SIZE
Definition defines.h:128
#define PATHFINDING_LEGROOMHEIGHT
Definition defines.h:311
#define ACTOR_MAX_SIZE
Definition defines.h:305
#define PATHFINDING_WIDTH
absolute max
Definition defines.h:292
#define DIST_EPSILON
Definition defines.h:377
#define UNIT_HEIGHT
Definition defines.h:122
#define CELL_HEIGHT
A cell's height in QUANT sized units.
Definition defines.h:296
#define UNIT_SIZE
Definition defines.h:121
#define ACTOR_MAX_HEIGHT
The tallest actor's height in QUANT sized units.
Definition defines.h:298
#define PATHFINDING_MIN_STEPUP
Definition defines.h:314
#define PATHFINDING_MAX_STEPUP
Definition defines.h:317
#define ACTOR_SIZE_NORMAL
Definition defines.h:302
#define PATHFINDING_HEIGHT
15 max, adjusting above 8 will require a rewrite to the DV code
Definition defines.h:294
#define PATHFINDING_MICROSTEP_SIZE
The size (in model units) of a microstep. Must be a power of 2 and less than UNIT_SIZE.
Definition defines.h:325
#define ACTOR_SIZE_INVALID
Definition defines.h:301
#define PATHFINDING_NO_STEPUP
Definition defines.h:319
int FS_Printf(qFILE *f, const char *msg,...)
Can print chunks for 1024 chars into a file.
Definition files.cpp:1497
int FS_OpenFile(const char *filename, qFILE *file, filemode_t mode)
Finds and opens the file in the search path.
Definition files.cpp:162
#define MAX_OSPATH
Definition filesys.h:44
@ FILE_WRITE
Definition filesys.h:111
void Sys_Error(const char *error,...)
Definition g_main.cpp:421
const char * filename
Definition ioapi.h:41
const vec4_t dvecs[PATHFINDING_DIRECTIONS]
Definition mathlib.cpp:58
#define PosToVec(p, v)
Pos boundary size is +/- 128 - to get into the positive area we add the possible max negative value a...
Definition mathlib.h:110
#define VecToPos(v, p)
Map boundary is +/- MAX_WORLD_WIDTH - to get into the positive area we add the possible max negative ...
Definition mathlib.h:100
#define PLAYER_STANDING_HEIGHT
Definition q_sizes.h:12
QGL_EXTERN GLfloat f
Definition r_gl.h:114
QGL_EXTERN GLint i
Definition r_gl.h:113
grid pathfinding and routing
#define RT_STEPUP_NX(r, actorSize, x, y, z)
Definition routing.h:63
#define ModelFloorToQuant(x)
These macros are meant to correctly convert from model units to QUANT units and back.
Definition routing.h:75
#define RT_CONN_NX_NY(r, actorSize, x, y, z)
Definition routing.h:60
#define RT_STEPUP_NX_NY(r, actorSize, x, y, z)
Definition routing.h:70
#define QuantToModel(x)
Definition routing.h:79
#define RT_CONN_PY(r, actorSize, x, y, z)
Definition routing.h:54
#define RT_STEPUP_PX(r, actorSize, x, y, z)
Definition routing.h:62
#define RT_STEPUP_PX_PY(r, actorSize, x, y, z)
Definition routing.h:67
#define ModelCeilingToQuant(x)
Definition routing.h:77
#define RT_CONN_NX_PY(r, actorSize, x, y, z)
Definition routing.h:59
#define RT_STEPUP_PX_NY(r, actorSize, x, y, z)
Definition routing.h:68
#define RT_CONN_NY(r, actorSize, x, y, z)
Definition routing.h:55
#define RT_CONN_PX(r, actorSize, x, y, z)
Some macros to access routing table values as described above.
Definition routing.h:52
#define RT_CONN_PX_NY(r, actorSize, x, y, z)
Definition routing.h:58
#define RT_STEPUP_NX_PY(r, actorSize, x, y, z)
Definition routing.h:69
#define RT_CONN_PX_PY(r, actorSize, x, y, z)
Definition routing.h:57
#define SizedPosToVec(p, actorSize, v)
SizedPosToVect locates the center of an actor based on size and position.
Definition routing.h:84
#define RT_STEPUP_PY(r, actorSize, x, y, z)
Definition routing.h:64
#define RT_STEPUP_NY(r, actorSize, x, y, z)
Definition routing.h:65
#define RT_CONN_NX(r, actorSize, x, y, z)
Definition routing.h:53
void Com_DefaultExtension(char *path, size_t len, const char *extension)
Sets a default extension if there is none.
Definition shared.cpp:297
void UFO_assert(bool condition, const char *fmt,...)
Definition shared.cpp:627
An 'opening' describes the connection between two adjacent spaces where an actor can exist in a cell.
Definition routing.cpp:164
int invstepup
Definition routing.cpp:168
A 'place' is a part of a grid column where an actor can exist Unlike for a grid-cell,...
Definition routing.cpp:113
bool usable
Definition routing.cpp:118
int floor
Definition routing.cpp:115
pos3_t cell
Definition routing.cpp:114
bool isUsable(void) const
Definition routing.cpp:120
int ceiling
Definition routing.cpp:116
int floorZ
Definition routing.cpp:117
float fraction
Definition tracing.h:58
static mapTiles_t mapTiles
char baseFilename[MAX_OSPATH]
Definition ufo2map.cpp:55
Tracing functions.
#define TRACE_VISIBLE_LEVELS
Definition tracing.h:50
#define PATHFINDING_BIG_STEPUP
The home of the routing tables.
Definition typedefs.h:244
#define PATHFINDING_BIG_STEPDOWN
Definition typedefs.h:246
pos_t pos3_t[3]
Definition ufotypes.h:58
vec_t vec3_t[3]
Definition ufotypes.h:39
int32_t actorSizeEnum_t
Definition ufotypes.h:77
#define VectorClear(a)
Definition vector.h:55
#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 VectorAdd(a, b, dest)
Definition vector.h:47
#define VectorSet(v, x, y, z)
Definition vector.h:59