Pyrogenesis  trunk
Pathfinding.h
Go to the documentation of this file.
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 
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 {
34 };
35 
36 /**
37  * Returned path.
38  * Waypoints are in *reverse* order (the earliest is at the back of the list)
39  */
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 {
52  PathCost() : data(0) { }
53 
54  /// Construct from a number of horizontal/vertical and diagonal steps
55  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
62  {
63  return PathCost(n, 0);
64  }
65 
66  /// Construct for diagonal movement of given number of steps
67  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 
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 
91  {
92  return data;
93  }
94 
95 private:
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  */
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  */
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  */
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  */
155  inline void TileCenter(u16 i, u16 j, entity_pos_t& x, entity_pos_t& z)
156  {
157  cassert(TERRAIN_TILE_SIZE % 2 == 0);
160  }
161 
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  */
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 
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  */
288 {
289 public:
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 
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 
365 
366  fixed m_Clearance; // min distance from static obstructions
367 
369  {
372  FOUNDATION
373  };
375 
376 private:
382 };
383 
384 #endif // INCLUDED_PATHFINDING
An entity initialisation parameter node.
Definition: ParamNode.h:148
A simple fixed-point number class.
Definition: Fixed.h:115
#define LOGERROR(...)
Definition: CLogger.h:36
Definition: FixedVector2D.h:24
u16 pass_class_t
Definition: Pathfinding.h:29
const entity_pos_t CLEARANCE_EXTENSION_RADIUS
To make sure the long-range pathfinder is more strict than the short-range one, we need to slightly o...
Definition: Pathfinding.h:139
static CFixed Zero()
Definition: Fixed.h:127
Definition: Pathfinding.h:105
const ssize_t TERRAIN_TILE_SIZE
metres [world space units] per tile in x and z
Definition: Terrain.h:40
const std::wstring & ToString() const
Returns the content of this node as a string.
Definition: ParamNode.cpp:237
Definition: Pathfinding.h:287
Returned path.
Definition: Pathfinding.h:40
static PathCost diag(u16 n)
Construct for diagonal movement of given number of steps.
Definition: Pathfinding.h:67
Definition: Pathfinding.h:371
uint16_t u16
Definition: types.h:38
bool IsOk() const
Returns true if this is a valid CParamNode, false if it represents a non-existent node...
Definition: ParamNode.cpp:232
bool operator>=(const PathCost &b) const
Definition: Pathfinding.h:87
u16 m_H
Definition: Grid.h:125
fixed m_MaxSlope
Definition: Pathfinding.h:379
bool operator>(const PathCost &b) const
Definition: Pathfinding.h:88
#define IS_PASSABLE(item, classmask)
Definition: Pathfinding.h:101
PathfinderPassability(pass_class_t mask, const CParamNode &node)
Definition: Pathfinding.h:290
std::vector< Waypoint > m_Waypoints
Definition: Pathfinding.h:42
Definition: Pathfinding.h:31
static PathCost horizvert(u16 n)
Construct for horizontal/vertical movement of given number of steps.
Definition: Pathfinding.h:61
const fixed NAVCELL_SIZE
Size of a navcell in metres ( = TERRAIN_TILE_SIZE / NAVCELLS_PER_TILE)
Definition: Pathfinding.h:122
PathCost operator+(const PathCost &a) const
Definition: Pathfinding.h:72
fixed m_MaxShore
Definition: Pathfinding.h:381
ObstructionHandling m_Obstructions
Definition: Pathfinding.h:374
entity_pos_t z
Definition: Pathfinding.h:33
fixed ToFixed() const
Parses the content of this node as a fixed-point number.
Definition: ParamNode.cpp:261
void NavcellCenter(u16 i, u16 j, entity_pos_t &x, entity_pos_t &z)
Definition: Pathfinding.h:162
#define ENSURE(expr)
ensure the expression <expr> evaluates to non-zero.
Definition: debug.h:287
uint32_t u32
Definition: types.h:39
const int NAVCELLS_PER_TILE
The long-range pathfinder operates primarily over a navigation grid (a uniform-cost 2D passability gr...
Definition: Pathfinding.h:117
const CParamNode & GetChild(const char *name) const
Returns the (unique) child node with the given name, or a node with IsOk() == false if there is none...
Definition: ParamNode.cpp:224
ObstructionHandling
Definition: Pathfinding.h:368
fixed m_Clearance
Definition: Pathfinding.h:366
CFixed Multiply(CFixed n) const
Multiply by a CFixed.
Definition: Fixed.h:311
CFixedVector2D Perpendicular() const
Definition: FixedVector2D.h:215
PathCost & operator+=(const PathCost &a)
Definition: Pathfinding.h:79
fixed m_MinShore
Definition: Pathfinding.h:380
bool 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)
Definition: Pathfinding.h:171
const int NAVCELL_SIZE_LOG2
Definition: Pathfinding.h:124
static const int PASS_CLASS_BITS
Definition: Pathfinding.h:99
pass_class_t m_Mask
Definition: Pathfinding.h:364
static CFixed FromInt(int n)
Definition: Fixed.h:136
u16 NavcellData
Definition: Pathfinding.h:100
void TileCenter(u16 i, u16 j, entity_pos_t &x, entity_pos_t &z)
Returns the position of the center of the given tile.
Definition: Pathfinding.h:155
T & get(int i, int j) const
Definition: Grid.h:117
bool IsPassable(fixed waterdepth, fixed steepness, fixed shoredist)
Definition: Pathfinding.h:359
bool operator<=(const PathCost &b) const
Definition: Pathfinding.h:85
PathCost()
Definition: Pathfinding.h:52
u32 data
Definition: Pathfinding.h:96
void NearestNavcell(entity_pos_t x, entity_pos_t z, u16 &i, u16 &j, u16 w, u16 h)
Compute the navcell indexes on the grid nearest to a given point w, h are the grid dimensions...
Definition: Pathfinding.h:145
fixed m_MaxDepth
Definition: Pathfinding.h:378
const int NAVCELL_SIZE_INT
Definition: Pathfinding.h:123
u32 ToInt()
Definition: Pathfinding.h:90
entity_pos_t x
Definition: Pathfinding.h:33
u16 m_W
Definition: Grid.h:125
#define cassert(expr)
Compile-time assertion.
Definition: code_annotation.h:197
Definition: Pathfinding.h:370
Represents the cost of a path consisting of horizontal/vertical and diagonal movements over a uniform...
Definition: Pathfinding.h:50
const entity_pos_t GOAL_DELTA
For extending the goal outwards/inwards a little bit NOTE: keep next to the definition of NAVCELL_SIZ...
Definition: Pathfinding.h:133
fixed m_MinDepth
Definition: Pathfinding.h:377
PathCost(u16 hv, u16 d)
Construct from a number of horizontal/vertical and diagonal steps.
Definition: Pathfinding.h:55
T clamp(T value, T min, T max)
Definition: MathUtil.h:32
bool operator<(const FCDJointWeightPair &a, const FCDJointWeightPair &b)
Definition: GeomReindex.cpp:64