1 /* Copyright (C) 2016 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #ifndef INCLUDED_PATHFINDING 19 #define INCLUDED_PATHFINDING 20 21 #include "maths/MathUtil.h" 22 #include "ps/CLogger.h" 23 24 #include "simulation2/system/ParamNode.h" 25 #include "graphics/Terrain.h" 26 #include "Grid.h" 27 #include "PathGoal.h" 28 29 typedef u16 pass_class_t; 30 31 struct Waypoint 32 { 33 entity_pos_t x, z; 34 }; 35 36 /** 37 * Returned path. 38 * Waypoints are in *reverse* order (the earliest is at the back of the list) 39 */ 40 struct WaypointPath 41 { 42 std::vector<Waypoint> m_Waypoints; 43 }; 44 45 /** 46 * Represents the cost of a path consisting of horizontal/vertical and 47 * diagonal movements over a uniform-cost grid. 48 * Maximum path length before overflow is about 45K steps. 49 */ 50 struct PathCost 51 { PathCostPathCost52 PathCost() : data(0) { } 53 54 /// Construct from a number of horizontal/vertical and diagonal steps PathCostPathCost55 PathCost(u16 hv, u16 d) 56 : data(hv * 65536 + d * 92682) // 2^16 * sqrt(2) == 92681.9 57 { 58 } 59 60 /// Construct for horizontal/vertical movement of given number of steps horizvertPathCost61 static PathCost horizvert(u16 n) 62 { 63 return PathCost(n, 0); 64 } 65 66 /// Construct for diagonal movement of given number of steps diagPathCost67 static PathCost diag(u16 n) 68 { 69 return PathCost(0, n); 70 } 71 72 PathCost operator+(const PathCost& a) const 73 { 74 PathCost c; 75 c.data = data + a.data; 76 return c; 77 } 78 79 PathCost& operator+=(const PathCost& a) 80 { 81 data += a.data; 82 return *this; 83 } 84 85 bool operator<=(const PathCost& b) const { return data <= b.data; } 86 bool operator< (const PathCost& b) const { return data < b.data; } 87 bool operator>=(const PathCost& b) const { return data >= b.data; } 88 bool operator>(const PathCost& b) const { return data > b.data; } 89 ToIntPathCost90 u32 ToInt() 91 { 92 return data; 93 } 94 95 private: 96 u32 data; 97 }; 98 99 static const int PASS_CLASS_BITS = 16; 100 typedef u16 NavcellData; // 1 bit per passability class (up to PASS_CLASS_BITS) 101 #define IS_PASSABLE(item, classmask) (((item) & (classmask)) == 0) 102 #define PASS_CLASS_MASK_FROM_INDEX(id) ((pass_class_t)(1u << id)) 103 #define SPECIAL_PASS_CLASS PASS_CLASS_MASK_FROM_INDEX((PASS_CLASS_BITS-1)) // 16th bit, used for special in-place computations 104 105 namespace Pathfinding 106 { 107 /** 108 * The long-range pathfinder operates primarily over a navigation grid (a uniform-cost 109 * 2D passability grid, with horizontal/vertical (not diagonal) connectivity). 110 * This is based on the terrain tile passability, plus the rasterized shapes of 111 * obstructions, all expanded outwards by the radius of the units. 112 * Since units are much smaller than terrain tiles, the nav grid should be 113 * higher resolution than the tiles. 114 * We therefore split each terrain tile into NxN "nav cells" (for some integer N, 115 * preferably a power of two). 116 */ 117 const int NAVCELLS_PER_TILE = 4; 118 119 /** 120 * Size of a navcell in metres ( = TERRAIN_TILE_SIZE / NAVCELLS_PER_TILE) 121 */ 122 const fixed NAVCELL_SIZE = fixed::FromInt((int)TERRAIN_TILE_SIZE) / Pathfinding::NAVCELLS_PER_TILE; 123 const int NAVCELL_SIZE_INT = 1; 124 const int NAVCELL_SIZE_LOG2 = 0; 125 126 /** 127 * For extending the goal outwards/inwards a little bit 128 * NOTE: keep next to the definition of NAVCELL_SIZE to avoid init order problems 129 * between translation units. 130 * TODO: figure out whether this is actually needed. It was added back in r8751 (in 2010) for unclear reasons 131 * and it does not seem to really improve behavior today 132 */ 133 const entity_pos_t GOAL_DELTA = NAVCELL_SIZE/8; 134 135 /** 136 * To make sure the long-range pathfinder is more strict than the short-range one, 137 * we need to slightly over-rasterize. So we extend the clearance radius by 1. 138 */ 139 const entity_pos_t CLEARANCE_EXTENSION_RADIUS = fixed::FromInt(1); 140 141 /** 142 * Compute the navcell indexes on the grid nearest to a given point 143 * w, h are the grid dimensions, i.e. the number of navcells per side 144 */ NearestNavcell(entity_pos_t x,entity_pos_t z,u16 & i,u16 & j,u16 w,u16 h)145 inline void NearestNavcell(entity_pos_t x, entity_pos_t z, u16& i, u16& j, u16 w, u16 h) 146 { 147 // Use NAVCELL_SIZE_INT to save the cost of dividing by a fixed 148 i = (u16)clamp((x / NAVCELL_SIZE_INT).ToInt_RoundToNegInfinity(), 0, w - 1); 149 j = (u16)clamp((z / NAVCELL_SIZE_INT).ToInt_RoundToNegInfinity(), 0, h - 1); 150 } 151 152 /** 153 * Returns the position of the center of the given tile 154 */ TileCenter(u16 i,u16 j,entity_pos_t & x,entity_pos_t & z)155 inline void TileCenter(u16 i, u16 j, entity_pos_t& x, entity_pos_t& z) 156 { 157 cassert(TERRAIN_TILE_SIZE % 2 == 0); 158 x = entity_pos_t::FromInt(i*(int)TERRAIN_TILE_SIZE + (int)TERRAIN_TILE_SIZE / 2); 159 z = entity_pos_t::FromInt(j*(int)TERRAIN_TILE_SIZE + (int)TERRAIN_TILE_SIZE / 2); 160 } 161 NavcellCenter(u16 i,u16 j,entity_pos_t & x,entity_pos_t & z)162 inline void NavcellCenter(u16 i, u16 j, entity_pos_t& x, entity_pos_t& z) 163 { 164 x = entity_pos_t::FromInt(i * 2 + 1).Multiply(NAVCELL_SIZE / 2); 165 z = entity_pos_t::FromInt(j * 2 + 1).Multiply(NAVCELL_SIZE / 2); 166 } 167 168 /* 169 * Checks that the line (x0,z0)-(x1,z1) does not intersect any impassable navcells. 170 */ CheckLineMovement(entity_pos_t x0,entity_pos_t z0,entity_pos_t x1,entity_pos_t z1,pass_class_t passClass,const Grid<NavcellData> & grid)171 inline bool CheckLineMovement(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, 172 pass_class_t passClass, const Grid<NavcellData>& grid) 173 { 174 // We shouldn't allow lines between diagonally-adjacent navcells. 175 // It doesn't matter whether we allow lines precisely along the edge 176 // of an impassable navcell. 177 178 // To rasterise the line: 179 // If the line is (e.g.) aiming up-right, then we start at the navcell 180 // containing the start point and the line must either end in that navcell 181 // or else exit along the top edge or the right edge (or through the top-right corner, 182 // which we'll arbitrary treat as the horizontal edge). 183 // So we jump into the adjacent navcell across that edge, and continue. 184 185 // To handle the special case of units that are stuck on impassable cells, 186 // we allow them to move from an impassable to a passable cell (but not 187 // vice versa). 188 189 u16 i0, j0, i1, j1; 190 NearestNavcell(x0, z0, i0, j0, grid.m_W, grid.m_H); 191 NearestNavcell(x1, z1, i1, j1, grid.m_W, grid.m_H); 192 193 // Find which direction the line heads in 194 int di = (i0 < i1 ? +1 : i1 < i0 ? -1 : 0); 195 int dj = (j0 < j1 ? +1 : j1 < j0 ? -1 : 0); 196 197 u16 i = i0; 198 u16 j = j0; 199 200 bool currentlyOnImpassable = !IS_PASSABLE(grid.get(i0, j0), passClass); 201 202 while (true) 203 { 204 // Make sure we are still in the limits 205 ENSURE( 206 ((di > 0 && i0 <= i && i <= i1) || (di < 0 && i1 <= i && i <= i0) || (di == 0 && i == i0)) && 207 ((dj > 0 && j0 <= j && j <= j1) || (dj < 0 && j1 <= j && j <= j0) || (dj == 0 && j == j0))); 208 209 // Fail if we're moving onto an impassable navcell 210 bool passable = IS_PASSABLE(grid.get(i, j), passClass); 211 if (passable) 212 currentlyOnImpassable = false; 213 else if (!currentlyOnImpassable) 214 return false; 215 216 // Succeed if we're at the target 217 if (i == i1 && j == j1) 218 return true; 219 220 // If we can only move horizontally/vertically, then just move in that direction 221 // If we are reaching the limits, we can go straight to the end 222 if (di == 0 || i == i1) 223 { 224 j += dj; 225 continue; 226 } 227 else if (dj == 0 || j == j1) 228 { 229 i += di; 230 continue; 231 } 232 233 // Otherwise we need to check which cell to move into: 234 235 // Check whether the line intersects the horizontal (top/bottom) edge of 236 // the current navcell. 237 // Horizontal edge is (i, j + (dj>0?1:0)) .. (i + 1, j + (dj>0?1:0)) 238 // Since we already know the line is moving from this navcell into a different 239 // navcell, we simply need to test that the edge's endpoints are not both on the 240 // same side of the line. 241 242 // If we are crossing exactly a vertex of the grid, we will get dota or dotb equal 243 // to 0. In that case we arbitrarily choose to move of dj. 244 // This only works because we handle the case (i == i1 || j == j1) beforehand. 245 // Otherwise we could go outside the j limits and never reach the final navcell. 246 247 entity_pos_t xia = entity_pos_t::FromInt(i).Multiply(Pathfinding::NAVCELL_SIZE); 248 entity_pos_t xib = entity_pos_t::FromInt(i+1).Multiply(Pathfinding::NAVCELL_SIZE); 249 entity_pos_t zj = entity_pos_t::FromInt(j + (dj+1)/2).Multiply(Pathfinding::NAVCELL_SIZE); 250 251 CFixedVector2D perp = CFixedVector2D(x1 - x0, z1 - z0).Perpendicular(); 252 entity_pos_t dota = (CFixedVector2D(xia, zj) - CFixedVector2D(x0, z0)).Dot(perp); 253 entity_pos_t dotb = (CFixedVector2D(xib, zj) - CFixedVector2D(x0, z0)).Dot(perp); 254 255 // If the horizontal edge is fully on one side of the line, so the line doesn't 256 // intersect it, we should move across the vertical edge instead 257 if ((dota < entity_pos_t::Zero() && dotb < entity_pos_t::Zero()) || 258 (dota > entity_pos_t::Zero() && dotb > entity_pos_t::Zero())) 259 i += di; 260 else 261 j += dj; 262 } 263 } 264 } 265 266 /* 267 * For efficient pathfinding we want to try hard to minimise the per-tile search cost, 268 * so we precompute the tile passability flags and movement costs for the various different 269 * types of unit. 270 * We also want to minimise memory usage (there can easily be 100K tiles so we don't want 271 * to store many bytes for each). 272 * 273 * To handle passability efficiently, we have a small number of passability classes 274 * (e.g. "infantry", "ship"). Each unit belongs to a single passability class, and 275 * uses that for all its pathfinding. 276 * Passability is determined by water depth, terrain slope, forestness, buildingness. 277 * We need at least one bit per class per tile to represent passability. 278 * 279 * Not all pass classes are used for actual pathfinding. The pathfinder calls 280 * CCmpObstructionManager's Rasterize() to add shapes onto the passability grid. 281 * Which shapes are rasterized depend on the value of the m_Obstructions of each passability 282 * class. 283 * 284 * Passabilities not used for unit pathfinding should not use the Clearance attribute, and 285 * will get a zero clearance value. 286 */ 287 class PathfinderPassability 288 { 289 public: PathfinderPassability(pass_class_t mask,const CParamNode & node)290 PathfinderPassability(pass_class_t mask, const CParamNode& node) : 291 m_Mask(mask) 292 { 293 if (node.GetChild("MinWaterDepth").IsOk()) 294 m_MinDepth = node.GetChild("MinWaterDepth").ToFixed(); 295 else 296 m_MinDepth = std::numeric_limits<fixed>::min(); 297 298 if (node.GetChild("MaxWaterDepth").IsOk()) 299 m_MaxDepth = node.GetChild("MaxWaterDepth").ToFixed(); 300 else 301 m_MaxDepth = std::numeric_limits<fixed>::max(); 302 303 if (node.GetChild("MaxTerrainSlope").IsOk()) 304 m_MaxSlope = node.GetChild("MaxTerrainSlope").ToFixed(); 305 else 306 m_MaxSlope = std::numeric_limits<fixed>::max(); 307 308 if (node.GetChild("MinShoreDistance").IsOk()) 309 m_MinShore = node.GetChild("MinShoreDistance").ToFixed(); 310 else 311 m_MinShore = std::numeric_limits<fixed>::min(); 312 313 if (node.GetChild("MaxShoreDistance").IsOk()) 314 m_MaxShore = node.GetChild("MaxShoreDistance").ToFixed(); 315 else 316 m_MaxShore = std::numeric_limits<fixed>::max(); 317 318 if (node.GetChild("Clearance").IsOk()) 319 { 320 m_Clearance = node.GetChild("Clearance").ToFixed(); 321 322 /* According to Philip who designed the original doc (in docs/pathfinder.pdf), 323 * clearance should usually be integer to ensure consistent behavior when rasterizing 324 * the passability map. 325 * This seems doubtful to me and my pathfinder fix makes having a clearance of 0.8 quite convenient 326 * so I comment out this check, but leave it here for the purpose of documentation should a bug arise. 327 328 if (!(m_Clearance % Pathfinding::NAVCELL_SIZE).IsZero()) 329 { 330 // If clearance isn't an integer number of navcells then we'll 331 // probably get weird behaviour when expanding the navcell grid 332 // by clearance, vs expanding static obstructions by clearance 333 LOGWARNING("Pathfinder passability class has clearance %f, should be multiple of %f", 334 m_Clearance.ToFloat(), Pathfinding::NAVCELL_SIZE.ToFloat()); 335 }*/ 336 } 337 else 338 m_Clearance = fixed::Zero(); 339 340 if (node.GetChild("Obstructions").IsOk()) 341 { 342 std::wstring obstructions = node.GetChild("Obstructions").ToString(); 343 if (obstructions == L"none") 344 m_Obstructions = NONE; 345 else if (obstructions == L"pathfinding") 346 m_Obstructions = PATHFINDING; 347 else if (obstructions == L"foundation") 348 m_Obstructions = FOUNDATION; 349 else 350 { 351 LOGERROR("Invalid value for Obstructions in pathfinder.xml for pass class %d", mask); 352 m_Obstructions = NONE; 353 } 354 } 355 else 356 m_Obstructions = NONE; 357 } 358 IsPassable(fixed waterdepth,fixed steepness,fixed shoredist)359 bool IsPassable(fixed waterdepth, fixed steepness, fixed shoredist) 360 { 361 return ((m_MinDepth <= waterdepth && waterdepth <= m_MaxDepth) && (steepness < m_MaxSlope) && (m_MinShore <= shoredist && shoredist <= m_MaxShore)); 362 } 363 364 pass_class_t m_Mask; 365 366 fixed m_Clearance; // min distance from static obstructions 367 368 enum ObstructionHandling 369 { 370 NONE, 371 PATHFINDING, 372 FOUNDATION 373 }; 374 ObstructionHandling m_Obstructions; 375 376 private: 377 fixed m_MinDepth; 378 fixed m_MaxDepth; 379 fixed m_MaxSlope; 380 fixed m_MinShore; 381 fixed m_MaxShore; 382 }; 383 384 #endif // INCLUDED_PATHFINDING 385