routing.c

Go to the documentation of this file.
00001 
00006 /*
00007 All original material Copyright (C) 2002-2010 UFO: Alien Invasion.
00008 
00009 Copyright (C) 1997-2001 Id Software, Inc.
00010 
00011 This program is free software; you can redistribute it and/or
00012 modify it under the terms of the GNU General Public License
00013 as published by the Free Software Foundation; either version 2
00014 of the License, or (at your option) any later version.
00015 
00016 This program is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00019 
00020 See the GNU General Public License for more details.
00021 
00022 You should have received a copy of the GNU General Public License
00023 along with this program; if not, write to the Free Software
00024 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00025 
00026 */
00027 
00028 #include "common.h"
00029 #include "routing.h"
00030 
00031 /*
00032 ===============================================================================
00033 MAP TRACING DEBUGGING TABLES
00034 ===============================================================================
00035 */
00036 
00037 qboolean debugTrace = qfalse;
00038 
00039 /*
00040 ==========================================================
00041   LOCAL CONSTANTS
00042 ==========================================================
00043 */
00044 
00045 #define RT_NO_OPENING -1
00046 
00047 /* Width of the box required to stand in a cell by an actor's feet.  */
00048 #define halfMicrostepSize (PATHFINDING_MICROSTEP_SIZE / 2 - DIST_EPSILON)
00049 /* This is a template for the extents of the box used by an actor's feet. */
00050 static const box_t footBox = {{-halfMicrostepSize, -halfMicrostepSize, 0},
00051                         { halfMicrostepSize,  halfMicrostepSize, 0}};
00052 
00053 /* Width of the box required to stand in a cell by an actor's torso.  */
00054 #define half1x1Width (UNIT_SIZE * 1 / 2 - WALL_SIZE - DIST_EPSILON)
00055 #define half2x2Width (UNIT_SIZE * 2 / 2 - WALL_SIZE - DIST_EPSILON)
00056 /* These are templates for the extents of the box used by an actor's torso. */
00057 static const box_t actor1x1Box = {{-half1x1Width, -half1x1Width, 0},
00058                             { half1x1Width,  half1x1Width, 0}};
00059 static const box_t actor2x2Box = {{-half2x2Width, -half2x2Width, 0},
00060                             { half2x2Width,  half2x2Width, 0}};
00061 
00062 /*
00063 ==========================================================
00064   LOCAL TYPES
00065 ==========================================================
00066 */
00067 
00068 /*
00069  * @brief A 'place' is a part of a grid column where an actor can exist
00070  * Unlike for a grid-cell, floor and ceiling are absolute values
00071  */
00072 typedef struct place_s {
00073     pos3_t cell;    
00074     int floor;      
00075     int ceiling;    
00076     int floorZ;     
00077     qboolean usable;
00078 } place_t;
00079 
00080 static inline void RT_PlaceInit (const routing_t *map, const actorSizeEnum_t actorSize, place_t *p, const int x, const int y, const int z)
00081 {
00082     const int relCeiling = RT_CEILING(map, actorSize, x, y, z);
00083     p->cell[0] = x;
00084     p->cell[1] = y;
00085     p->cell[2] = z;
00086     p->floor = RT_FLOOR(map, actorSize, x, y, z) + z * CELL_HEIGHT;
00087     p->ceiling = relCeiling + z * CELL_HEIGHT;
00088     p->floorZ = max(0, p->floor / CELL_HEIGHT) ;
00089     p->usable = (relCeiling && p->floor > -1 && p->ceiling - p->floor >= PATHFINDING_MIN_OPENING) ? qtrue : qfalse;
00090 }
00091 
00092 static inline qboolean RT_PlaceIsUsable (const place_t* p)
00093 {
00094     return p->usable;
00095 }
00096 
00097 static inline qboolean RT_PlaceDoesIntersectEnough (const place_t* p, const place_t* other)
00098 {
00099     return (min(p->ceiling, other->ceiling) - max(p->floor, other->floor) >= PATHFINDING_MIN_OPENING);
00100 }
00101 
00108 static inline int RT_PlaceIsShifted (const place_t* p, const place_t* other)
00109 {
00110     if (!RT_PlaceIsUsable(p) || !RT_PlaceIsUsable(other))
00111         return 0;
00112     if (p->floor < other->floor && p->ceiling < other->ceiling)
00113         return 1;   /* stepping up */
00114     if (p->floor > other->floor && p->ceiling > other->ceiling)
00115         return 2;   /* stepping down */
00116     return 0;
00117 }
00118 
00123 typedef struct opening_s {
00124     int size;       
00125     int base;       
00126     int stepup;     
00127     int invstepup;  
00128 } opening_t;
00129 
00130 /*
00131 ==========================================================
00132   GRID ORIENTED MOVEMENT AND SCANNING
00133 ==========================================================
00134 */
00135 
00136 #ifdef DEBUG
00137 
00149 static void RT_DumpMap (const routing_t *map, actorSizeEnum_t actorSize, int lx, int ly, int lz, int hx, int hy, int hz)
00150 {
00151     int x, y, z;
00152 
00153     Com_Printf("\nRT_DumpMap (%i %i %i) (%i %i %i)\n", lx, ly, lz, hx, hy, hz);
00154     for (z = hz; z >= lz; --z) {
00155         Com_Printf("\nLayer %i:\n   ", z);
00156         for (x = lx; x <= hx; ++x) {
00157             Com_Printf("%9i", x);
00158         }
00159         Com_Printf("\n");
00160         for (y = hy; y >= ly; --y) {
00161             Com_Printf("%3i ", y);
00162             for (x = lx; x <= hx; ++x) {
00163                 Com_Printf("%s%s%s%s "
00164                     , RT_CONN_NX(map, actorSize, x, y, z) ? "w" : " "
00165                     , RT_CONN_PY(map, actorSize, x, y, z) ? "n" : " "
00166                     , RT_CONN_NY(map, actorSize, x, y, z) ? "s" : " "
00167                     , RT_CONN_PX(map, actorSize, x, y, z) ? "e" : " "
00168                     );
00169             }
00170             Com_Printf("\n");
00171         }
00172     }
00173 }
00174 
00179 void RT_DumpWholeMap (mapTiles_t *mapTiles, const routing_t *map)
00180 {
00181     box_t box;
00182     vec3_t normal, origin;
00183     pos3_t start, end, test;
00184     trace_t trace;
00185     int i;
00186 
00187     /* Initialize start, end, and normal */
00188     VectorClear(start);
00189     VectorSet(end, PATHFINDING_WIDTH - 1, PATHFINDING_WIDTH - 1, PATHFINDING_HEIGHT - 1);
00190     VectorSet(normal, UNIT_SIZE / 2, UNIT_SIZE / 2, UNIT_HEIGHT / 2);
00191     VectorClear(origin);
00192 
00193     for (i = 0; i < 3; i++) {
00194         /* Lower positive boundary */
00195         while (end[i] > start[i]) {
00196             /* Adjust ceiling */
00197             VectorCopy(start, test);
00198             test[i] = end[i] - 1; /* test is now one floor lower than end */
00199             /* Prep boundary box */
00200             PosToVec(test, box.mins);
00201             VectorSubtract(box.mins, normal, box.mins);
00202             PosToVec(end, box.maxs);
00203             VectorAdd(box.maxs, normal, box.maxs);
00204             /* Test for stuff in a small box, if there is something then exit while */
00205             trace = RT_COMPLETEBOXTRACE_SIZE(mapTiles, origin, origin, &box, NULL);
00206             if (trace.fraction < 1.0)
00207                 break;
00208             /* There is nothing, lower the boundary. */
00209             end[i]--;
00210         }
00211 
00212         /* Raise negative boundary */
00213         while (end[i] > start[i]) {
00214             /* Adjust ceiling */
00215             VectorCopy(end, test);
00216             test[i] = start[i] + 1; /* test is now one floor lower than end */
00217             /* Prep boundary box */
00218             PosToVec(start, box.mins);
00219             VectorSubtract(box.mins, normal, box.mins);
00220             PosToVec(test, box.maxs);
00221             VectorAdd(box.maxs, normal, box.maxs);
00222             /* Test for stuff in a small box, if there is something then exit while */
00223             trace = RT_COMPLETEBOXTRACE_SIZE(mapTiles, origin, origin, &box, NULL);
00224             if (trace.fraction < 1.0)
00225                 break;
00226             /* There is nothing, raise the boundary. */
00227             start[i]++;
00228         }
00229     }
00230 
00231     /* Dump the client map */
00232     RT_DumpMap(map, 0, start[0], start[1], start[2], end[0], end[1], end[2]);
00233 }
00234 #endif
00235 
00244 void RT_GetMapSize (mapTiles_t *mapTiles, vec3_t map_min, vec3_t map_max)
00245 {
00246     box_t box;
00247     const vec3_t normal = {UNIT_SIZE / 2, UNIT_SIZE / 2, UNIT_HEIGHT / 2};
00248     pos3_t start, end, test;
00249     vec3_t origin;
00250     int i;
00251     trace_t trace;
00252 
00253     /* Initialize start, end, and normal */
00254     VectorSet(start, 0, 0, 0);
00255     VectorSet(end, PATHFINDING_WIDTH - 1, PATHFINDING_WIDTH - 1, PATHFINDING_HEIGHT - 1);
00256     VectorCopy(vec3_origin, origin);
00257 
00258     for (i = 0; i < 3; i++) {
00259         /* Lower positive boundary */
00260         while (end[i] > start[i]) {
00261             /* Adjust ceiling */
00262             VectorCopy(start, test);
00263             test[i] = end[i]; /* the box from test to end is now one cell high */
00264             /* Prep boundary box */
00265             PosToVec(test, box.mins);
00266             VectorSubtract(box.mins, normal, box.mins);
00267             PosToVec(end, box.maxs);
00268             VectorAdd(box.maxs, normal, box.maxs);
00269             /* Test for stuff in a small box, if there is something then exit while */
00270             trace = RT_COMPLETEBOXTRACE_SIZE(mapTiles, origin, origin, &box, NULL);
00271             if (trace.fraction < 1.0)
00272                 break;
00273             /* There is nothing, lower the boundary. */
00274             end[i]--;
00275         }
00276 
00277         /* Raise negative boundary */
00278         while (end[i] > start[i]) {
00279             /* Adjust ceiling */
00280             VectorCopy(end, test);
00281             test[i] = start[i]; /* the box from start to test is now one cell high */
00282             /* Prep boundary box */
00283             PosToVec(start, box.mins);
00284             VectorSubtract(box.mins, normal, box.mins);
00285             PosToVec(test, box.maxs);
00286             VectorAdd(box.maxs, normal, box.maxs);
00287             /* Test for stuff in a small box, if there is something then exit while */
00288             trace = RT_COMPLETEBOXTRACE_SIZE(mapTiles, origin, origin, &box, NULL);
00289             if (trace.fraction < 1.0)
00290                 break;
00291             /* There is nothing, raise the boundary. */
00292             start[i]++;
00293         }
00294     }
00295 
00296     /* Com_Printf("Extents: (%i, %i, %i) to (%i, %i, %i)\n", start[0], start[1], start[2], end[0], end[1], end[2]); */
00297 
00298     /* convert to vectors */
00299     PosToVec(start, map_min);
00300     PosToVec(end, map_max);
00301 
00302     /* Stretch to the exterior edges of our extents */
00303     VectorSubtract(map_min, normal, map_min);
00304     VectorAdd(map_max, normal, map_max);
00305 }
00306 
00307 
00308 /*
00309 ===============================================================================
00310 NEW MAP TRACING FUNCTIONS
00311 ===============================================================================
00312 */
00313 
00323 qboolean RT_AllCellsBelowAreFilled (const routing_t * map, const int actorSize, const pos3_t pos)
00324 {
00325     int i;
00326 
00327     /* the -1 level is considered solid ground */
00328     if (pos[2] == 0)
00329         return qtrue;
00330 
00331     for (i = 0; i < pos[2]; i++) {
00332         if (RT_CEILING(map, actorSize, pos[0], pos[1], i) != 0)
00333             return qfalse;
00334     }
00335     return qtrue;
00336 }
00337 
00353 int RT_CheckCell (mapTiles_t *mapTiles, routing_t * map, const int actorSize, const int x, const int y, const int z, const char **list)
00354 {
00355     /* Width of the box required to stand in a cell by an actor's torso.  */
00356     const float halfActorWidth = UNIT_SIZE * actorSize / 2 - WALL_SIZE - DIST_EPSILON;
00357     /* This is a template for the extents of the box used by an actor's legs. */
00358     const box_t legBox = {{-halfMicrostepSize, -halfMicrostepSize, 0},
00359                             { halfMicrostepSize,  halfMicrostepSize, QuantToModel(PATHFINDING_LEGROOMHEIGHT) - DIST_EPSILON * 2}};
00360     /* This is a template for the extents of the box used by an actor's torso. */
00361     const box_t torsoBox = {{-halfActorWidth, -halfActorWidth, QuantToModel(PATHFINDING_LEGROOMHEIGHT)},
00362                             { halfActorWidth,  halfActorWidth, QuantToModel(PATHFINDING_MIN_OPENING) - DIST_EPSILON * 2}};
00363     /* This is a template for the ceiling trace after an actor's torso space has been found. */
00364     const box_t ceilBox = {{-halfActorWidth, -halfActorWidth, 0},
00365                             { halfActorWidth,  halfActorWidth, 0}};
00366 
00367     vec3_t start, end; /* Start and end of the downward traces. */
00368     vec3_t tstart, tend; /* Start and end of the upward traces. */
00369     box_t box; /* Holds the exact bounds to be traced for legs and torso. */
00370     pos3_t pos;
00371     float bottom, top; /* Floor and ceiling model distances from the cell base. (in mapunits) */
00372     float initial; /* Cell floor and ceiling z coordinate. */
00373     int bottomQ, topQ; /* The floor and ceiling in QUANTs */
00374     int i;
00375     int fz, cz; /* Floor and ceiling Z cell coordinates */
00376     trace_t tr;
00377 
00378     assert(actorSize > ACTOR_SIZE_INVALID && actorSize <= ACTOR_MAX_SIZE);
00379     assert(map);
00380     /* x and y cannot exceed PATHFINDING_WIDTH - actorSize */
00381     assert((x >= 0) && (x <= PATHFINDING_WIDTH - actorSize));
00382     assert((y >= 0) && (y <= PATHFINDING_WIDTH - actorSize));
00383     assert(z < PATHFINDING_HEIGHT);
00384 
00385     /* calculate tracing coordinates */
00386     VectorSet(pos, x, y, z);
00387     SizedPosToVec(pos, actorSize, end); /* end is now at the center of the cells the actor occupies. */
00388 
00389     /* prepare fall down check */
00390     VectorCopy(end, start);
00391     /*
00392      * Adjust these points so that start to end is from the top of the cell to the bottom of the model.
00393      */
00394     initial = start[2] + UNIT_HEIGHT / 2; /* This is the top-most starting point in this cell. */
00395     start[2] += UNIT_HEIGHT / 2 - QUANT; /* This one QUANT unit below initial. */
00396     end[2] = -UNIT_HEIGHT * 2; /* To the bottom of the model! (Plus some for good measure) */
00397 
00398     /*
00399      * Trace for a floor.  Steps:
00400      * 1. Start at the top of the designated cell and scan toward the model's base.
00401      * 2. If we do not find a brush, then this cell is bottomless and not enterable.
00402      * 3. We have found an upward facing brush.  Scan up PATHFINDING_LEGROOMHEIGHT height.
00403      * 4. If we find anything, then this space is too small of an opening.  Restart just below our current floor.
00404      * 5. Trace up towards the model ceiling with a box as large as the actor.  The first obstruction encountered
00405      *      marks the ceiling.  If there are no obstructions, the model ceiling is the ceiling.
00406      * 6. If the opening between the floor and the ceiling is not at least PATHFINDING_MIN_OPENING tall, then
00407      *      restart below the current floor.
00408      */
00409     while (qtrue) { /* Loop forever, we will exit if we hit the model bottom or find a valid floor. */
00410         if (debugTrace)
00411             Com_Printf("[(%i, %i, %i, %i)]Casting floor (%f, %f, %f) to (%f, %f, %f)\n",
00412                 x, y, z, actorSize, start[0], start[1], start[2], end[0], end[1], end[2]);
00413 
00414         tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, start, end, &footBox, list);
00415         if (tr.fraction >= 1.0) {
00416             /* There is no brush underneath this starting point. */
00417             if (debugTrace)
00418                 Com_Printf("Reached bottom of map.  No floor in cell(s). %f\n", tr.endpos[2]);
00419             /* Mark all cells to the model base as filled. */
00420             for (i = z; i >= 0 ; i--) {
00421                 /* no floor in this cell, it is bottomless! */
00422                 RT_FLOOR(map, actorSize, x, y, i) = -1 - i * CELL_HEIGHT; /* There is no floor in this cell, place it at -1 below the model. */
00423                 RT_CEILING(map, actorSize, x, y, i) = 0; /* There is no ceiling, the true indicator of a filled cell. */
00424             }
00425             /* return 0 to indicate we just scanned the model bottom. */
00426             return 0;
00427         }
00428 
00429         /* We have hit a brush that faces up and can be stood on. Look for a ceiling. */
00430         bottom = tr.endpos[2];  /* record the floor position. */
00431 
00432         assert(initial > bottom);
00433 
00434         if (debugTrace)
00435             Com_Printf("Potential floor found at %f.\n", bottom);
00436 
00437         /* Record the hit position in tstart for later use. */
00438         VectorCopy(tr.endpos, tstart);
00439 
00440         /* Prep the start and end of the "leg room" test. */
00441         VectorAdd(tstart, legBox.mins, box.mins); /* Now bmins has the lower required foot space extent */
00442         VectorAdd(tstart, legBox.maxs, box.maxs); /* Now bmaxs has the upper required foot space extent */
00443 
00444         if (debugTrace)
00445             Com_Printf("    Casting leg room (%f, %f, %f) to (%f, %f, %f)\n",
00446                 box.mins[0], box.mins[1], box.mins[2], box.maxs[0], box.maxs[1], box.maxs[2]);
00447         tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, vec3_origin, vec3_origin, &box, list);
00448         if (tr.fraction < 1.0) {
00449             if (debugTrace)
00450                 Com_Printf("Cannot use found surface- leg obstruction found.\n");
00451             /*
00452              * There is a premature obstruction.  We can't use this as a floor.
00453              * Check under start.  We need to have at least the minimum amount of clearance from our ceiling,
00454              * So start at that point.
00455              */
00456             start[2] = bottom - QuantToModel(PATHFINDING_MIN_OPENING);
00457             /* Check in case we are trying to scan too close to the bottom of the model. */
00458             if (start[2] <= QuantToModel(PATHFINDING_MIN_OPENING)) {
00459                 /* There is no brush underneath this starting point. */
00460                 if (debugTrace)
00461                     Com_Printf("Reached bottom of map.  No floor in cell(s).\n");
00462                 /* Mark all cells to the model base as filled. */
00463                 for (i = z; i >= 0 ; i--) {
00464                     /* no floor in this cell, it is bottomless! */
00465                     RT_FLOOR(map, actorSize, x, y, i) = CELL_HEIGHT; /* There is no floor in this cell. */
00466                     RT_CEILING(map, actorSize, x, y, i) = 0; /* There is no ceiling, the true indicator of a filled cell. */
00467                 }
00468                 /* return 0 to indicate we just scanned the model bottom. */
00469                 return 0;
00470             }
00471             /* Restart */
00472             continue;
00473         }
00474 
00475         /* Prep the start and end of the "torso room" test. */
00476         VectorAdd(tstart, torsoBox.mins, box.mins); /* Now bmins has the lower required torso space extent */
00477         VectorAdd(tstart, torsoBox.maxs, box.maxs); /* Now bmaxs has the upper required torso space extent */
00478 
00479         if (debugTrace)
00480             Com_Printf("    Casting torso room (%f, %f, %f) to (%f, %f, %f)\n",
00481                 box.mins[0], box.mins[1], box.mins[2], box.maxs[0], box.maxs[1], box.maxs[2]);
00482         tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, vec3_origin, vec3_origin, &box, list);
00483         if (tr.fraction < 1.0) {
00484             if (debugTrace)
00485                 Com_Printf("Cannot use found surface- torso obstruction found.\n");
00486             /*
00487              * There is a premature obstruction.  We can't use this as a floor.
00488              * Check under start.  We need to have at least the minimum amount of clearance from our ceiling,
00489              * So start at that point.
00490              */
00491             start[2] = bottom - QuantToModel(PATHFINDING_MIN_OPENING);
00492             /* Check in case we are trying to scan too close to the bottom of the model. */
00493             if (start[2] <= QuantToModel(PATHFINDING_MIN_OPENING)) {
00494                 /* There is no brush underneath this starting point. */
00495                 if (debugTrace)
00496                     Com_Printf("Reached bottom of map.  No floor in cell(s).\n");
00497                 /* Mark all cells to the model base as filled. */
00498                 for (i = z; i >= 0 ; i--) {
00499                     /* no floor in this cell, it is bottomless! */
00500                     RT_FLOOR(map, actorSize, x, y, i) = CELL_HEIGHT; /* There is no floor in this cell. */
00501                     RT_CEILING(map, actorSize, x, y, i) = 0; /* There is no ceiling, the true indicator of a filled cell. */
00502                 }
00503                 /* return 0 to indicate we just scanned the model bottom. */
00504                 return 0;
00505             }
00506             /* Restart */
00507             continue;
00508         }
00509 
00510         /*
00511          * If we are here, then the immediate floor is unobstructed MIN_OPENING units high.
00512          * This is a valid floor.  Find the actual ceiling.
00513          */
00514 
00515         tstart[2] = box.maxs[2]; /* The box trace for height starts at the top of the last trace. */
00516         VectorCopy(tstart, tend);
00517         tend[2] = PATHFINDING_HEIGHT * UNIT_HEIGHT; /* tend now reaches the model ceiling. */
00518 
00519         if (debugTrace)
00520             Com_Printf("    Casting ceiling (%f, %f, %f) to (%f, %f, %f)\n",
00521                 tstart[0], tstart[1], tstart[2], tend[0], tend[1], tend[2]);
00522 
00523         tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, tstart, tend, &ceilBox, list);
00524 
00525         /* We found the ceiling. */
00526         top = tr.endpos[2];
00527 
00528         /*
00529          * There is one last possibility:
00530          * If our found ceiling is above the cell we started the scan in, then we may have scanned up through another
00531          * floor (one sided brush).  If this is the case, we set the ceiling to QUANT below the floor of the above
00532          * ceiling if it is lower than our found ceiling.
00533          */
00534         if (tr.endpos[2] > (z + 1) * UNIT_HEIGHT)
00535             top = min(tr.endpos[2], (z + 1) * UNIT_HEIGHT + QuantToModel(RT_FLOOR(map, actorSize, x, y, z + 1) - 1));
00536 
00537         /* We found the ceiling. */
00538         top = tr.endpos[2];
00539 
00540         /* exit the infinite while loop */
00541         break;
00542     }
00543 
00544     assert(bottom <= top);
00545 
00546     /* top and bottom are absolute model heights.  Find the actual cell z coordinates for these heights.
00547      * ...but before rounding, give back the DIST_EPSILON that was added by the trace.
00548      * Actually, we have to give back two DIST_EPSILON to prevent rounding issues */
00549     bottom -= 2 * DIST_EPSILON;
00550     top += 2 * DIST_EPSILON;
00551     bottomQ = ModelFloorToQuant(bottom); /* Convert to QUANT units to ensure the floor is rounded up to the correct value. */
00552     topQ = ModelCeilingToQuant(top); /* Convert to QUANT units to ensure the floor is rounded down to the correct value. */
00553     fz = floor(bottomQ / CELL_HEIGHT); /* Ensure we round down to get the bottom-most affected cell */
00555     cz = min(z, (int)(floor((topQ - 1) / CELL_HEIGHT))); /* Use the lower of z or the calculated ceiling */
00556 
00557     assert(fz <= cz);
00558 
00559     if (debugTrace)
00560         Com_Printf("Valid ceiling found, bottom=%f, top=%f, fz=%i, cz=%i.\n", bottom, top, fz, cz);
00561 
00562     /* Last, update the floors and ceilings of cells from (x, y, fz) to (x, y, cz) */
00563     for (i = fz; i <= cz; i++) {
00564         /* Round up floor to keep feet out of model. */
00565         RT_FLOOR(map, actorSize, x, y, i) = bottomQ - i * CELL_HEIGHT;
00566         /* Round down ceiling to heep head out of model.  Also offset by floor and max at 255. */
00567         RT_CEILING(map, actorSize, x, y, i) = topQ - i * CELL_HEIGHT;
00568         if (debugTrace) {
00569             Com_Printf("floor(%i, %i, %i, %i)=%i.\n", x, y, i, actorSize, RT_FLOOR(map, actorSize, x, y, i));
00570             Com_Printf("ceil(%i, %i, %i, %i)=%i.\n", x, y, i, actorSize, RT_CEILING(map, actorSize, x, y, i));
00571         }
00572     }
00573 
00574     /* Also, update the floors of any filled cells immediately above the ceiling up to our original cell. */
00575     for (i = cz + 1; i <= z; i++) {
00576         RT_FLOOR(map, actorSize, x, y, i) = CELL_HEIGHT; /* There is no floor in this cell. */
00577         RT_CEILING(map, actorSize, x, y, i) = 0; /* There is no ceiling, the true indicator of a filled cell. */
00578         if (debugTrace) {
00579             Com_Printf("floor(%i, %i, %i)=%i.\n", x, y, i, RT_FLOOR(map, actorSize, x, y, i));
00580             Com_Printf("ceil(%i, %i, %i)=%i.\n", x, y, i, RT_CEILING(map, actorSize, x, y, i));
00581         }
00582     }
00583 
00584     /* Return the lowest z coordinate that we updated floors for. */
00585     return fz;
00586 }
00587 
00588 
00601 static int RT_FillPassageData (routing_t * map, const actorSizeEnum_t actorSize, const int dir, const int  x, const int y, const int z, const int openingSize, const int openingBase, const int stepup)
00602 {
00603     const int openingTop = openingBase + openingSize;
00604     int fz, cz; 
00605     int i;
00606 
00607     /* Final interpretation:
00608      * We now have the floor and the ceiling of the passage traveled between the two cells.
00609      * This span may cover many cells vertically.  We can use this to our advantage:
00610      * +Like in the floor tracing, we can assign the direction value for multiple cells and
00611      *  skip some scans.
00612      * +The value of each current cell will list the max allowed height of an actor in the passageway,
00613      *  which also can be used to see if an actor can fly upward.
00614      * +The allowed height will be based off the floor in the cell or the bottom of the cell; we do not
00615      *  want super tall characters to fly through ceilings.
00616      * +To see if an actor can fly down, we check the cells on level down to see if the diagonal movement
00617      *  can be made and that both have ceilings above the current level.
00618      */
00619 
00620     fz = z;
00621     cz = min(PATHFINDING_HEIGHT - 1, ceil((float)openingTop / CELL_HEIGHT) - 1);
00622 
00623     /* last chance- if cz < z, then bail (and there is an error with the ceiling data somewhere */
00624     if (cz < z) {
00625         /* We can't go this way. */
00626         RT_CONN(map, actorSize, x, y, z, dir) = 0;
00627         RT_STEPUP(map, actorSize, x, y, z, dir) = PATHFINDING_NO_STEPUP;
00628         if (debugTrace)
00629             Com_Printf("Passage found but below current cell, opening_base=%i, opening_top=%i, z = %i, cz = %i.\n", openingBase, openingTop, z, cz);
00630         return z;
00631     }
00632 
00633     if (debugTrace)
00634         Com_Printf("Passage found, opening_base=%i, opening_size=%i, opening_top=%i, stepup=%i. (%i to %i)\n", openingBase, openingSize, openingTop, stepup, fz, cz);
00635 
00636     assert(fz <= z && z <= cz);
00637 
00638     /* Last, update the routes of cells from (x, y, fz) to (x, y, cz) for direction dir */
00639     for (i = fz; i <= cz; i++) {
00640         /* Offset from the floor or the bottom of the current cell, whichever is higher. */
00641         /* Only if > 0 */
00642         RT_CONN_TEST(map, actorSize, x, y, i, dir);
00643         assert (openingTop - max(openingBase, i * CELL_HEIGHT) >= 0);
00644         RT_CONN(map, actorSize, x, y, i, dir) = openingTop - max(openingBase, i * CELL_HEIGHT);
00645         /* The stepup is 0 for all cells that are not at the floor. */
00646         RT_STEPUP(map, actorSize, x, y, i, dir) = 0;
00647         if (debugTrace) {
00648             Com_Printf("RT_CONN for (%i, %i, %i) as:%i dir:%i = %i\n", x, y, i, actorSize, dir, RT_CONN(map, actorSize, x, y, i, dir));
00649         }
00650     }
00651 
00652     RT_STEPUP(map, actorSize, x, y, z, dir) = stepup;
00653     if (debugTrace) {
00654         Com_Printf("Final RT_STEPUP for (%i, %i, %i) as:%i dir:%i = %i\n", x, y, z, actorSize, dir, stepup);
00655     }
00656 
00657     /*
00658      * Return the highest z coordinate scanned- cz if fz==cz, z==cz, or the floor in cz is negative.
00659      * Otherwise cz - 1 to recheck cz in case there is a floor in cz with its own ceiling.
00660      */
00661     if (fz == cz || z == cz || RT_FLOOR(map, actorSize, x, y, cz) < 0)
00662         return cz;
00663     return cz - 1;
00664 }
00665 
00674 static trace_t RT_ObstructedTrace (mapTiles_t *mapTiles, const vec3_t start, const vec3_t end, actorSizeEnum_t actorSize, int hi, int lo, const char **list)
00675 {
00676     box_t box; 
00677     const float halfActorWidth = UNIT_SIZE * actorSize / 2 - WALL_SIZE - DIST_EPSILON;
00678 
00679     /* Configure the box trace extents. The box is relative to the original floor. */
00680     VectorSet(box.maxs, halfActorWidth, halfActorWidth, QuantToModel(hi) - DIST_EPSILON);
00681     VectorSet(box.mins, -halfActorWidth, -halfActorWidth, QuantToModel(lo)  + DIST_EPSILON);
00682 
00683     /* perform the trace, then return true if the trace was obstructed. */
00684     return RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, start, end, &box, list);
00685 }
00686 
00687 
00697 static int RT_FindOpeningFloorFrac (mapTiles_t *mapTiles, const vec3_t start, const vec3_t end, const actorSizeEnum_t actorSize, const float frac, const int startingHeight, const char **list)
00698 {
00699     vec3_t mstart, mend;        
00700     trace_t tr;
00701     const box_t* box = (actorSize == ACTOR_SIZE_NORMAL ? &actor1x1Box : &actor2x2Box);
00702 
00703     /* Position mstart and mend at the fraction point */
00704     VectorInterpolation(start, end, frac, mstart);
00705     VectorCopy(mstart, mend);
00706     mstart[2] = QuantToModel(startingHeight) + (QUANT / 2); /* Set at the starting height, plus a little more to keep us off a potential surface. */
00707     mend[2] = -QUANT;  /* Set below the model. */
00708 
00709     tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, mstart, mend, box, list);
00710 
00711     if (debugTrace)
00712         Com_Printf("Brush found at %f.\n", tr.endpos[2]);
00713 
00714     /* OK, now we have the floor height value in tr.endpos[2].
00715      * Divide by QUANT and round up.
00716      */
00717     return ModelFloorToQuant(tr.endpos[2] - DIST_EPSILON);
00718 }
00719 
00720 
00730 static int RT_FindOpeningCeilingFrac (mapTiles_t *mapTiles, const vec3_t start, const vec3_t end, const actorSizeEnum_t actorSize, const float frac, const int startingHeight, const char **list)
00731 {
00732     vec3_t mstart, mend;    
00733     trace_t tr;
00734     const box_t* box = (actorSize == ACTOR_SIZE_NORMAL ? &actor1x1Box : &actor2x2Box);  
00736     /* Position mstart and mend at the midpoint */
00737     VectorInterpolation(start, end, frac, mstart);
00738     VectorCopy(mstart, mend);
00739     mstart[2] = QuantToModel(startingHeight) - (QUANT / 2); /* Set at the starting height, minus a little more to keep us off a potential surface. */
00740     mend[2] = UNIT_HEIGHT * PATHFINDING_HEIGHT + QUANT;  /* Set above the model. */
00741 
00742     tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, mstart, mend, box, list);
00743 
00744     if (debugTrace)
00745         Com_Printf("Brush found at %f.\n", tr.endpos[2]);
00746 
00747     /* OK, now we have the floor height value in tr.endpos[2].
00748      * Divide by QUANT and round down. */
00749     return ModelCeilingToQuant(tr.endpos[2] + DIST_EPSILON);
00750 }
00751 
00752 
00762 static int RT_FindOpeningFloor (mapTiles_t *mapTiles, const vec3_t start, const vec3_t end, const actorSizeEnum_t actorSize, const int startingHeight, const int floorLimit, const char **list)
00763 {
00764     /* Look for additional space below init_bottom, down to lowest_bottom. */
00765     int midfloor;
00766 
00767     if (start[0] == end[0] || start[1] == end[1]) {
00768         /* For orthogonal dirs, find the height at the midpoint. */
00769         midfloor = RT_FindOpeningFloorFrac(mapTiles, start, end, actorSize, 0.5, startingHeight, list);
00770         if (debugTrace)
00771             Com_Printf("midfloor:%i.\n", midfloor);
00772     } else {
00773         int midfloor2;
00774 
00775         /* If this is diagonal, trace the 1/3 and 2/3 points instead. */
00776         /* 1/3 point */
00777         midfloor = RT_FindOpeningFloorFrac(mapTiles, start, end, actorSize, 0.33, startingHeight, list);
00778         if (debugTrace)
00779             Com_Printf("1/3floor:%i.\n", midfloor);
00780 
00781         /* 2/3 point */
00782         midfloor2 = RT_FindOpeningFloorFrac(mapTiles, start, end, actorSize, 0.66, startingHeight, list);
00783         if (debugTrace)
00784             Com_Printf("2/3floor:%i.\n", midfloor2);
00785         midfloor = max(midfloor, midfloor2);
00786     }
00787 
00788     /* return the highest floor. */
00789     return max(floorLimit, midfloor);
00790 }
00791 
00792 
00802 static int RT_FindOpeningCeiling (mapTiles_t *mapTiles, const vec3_t start, const vec3_t end, const actorSizeEnum_t actorSize, const int startingHeight, const int ceilLimit, const char **list)
00803 {
00804     int midceil;
00805 
00806     if (start[0] == end[0] || start[1] == end[1]) {
00807         /* For orthogonal dirs, find the height at the midpoint. */
00808         midceil = RT_FindOpeningCeilingFrac(mapTiles, start, end, actorSize, 0.5, startingHeight, list);
00809         if (debugTrace)
00810             Com_Printf("midceil:%i.\n", midceil);
00811     } else {
00812         int midceil2;
00813 
00814         /* If this is diagonal, trace the 1/3 and 2/3 points instead. */
00815         /* 1/3 point */
00816         midceil = RT_FindOpeningCeilingFrac(mapTiles, start, end, actorSize, 0.33, startingHeight, list);
00817         if (debugTrace)
00818             Com_Printf("1/3ceil:%i.\n", midceil);
00819 
00820         /* 2/3 point */
00821         midceil2 = RT_FindOpeningCeilingFrac(mapTiles, start, end, actorSize, 0.66, startingHeight, list);
00822         if (debugTrace)
00823             Com_Printf("2/3ceil:%i.\n", midceil2);
00824         midceil = min(midceil, midceil2);
00825     }
00826 
00827     /* return the lowest ceiling. */
00828     return min(ceilLimit, midceil);
00829 }
00830 
00831 
00832 static int RT_CalcNewZ (const routing_t * map, const actorSizeEnum_t actorSize, const int ax, const int ay, const int top, const int hi)
00833 {
00834     int temp_z, adj_lo;
00835 
00836     temp_z = min(floor((hi - 1) / CELL_HEIGHT), PATHFINDING_HEIGHT - 1);
00837     adj_lo = RT_FLOOR(map, actorSize, ax, ay, temp_z) + temp_z * CELL_HEIGHT;
00838     if (adj_lo > hi) {
00839         temp_z--;
00840         adj_lo = RT_FLOOR(map, actorSize, ax, ay, temp_z) + temp_z * CELL_HEIGHT;
00841     }
00847     if (adj_lo >= 0 && top - adj_lo >= PATHFINDING_MIN_OPENING - PATHFINDING_MIN_STEPUP) {
00848         if (debugTrace)
00849             Com_Printf("Found floor in destination cell: %i (%i).\n", adj_lo, temp_z);
00850         return floor(adj_lo / CELL_HEIGHT);
00851     }
00852     if (debugTrace)
00853         Com_Printf("Skipping found floor in destination cell- not enough opening: %i (%i).\n", adj_lo, temp_z);
00854 
00855     return RT_NO_OPENING;
00856 }
00857 
00858 
00875 static int RT_TraceOpening (mapTiles_t *mapTiles, const routing_t * map, const actorSizeEnum_t actorSize, 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 *lo_val, int *hi_val, const char **list)
00876 {
00877     trace_t tr = RT_ObstructedTrace(mapTiles, start, end, actorSize, hi, lo, list);
00878     if (tr.fraction >= 1.0) {
00879         lo = RT_FindOpeningFloor(mapTiles, start, end, actorSize, lo, bottom, list);
00880         hi = RT_FindOpeningCeiling(mapTiles, start, end, actorSize, hi, top, list);
00881         if (hi - lo >= PATHFINDING_MIN_OPENING) {
00882             int temp_z;
00883             if (lo == -1) {
00884                 if (debugTrace)
00885                     Com_Printf("Bailing- no floor in destination cell.\n");
00886                 *lo_val = *hi_val = 0;
00887                 return RT_NO_OPENING;
00888             }
00889             /* This opening works, use it! */
00890             *lo_val = lo;
00891             *hi_val = hi;
00892             /* Find the floor for the highest adjacent cell in this passage. */
00893             temp_z = RT_CalcNewZ(map, actorSize, ax, ay, top, hi);
00894             if (temp_z != RT_NO_OPENING)
00895                 return temp_z;
00896         }
00897     }
00898     *lo_val = *hi_val = hi;
00899     return RT_NO_OPENING;
00900 }
00901 
00902 
00916 static int RT_FindOpening (mapTiles_t *mapTiles, const routing_t * map, const actorSizeEnum_t actorSize, place_t* from, const int ax, const int ay, const int bottom, const int top, int *lo_val, int *hi_val, const char **list)
00917 {
00918     vec3_t start, end;
00919     pos3_t pos;
00920     int temp_z;
00921 
00922     const int endfloor = RT_FLOOR(map, actorSize, ax, ay, from->cell[2]) + from->cell[2] * CELL_HEIGHT;
00923     const int hifloor = max (endfloor, bottom);
00924 
00925     if (debugTrace)
00926         Com_Printf("ef:%i t:%i b:%i\n", endfloor, top, bottom);
00927 
00928     if (bottom == -1) {
00929         if (debugTrace)
00930             Com_Printf("Bailing- no floor in current cell.\n");
00931         *lo_val = *hi_val = 0;
00932         return RT_NO_OPENING;
00933     }
00934 
00935     /* Initialize the starting vector */
00936     SizedPosToVec(from->cell, actorSize, start);
00937 
00938     /* Initialize the ending vector */
00939     VectorSet(pos, ax, ay, from->cell[2]);
00940     SizedPosToVec(pos, actorSize, end);
00941 
00942     /* Initialize the z component of both vectors */
00943     start[2] = end[2] = 0;
00944 
00945     /* shortcut: if both ceilings are the sky, we can check for walls
00946      * AND determine the bottom of the passage in just one trace */
00947     if (from->ceiling >= PATHFINDING_HEIGHT * CELL_HEIGHT
00948      && from->cell[2] * CELL_HEIGHT + RT_CEILING(map, actorSize, ax, ay, from->cell[2]) >= PATHFINDING_HEIGHT * CELL_HEIGHT) {
00949         vec3_t sky, earth;
00950         const box_t* box = (actorSize == ACTOR_SIZE_NORMAL ? &actor1x1Box : &actor2x2Box);
00951         trace_t tr;
00952         int tempBottom;
00953 
00954         if (debugTrace)
00955             Com_Printf("Using sky trace.\n");
00956 
00957         VectorInterpolation(start, end, 0.5, sky);  /* center it halfway between the cells */
00958         VectorCopy(sky, earth);
00959         sky[2] = UNIT_HEIGHT * PATHFINDING_HEIGHT;  /* Set to top of model. */
00960         earth[2] = QuantToModel(bottom);
00961 
00962         tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, sky, earth, box, list);
00963         tempBottom = ModelFloorToQuant(tr.endpos[2]);
00964         if (tempBottom <= bottom + PATHFINDING_MIN_STEPUP) {
00965             const int hi = bottom + PATHFINDING_MIN_OPENING;
00966             if (debugTrace)
00967                 Com_Printf("Found opening with sky trace.\n");
00968             *lo_val = tempBottom;
00969             *hi_val = CELL_HEIGHT * PATHFINDING_HEIGHT;
00970             return RT_CalcNewZ(map, actorSize, ax, ay, top, hi);
00971         }
00972         if (debugTrace)
00973             Com_Printf("Failed sky trace.\n");
00974     }
00975     /* Warning: never try to make this an 'else if', or 'arched entry' situations will fail !! */
00976 
00977     /* Now calculate the "guaranteed" opening, if any. If the opening from
00978      * the floor to the ceiling is not too tall, there must be a section that
00979      * will always be vacant if there is a usable passage of any size and at
00980      * any height. */
00981     if (top - bottom < PATHFINDING_MIN_OPENING * 2) {
00982         const int lo = top - PATHFINDING_MIN_OPENING;
00983         const int hi = bottom + PATHFINDING_MIN_OPENING;
00984         if (debugTrace)
00985             Com_Printf("Tracing closed space from %i to %i.\n", bottom, top);
00986         temp_z = RT_TraceOpening(mapTiles, map, actorSize, start, end, ax, ay, hifloor, top, lo, hi, lo_val, hi_val, list);
00987     } else {
00988         /* There is no "guaranteed" opening, brute force search. */
00989         int lo = bottom;
00990         temp_z = 0;
00991         while (lo <= top - PATHFINDING_MIN_OPENING) {
00992             /* Check for a 1 QUANT opening. */
00993             if (debugTrace)
00994                 Com_Printf("Tracing open space from %i.\n", lo);
00995             temp_z = RT_TraceOpening(mapTiles, map, actorSize, start, end, ax, ay, bottom, top, lo, lo + 1, lo_val, hi_val, list);
00996             if (temp_z != RT_NO_OPENING)
00997                 break;
00998             /* Credit to Duke: We skip the minimum opening, as if there is a
00999              * viable opening, even one slice above, that opening would be open. */
01000             lo = *hi_val + PATHFINDING_MIN_OPENING;
01001         }
01002     }
01003     return temp_z;
01004 }
01005 
01006 
01019 static int RT_MicroTrace (mapTiles_t *mapTiles, const routing_t * map, const actorSizeEnum_t actorSize, place_t* from, const int ax, const int ay, const int az, const int stairwaySituation, opening_t* opening, const char **list)
01020 {
01021     /* OK, now we have a viable shot across.  Run microstep tests now. */
01022     /* Now calculate the stepup at the floor using microsteps. */
01023     int top = opening->base + opening->size;
01024     signed char bases[UNIT_SIZE / PATHFINDING_MICROSTEP_SIZE + 1];
01025     float sx, sy, ex, ey;
01026     /* Shortcut the value of UNIT_SIZE / PATHFINDING_MICROSTEP_SIZE. */
01027     const int steps = UNIT_SIZE / PATHFINDING_MICROSTEP_SIZE;
01028     trace_t tr;
01029     int i, current_h, highest_h, highest_i = 0, skipped, newBottom;
01030     vec3_t start, end;
01031     pos3_t pos;
01032     int last_step;
01033 
01034     /* First prepare the two known end values. */
01035     bases[0] = from->floor;
01036     bases[steps] = last_step = max(0, RT_FLOOR(map, actorSize, ax, ay, az)) + az * CELL_HEIGHT;
01037 
01038     /* Initialize the starting vector */
01039     SizedPosToVec(from->cell, actorSize, start);
01040 
01041     /* Initialize the ending vector */
01042     VectorSet(pos, ax, ay, az);
01043     SizedPosToVec(pos, actorSize, end);
01044 
01045     /* Now prep the z values for start and end. */
01046     start[2] = QuantToModel(opening->base) + 1; 
01047     end[2] = -QUANT;
01048 
01049     /* Memorize the start and end x,y points */
01050     sx = start[0];
01051     sy = start[1];
01052     ex = end[0];
01053     ey = end[1];
01054 
01055     newBottom = max(bases[0], bases[steps]);
01056     /* Now calculate the rest of the microheights. */
01057     for (i = 1; i < steps; i++) {
01058         start[0] = end[0] = sx + (ex - sx) * (i / (float)steps);
01059         start[1] = end[1] = sy + (ey - sy) * (i / (float)steps);
01060 
01061         /* perform the trace, then return true if the trace was obstructed. */
01062         tr = RT_COMPLETEBOXTRACE_PASSAGE(mapTiles, start, end, &footBox, list);
01063         if (tr.fraction >= 1.0) {
01064             bases[i] = -1;
01065         } else {
01066             bases[i] = ModelFloorToQuant(tr.endpos[2] - DIST_EPSILON);
01067             /* Walking through glass fix:
01068              * It is possible to have an obstruction that can be skirted around diagonally
01069              * because the microtraces are so tiny.  But, we have a full size trace in opening->base
01070              * that apporoximates where legroom ends.  If the found floor of the middle microtrace is
01071              * too low, then set it to the worst case scenario floor based on base->opening.
01072              */
01073             if (i == floor(steps / 2.0) && bases[i] < opening->base - PATHFINDING_MIN_STEPUP) {
01074                 if (debugTrace)
01075                     Com_Printf("Adjusting middle trace- the known base is too high. \n");
01076                 bases[i] = opening->base - PATHFINDING_MIN_STEPUP;
01077             }
01078         }
01079 
01080         if (debugTrace)
01081             Com_Printf("Microstep %i from (%f, %f, %f) to (%f, %f, %f) = %i [%f]\n",
01082                 i, start[0], start[1], start[2], end[0], end[1], end[2], bases[i], tr.endpos[2]);\
01083 
01084         newBottom = max(newBottom, bases[i]);
01085     }
01086 
01087     if (debugTrace)
01088         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]);
01089 
01090 
01092     /* Now find the maximum stepup moving from (x, y) to (ax, ay). */
01093     /* Initialize stepup. */
01094     current_h = bases[0];
01095     highest_h = -1;
01096     highest_i = 1;
01097     opening->stepup = 0; 
01098     skipped = 0;
01099     for (i = 1; i <= steps; i++) {
01100         if (debugTrace)
01101             Com_Printf("Tracing forward i:%i h:%i\n", i, current_h);
01102         /* If there is a rise, use it. */
01103         if (bases[i] >= current_h || ++skipped > PATHFINDING_MICROSTEP_SKIP) {
01104             if (skipped == PATHFINDING_MICROSTEP_SKIP) {
01105                 i = highest_i;
01106                 if (debugTrace)
01107                     Com_Printf(" Skipped too many steps, reverting to i:%i\n", i);
01108             }
01109             opening->stepup = max(opening->stepup, bases[i] - current_h);
01110             current_h = bases[i];
01111             highest_h = -2;
01112             highest_i = i + 1;
01113             skipped = 0;
01114             if (debugTrace)
01115                 Com_Printf(" Advancing b:%i stepup:%i\n", bases[i], opening->stepup);
01116         } else {
01117             /* We are skipping this step in case the actor can step over this lower step. */
01118             /* Record the step in case it is the highest of the low steps. */
01119             if (bases[i] > highest_h) {
01120                 highest_h = bases[i];
01121                 highest_i = i;
01122             }
01123             if (debugTrace)
01124                 Com_Printf(" Skipped because we are falling, skip:%i.\n", skipped);
01125             /* If this is the last iteration, make sure we go back and get our last stepup tests. */
01126             if (i == steps) {
01127                 skipped = PATHFINDING_MICROSTEP_SKIP;
01128                 i = highest_i - 1;
01129                 if (debugTrace)
01130                     Com_Printf(" Tripping skip counter to perform last tests.\n");
01131             }
01132         }
01133     }
01134 
01136     /* Now find the maximum stepup moving from (x, y) to (ax, ay). */
01137     /* Initialize stepup. */
01138     current_h = bases[steps];
01139     highest_h = -1;
01140     highest_i = steps - 1; 
01141     opening->invstepup = 0; 
01142     skipped = 0;
01143     for (i = steps - 1; i >= 0; i--) {
01144         if (debugTrace)
01145             Com_Printf("Tracing backward i:%i h:%i\n", i, current_h);
01146         /* If there is a rise, use it. */
01147         if (bases[i] >= current_h || ++skipped > PATHFINDING_MICROSTEP_SKIP) {
01148             if (skipped == PATHFINDING_MICROSTEP_SKIP) {
01149                 i = highest_i;
01150                 if (debugTrace)
01151                     Com_Printf(" Skipped too many steps, reverting to i:%i\n", i);
01152             }
01153             opening->invstepup = max(opening->invstepup, bases[i] - current_h);
01154             current_h = bases[i];
01155             highest_h = -2;
01156             highest_i = i - 1;
01157             skipped = 0;
01158             if (debugTrace)
01159                 Com_Printf(" Advancing b:%i stepup:%i\n", bases[i], opening->invstepup);
01160         } else {
01161             /* We are skipping this step in case the actor can step over this lower step. */
01162             /* Record the step in case it is the highest of the low steps. */
01163             if (bases[i] > highest_h) {
01164                 highest_h = bases[i];
01165                 highest_i = i;
01166             }
01167             if (debugTrace)
01168                 Com_Printf(" Skipped because we are falling, skip:%i.\n", skipped);
01169             /* If this is the last iteration, make sure we go back and get our last stepup tests. */
01170             if (i == 0) {
01171                 skipped = PATHFINDING_MICROSTEP_SKIP;
01172                 i = highest_i + 1;
01173                 if (debugTrace)
01174                     Com_Printf(" Tripping skip counter to perform last tests.\n");
01175             }
01176         }
01177     }
01178 
01179     if (stairwaySituation) {
01180         const int middle = bases[4];    /* terrible hack by Duke. This relies on PATHFINDING_MICROSTEP_SIZE being set to 4 !! */
01181 
01182         if (stairwaySituation == 1) {       /* stepping up */
01183             if (bases[1] <= middle &&       /* if nothing in the 1st part of the passage is higher than what's at the border */
01184                 bases[2] <= middle &&
01185                 bases[3] <= middle ) {
01186                 if (debugTrace)
01187                     Com_Printf("Addition granted by ugly stair hack-stepping up.\n");
01188                 return opening->base - middle;
01189             }
01190         } else if (stairwaySituation == 2) {/* stepping down */
01191             if (bases[5] <= middle &&       /* same for the 2nd part of the passage */
01192                 bases[6] <= middle &&
01193                 bases[7] <= middle )
01194                 if (debugTrace)
01195                     Com_Printf("Addition granted by ugly stair hack-stepping down.\n");
01196                 return opening->base - middle;
01197         }
01198     }
01199 
01200     /* Return the confirmed passage opening. */
01201     return opening->base - newBottom;
01202 }
01203 
01204 
01214 static int RT_TraceOnePassage (mapTiles_t *mapTiles, const routing_t * map, const actorSizeEnum_t actorSize, place_t* from, place_t* to, opening_t* opening, const char **list)
01215 {
01216     int hi; 
01217     const int z = from->cell[2];
01218     int az; 
01219     const int lower = max(from->floor, to->floor);
01220     const int upper = min(from->ceiling, to->ceiling);
01221     const int ax = to->cell[0];
01222     const int ay = to->cell[1];
01223 
01224     az = RT_FindOpening(mapTiles, map, actorSize, from, ax, ay, lower, upper, &opening->base, &hi, list);
01225     /* calc opening found so far and set stepup */
01226     opening->size = hi - opening->base;
01227     az = to->floorZ;
01228 
01229     /* We subtract MIN_STEPUP because that is foot space-
01230      * the opening there only needs to be the microtrace
01231      * wide and not the usual dimensions.
01232      */
01233     if (az != RT_NO_OPENING && opening->size >= PATHFINDING_MIN_OPENING - PATHFINDING_MIN_STEPUP) {
01234         const int srcFloor = from->floor;
01235         const int dstFloor = RT_FLOOR(map, actorSize, ax, ay, az) + az * CELL_HEIGHT;
01236         /* if we already have enough headroom, try to skip microtracing */
01238         if (opening->size < CELL_HEIGHT
01239             || abs(srcFloor - opening->base) > PATHFINDING_MIN_STEPUP
01240             || abs(dstFloor - opening->base) > PATHFINDING_MIN_STEPUP) {
01241             int stairway = RT_PlaceIsShifted(from,to);
01242             /* This returns the total opening height, as the
01243              * microtrace may reveal more passage height from the foot space. */
01244             const int bonusSize = RT_MicroTrace(mapTiles, map, actorSize, from, ax, ay, az, stairway, opening, list);
01245             opening->base -= bonusSize;
01246             opening->size = hi - opening->base; /* re-calculate */
01247         } else {
01248             /* Skipping microtracing, just set the stepup values. */
01249             opening->stepup = max(0, opening->base - srcFloor);
01250             opening->invstepup = max(0, opening->base - dstFloor);
01251         }
01252 
01253         /* Now place an upper bound on stepup */
01254         if (opening->stepup > PATHFINDING_MAX_STEPUP) {
01255             opening->stepup = PATHFINDING_NO_STEPUP;
01256         } else {
01257             /* Add rise/fall bit as needed. */
01258             if (az < z && opening->invstepup <= PATHFINDING_MAX_STEPUP)
01259             /* BIG_STEPDOWN indicates 'walking down', don't set it if we're 'falling' */
01260                 opening->stepup |= PATHFINDING_BIG_STEPDOWN;
01261             else if (az > z)
01262                 opening->stepup |= PATHFINDING_BIG_STEPUP;
01263         }
01264 
01265         /* Now place an upper bound on stepup */
01266         if (opening->invstepup > PATHFINDING_MAX_STEPUP) {
01267             opening->invstepup = PATHFINDING_NO_STEPUP;
01268         } else {
01269             /* Add rise/fall bit as needed. */
01270             if (az > z)
01271                 opening->invstepup |= PATHFINDING_BIG_STEPDOWN;
01272             else if (az < z)
01273                 opening->invstepup |= PATHFINDING_BIG_STEPUP;
01274         }
01275 
01276         if (opening->size >= PATHFINDING_MIN_OPENING) {
01277             return opening->size;
01278         }
01279     }
01280 
01281     if (debugTrace)
01282         Com_Printf(" No opening found.\n");
01283     opening->stepup = PATHFINDING_NO_STEPUP;
01284     opening->invstepup = PATHFINDING_NO_STEPUP;
01285     return 0;
01286 }
01287 
01299 static void RT_TracePassage (mapTiles_t *mapTiles, const routing_t * map, const actorSizeEnum_t actorSize, const int x, const int y, const int z, const int ax, const int ay, opening_t* opening, const char **list)
01300 {
01301     int aboveCeil, lowCeil;
01303     place_t from, to, above;
01304     place_t* placeToCheck = NULL;
01305 
01306     RT_PlaceInit(map, actorSize, &from, x, y, z);
01307     RT_PlaceInit(map, actorSize, &to, ax, ay, z);
01308 
01309     aboveCeil = (z < PATHFINDING_HEIGHT - 1) ? RT_CEILING(map, actorSize, ax, ay, z + 1) + (z + 1) * CELL_HEIGHT : to.ceiling;
01310     lowCeil = min(from.ceiling, (RT_CEILING(map, actorSize, ax, ay, z) == 0 || to.ceiling - from.floor < PATHFINDING_MIN_OPENING) ? aboveCeil : to.ceiling);
01311 
01312     /*
01313      * First check the ceiling for the cell beneath the adjacent floor to see
01314      * if there is a potential opening.  The the difference between the
01315      * ceiling and the floor is at least PATHFINDING_MIN_OPENING tall, then
01316      * scan it to see if we can use it.  If we can, then one of two things
01317      * will happen:
01318      *  - The actual adjacent call has no floor of its own, and we will walk
01319      *      or fall into the cell below the adjacent cell anyway.
01320      *  - There is a floor in the adjacent cell, but we will not be able to
01321      *      walk into it anyway because there cannot be any steps if there is
01322      *      a passage.  An actor can walk down into the cell ONLY IF it's
01323      *      negative stepup meets or exceeds the change in floor height.
01324      *      No actors will be allowed to fall because they cannot temporarily
01325      *      occupy the space beneath the floor in the adjacent cell to fall
01326      *      (all actors in the cell must be ON TOP of the floor in the cell).
01327      * If there is no passage, then the obstruction may be used as steps to
01328      * climb up to the adjacent floor.
01329      */
01330     if (RT_PlaceIsUsable(&to) && RT_PlaceDoesIntersectEnough(&from, &to)) {
01331         placeToCheck = &to;
01332     }
01333     else if (z < PATHFINDING_HEIGHT - 1) {
01334         RT_PlaceInit(map, actorSize, &above, ax, ay, z + 1);
01335         if (RT_PlaceIsUsable(&above) && RT_PlaceDoesIntersectEnough(&from, &above)) {
01336             placeToCheck = &above;
01337         }
01338     }
01339     if (!placeToCheck) {
01340         if (debugTrace)
01341             Com_Printf(" No opening found. c:%i lc:%i.\n", from.ceiling, lowCeil);
01342         /* If we got here, then there is no opening from floor to ceiling. */
01343         opening->stepup = PATHFINDING_NO_STEPUP;
01344         opening->invstepup = PATHFINDING_NO_STEPUP;
01345         opening->base = lowCeil;
01346         opening->size = 0;
01347         return;
01348     }
01349 
01350     /*
01351      * Now that we got here, we know that either the opening between the
01352      * ceiling below the adjacent cell and the current floor is too small or
01353      * obstructed.  Try to move onto the adjacent floor.
01354      */
01355     if (debugTrace)
01356         Com_Printf(" Testing up c:%i lc:%i.\n", from.ceiling, lowCeil);
01357 
01358     RT_TraceOnePassage(mapTiles, map, actorSize, &from, placeToCheck, opening, list);
01359     if (opening->size < PATHFINDING_MIN_OPENING) {
01360         if (debugTrace)
01361             Com_Printf(" No opening found.\n");
01362         /* If we got here, then there is no opening from floor to ceiling. */
01363         opening->stepup = PATHFINDING_NO_STEPUP;
01364         opening->invstepup = PATHFINDING_NO_STEPUP;
01365         opening->base = lowCeil;
01366         opening->size = 0;
01367     }
01368 }
01369 
01370 
01382 static int RT_UpdateConnection (mapTiles_t *mapTiles, routing_t * map, const actorSizeEnum_t actorSize, const int x, const int y, const int ax, const int ay, const int z, const int dir, const char **list)
01383 {
01384     const int ceiling = RT_CEILING(map, actorSize, x, y, z);
01385     const int adjCeiling = RT_CEILING(map, actorSize, ax, ay, z);
01386     const int absCeiling = ceiling + z * CELL_HEIGHT;
01387     const int exadjCeiling = (z < PATHFINDING_HEIGHT - 1) ? RT_CEILING(map, actorSize, ax, ay, z + 1) : adjCeiling;
01388     const int exabs_adj_ceiling = (z < PATHFINDING_HEIGHT - 1) ? adjCeiling + (z + 1) * CELL_HEIGHT : absCeiling;
01389     const int absAdjCeiling = adjCeiling + z * CELL_HEIGHT;
01390     const int absFloor = RT_FLOOR(map, actorSize, x, y, z) + z * CELL_HEIGHT;
01391     const int absAdjFloor = RT_FLOOR(map, actorSize, ax, ay, z) + z * CELL_HEIGHT;
01392     opening_t opening;  
01393     int new_z1, az = z;
01394 #if RT_IS_BIDIRECTIONAL == 1
01395     int new_z2;
01396 #endif
01397 
01398     if (debugTrace)
01399         Com_Printf("\n(%i, %i, %i) to (%i, %i, %i) as:%i\n", x, y, z, ax, ay, z, actorSize);
01400 
01401     /* test if the adjacent cell and the cell above it are blocked by a loaded model */
01402     if (adjCeiling == 0 && (exadjCeiling == 0 || ceiling == 0)) {
01403         /* We can't go this way. */
01404         RT_CONN(map, actorSize, x, y, z, dir) = 0;
01405         RT_STEPUP(map, actorSize, x, y, z, dir) = PATHFINDING_NO_STEPUP;
01406 #if RT_IS_BIDIRECTIONAL == 1
01407         RT_CONN(map, actorSize, ax, ay, z, dir ^ 1) = 0;
01408         RT_STEPUP(map, actorSize, ax, ay, z, dir ^ 1) = PATHFINDING_NO_STEPUP;
01409 #endif
01410         if (debugTrace)
01411             Com_Printf("Current cell filled. c:%i ac:%i\n", RT_CEILING(map, actorSize, x, y, z), RT_CEILING(map, actorSize, ax, ay, z));
01412         return z;
01413     }
01414 
01415     /* In case the adjacent floor has no ceiling, swap the current and adjacent cells. */
01416 #if RT_IS_BIDIRECTIONAL == 1
01417     if (ceiling == 0 && adjCeiling != 0) {
01418         return RT_UpdateConnection(map, actorSize, ax, ay, x, y, z, dir ^ 1);
01419     }
01420 #endif
01421 
01426     if (absCeiling < absAdjFloor || exabs_adj_ceiling < absFloor) {
01427         /* We can't go this way. */
01428         RT_CONN(map, actorSize, x, y, z, dir) = 0;
01429         RT_STEPUP(map, actorSize, x, y, z, dir) = PATHFINDING_NO_STEPUP;
01430 #if RT_IS_BIDIRECTIONAL == 1
01431         RT_CONN(map, actorSize, ax, ay, z, dir ^ 1) = 0;
01432         RT_STEPUP(map, actorSize, ax, ay, z, dir ^ 1) = PATHFINDING_NO_STEPUP;
01433 #endif
01434         if (debugTrace)
01435             Com_Printf("Ceiling lower than floor. f:%i c:%i af:%i ac:%i\n", absFloor, absCeiling, absAdjFloor, absAdjCeiling);
01436         return z;
01437     }
01438 
01439     /* Find an opening. */
01440     RT_TracePassage(mapTiles, map, actorSize, x, y, z, ax, ay, &opening, list);
01441     if (debugTrace) {
01442         Com_Printf("Final RT_STEPUP for (%i, %i, %i) as:%i dir:%i = %i\n", x, y, z, actorSize, dir, opening.stepup);
01443     }
01444     /* We always call the fill function.  If the passage cannot be traveled, the
01445      * function fills it in as unpassable. */
01446     new_z1 = RT_FillPassageData(map, actorSize, dir, x, y, z, opening.size, opening.base, opening.stepup);
01447 
01448     if (opening.stepup & PATHFINDING_BIG_STEPUP) {
01449         /* ^ 1 reverses the direction of dir */
01450 #if RT_IS_BIDIRECTIONAL == 1
01451         RT_CONN(map, actorSize, ax, ay, z, dir ^ 1) = 0;
01452         RT_STEPUP(map, actorSize, ax, ay, z, dir ^ 1) = PATHFINDING_NO_STEPUP;
01453 #endif
01454         az++;
01455     } else if (opening.stepup & PATHFINDING_BIG_STEPDOWN) {
01456         az--;
01457     }
01458 #if RT_IS_BIDIRECTIONAL == 1
01459     new_z2 = RT_FillPassageData(map, actorSize, dir ^ 1, ax, ay, az, opening.size, opening.base, opening.invstepup);
01460     if (new_z2 == az && az < z)
01461         new_z2++;
01462     return min(new_z1, new_z2);
01463 #else
01464     return new_z1;
01465 #endif
01466 }
01467 
01468 
01477 void RT_UpdateConnectionColumn (mapTiles_t *mapTiles, routing_t * map, const int actorSize, const int x, const int y, const int dir, const char **list)
01478 {
01479     int z = 0; 
01480     int new_z; 
01482     /* get the neighbor cell's coordinates */
01483     const int ax = x + dvecs[dir][0];
01484     const int ay = y + dvecs[dir][1];
01485 
01486     assert(actorSize > ACTOR_SIZE_INVALID && actorSize <= ACTOR_MAX_SIZE);
01487     assert(map);
01488     assert((x >= 0) && (x <= PATHFINDING_WIDTH - actorSize));
01489     assert((y >= 0) && (y <= PATHFINDING_WIDTH - actorSize));
01490 
01491     /* just a place to place a breakpoint */
01492     if (x == 119 && y == 136 && dir == 2)
01493         new_z = 17;
01494 
01495     /* Ensure that the current coordinates are valid. */
01496     RT_CONN_TEST(map, actorSize, x, y, z, dir);
01497 
01498     /* Com_Printf("At (%i, %i, %i) looking in direction %i with size %i\n", x, y, z, dir, actorSize); */
01499 
01500     /* if our destination cell is out of bounds, bail. */
01501     if (ax < 0 || ax > PATHFINDING_WIDTH - actorSize || ay < 0 || y > PATHFINDING_WIDTH - actorSize) {
01502         /* We can't go this way. */
01503         RT_CONN(map, actorSize, x, y, z, dir) = 0;
01504         RT_STEPUP(map, actorSize, x, y, z, dir) = PATHFINDING_NO_STEPUP;
01505         /* There is only one entry here: There is no inverse cell to store data for. */
01506         if (debugTrace)
01507             Com_Printf("Destination cell non-existant.\n");
01508         return;
01509     }
01510 
01511     /* Ensure that the destination coordinates are valid. */
01512     RT_CONN_TEST(map, actorSize, ax, ay, z, dir);
01513 
01514     /* Main loop */
01515     for (z = 0; z < PATHFINDING_HEIGHT; z++) {
01516         new_z = RT_UpdateConnection(mapTiles, map, actorSize, x, y, ax, ay, z, dir, list);
01517         assert(new_z >= z);
01518         z = new_z;
01519     }
01520 }
01521 
01522 void RT_WriteCSVFiles (const routing_t *map, const char* baseFilename, const ipos3_t mins, const ipos3_t maxs)
01523 {
01524     char filename[MAX_OSPATH], ext[MAX_OSPATH];
01525     qFILE f;
01526     int i, x, y, z;
01527 
01528     /* An elevation files- dumps the floor and ceiling levels relative to each cell. */
01529     for (i = 1; i <= ACTOR_MAX_SIZE; i++) {
01530         strncpy(filename, baseFilename, sizeof(filename) - 1);
01531         sprintf(ext, ".%i.elevation.csv", i);
01532         Com_DefaultExtension(filename, sizeof(filename), ext);
01533         FS_OpenFile(filename, &f, FILE_WRITE);
01534         if (!f.f)
01535             Sys_Error("Could not open file %s.", filename);
01536         FS_Printf(&f, ",");
01537         for (x = mins[0]; x <= maxs[0] - i + 1; x++)
01538             FS_Printf(&f, "x:%i,", x);
01539         FS_Printf(&f, "\n");
01540         for (z = maxs[2]; z >= mins[2]; z--) {
01541             for (y = maxs[1]; y >= mins[1] - i + 1; y--) {
01542                 FS_Printf(&f, "z:%i  y:%i,", z ,y);
01543                 for (x = mins[0]; x <= maxs[0] - i + 1; x++) {
01544                     /* compare results */
01545                     FS_Printf(&f, "h:%i c:%i,", RT_FLOOR(map, i, x, y, z), RT_CEILING(map, i, x, y, z));
01546                 }
01547                 FS_Printf(&f, "\n");
01548             }
01549             FS_Printf(&f, "\n");
01550         }
01551         FS_CloseFile(&f);
01552     }
01553 
01554     /* Output the walls/passage files. */
01555     for (i = 1; i <= ACTOR_MAX_SIZE; i++) {
01556         strncpy(filename, baseFilename, sizeof(filename) - 1);
01557         sprintf(ext, ".%i.walls.csv", i);
01558         Com_DefaultExtension(filename, sizeof(filename), ext);
01559         FS_OpenFile(filename, &f, FILE_WRITE);
01560         if (!f.f)
01561             Sys_Error("Could not open file %s.", filename);
01562         FS_Printf(&f, ",");
01563         for (x = mins[0]; x <= maxs[0] - i + 1; x++)
01564             FS_Printf(&f, "x:%i,", x);
01565         FS_Printf(&f, "\n");
01566         for (z = maxs[2]; z >= mins[2]; z--) {
01567             for (y = maxs[1]; y >= mins[1] - i + 1; y--) {
01568                 FS_Printf(&f, "z:%i  y:%i,", z ,y);
01569                 for (x = mins[0]; x <= maxs[0] - i + 1; x++) {
01570                     /* compare results */
01571                     FS_Printf(&f, "\"");
01572 
01573                     /* NW corner */
01574                     FS_Printf(&f, "%3i-%3i ", RT_CONN_NX_PY(map, i, x, y, z), RT_STEPUP_NX_PY(map, i, x, y, z));
01575 
01576                     /* N side */
01577                     FS_Printf(&f, "%3i-%3i ", RT_CONN_PY(map, i, x, y, z), RT_STEPUP_PY(map, i, x, y, z));
01578 
01579                     /* NE corner */
01580                     FS_Printf(&f, "%3i-%3i ", RT_CONN_PX_PY(map, i, x, y, z), RT_STEPUP_PX_PY(map, i, x, y, z));
01581 
01582                     FS_Printf(&f, "\n");
01583 
01584                     /* W side */
01585                     FS_Printf(&f, "%3i-%3i ", RT_CONN_NX(map, i, x, y, z), RT_STEPUP_NX(map, i, x, y, z));
01586 
01587                     /* Center - display floor height */
01588                     FS_Printf(&f, "_%+2i_ ", RT_FLOOR(map, i, x, y, z));
01589 
01590                     /* E side */
01591                     FS_Printf(&f, "%3i-%3i ", RT_CONN_PX(map, i, x, y, z), RT_STEPUP_PX(map, i, x, y, z));
01592 
01593                     FS_Printf(&f, "\n");
01594 
01595                     /* SW corner */
01596                     FS_Printf(&f, "%3i-%3i ", RT_CONN_NX_NY(map, i, x, y, z), RT_STEPUP_NX_NY(map, i, x, y, z));
01597 
01598                     /* S side */
01599                     FS_Printf(&f, "%3i-%3i ", RT_CONN_NY(map, i, x, y, z), RT_STEPUP_NY(map, i, x, y, z));
01600 
01601                     /* SE corner */
01602                     FS_Printf(&f, "%3i-%3i ", RT_CONN_PX_NY(map, i, x, y, z), RT_STEPUP_PX_NY(map, i, x, y, z));
01603 
01604                     FS_Printf(&f, "\",");
01605                 }
01606                 FS_Printf(&f, "\n");
01607             }
01608             FS_Printf(&f, "\n");
01609         }
01610         FS_CloseFile(&f);
01611     }
01612 }

Generated by  doxygen 1.6.2