Pyrogenesis  trunk
LongPathfinder.h
Go to the documentation of this file.
1 /* Copyright (C) 2015 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_LONGPATHFINDER
19 #define INCLUDED_LONGPATHFINDER
20 
21 #include "Pathfinding.h"
22 #include "HierarchicalPathfinder.h"
23 
24 #include "PriorityQueue.h"
25 #include "graphics/Overlay.h"
26 #include "renderer/Scene.h"
27 
28 #define ACCEPT_DIAGONAL_GAPS 0
29 
30 /**
31  * Represents the 2D coordinates of a tile.
32  * The i/j components are packed into a single u32, since we usually use these
33  * objects for equality comparisons and the VC2010 optimizer doesn't seem to automatically
34  * compare two u16s in a single operation.
35  * TODO: maybe VC2012 will?
36  */
37 struct TileID
38 {
39  TileID() { }
40 
41  TileID(u16 i, u16 j) : data((i << 16) | j) { }
42 
43  bool operator==(const TileID& b) const
44  {
45  return data == b.data;
46  }
47 
48  /// Returns lexicographic order over (i,j)
49  bool operator<(const TileID& b) const
50  {
51  return data < b.data;
52  }
53 
54  u16 i() const { return data >> 16; }
55  u16 j() const { return data & 0xFFFF; }
56 
57 private:
59 };
60 
61 /**
62  * Tile data for A* computation.
63  * (We store an array of one of these per terrain tile, so it ought to be optimised for size)
64  */
66 {
67 public:
68  enum {
69  STATUS_UNEXPLORED = 0,
70  STATUS_OPEN = 1,
71  STATUS_CLOSED = 2
72  };
73 
74  bool IsUnexplored() const { return GetStatus() == STATUS_UNEXPLORED; }
75  bool IsOpen() const { return GetStatus() == STATUS_OPEN; }
76  bool IsClosed() const { return GetStatus() == STATUS_CLOSED; }
77  void SetStatusOpen() { SetStatus(STATUS_OPEN); }
78  void SetStatusClosed() { SetStatus(STATUS_CLOSED); }
79 
80  // Get pi,pj coords of predecessor to this tile on best path, given i,j coords of this tile
81  inline int GetPredI(int i) const { return i + GetPredDI(); }
82  inline int GetPredJ(int j) const { return j + GetPredDJ(); }
83 
84  inline PathCost GetCost() const { return g; }
85  inline void SetCost(PathCost cost) { g = cost; }
86 
87 private:
88  PathCost g; // cost to reach this tile
89  u32 data; // 2-bit status; 15-bit PredI; 15-bit PredJ; packed for storage efficiency
90 
91 public:
92  inline u8 GetStatus() const
93  {
94  return data & 3;
95  }
96 
97  inline void SetStatus(u8 s)
98  {
99  ASSERT(s < 4);
100  data &= ~3;
101  data |= (s & 3);
102  }
103 
104  int GetPredDI() const
105  {
106  return (i32)data >> 17;
107  }
108 
109  int GetPredDJ() const
110  {
111  return ((i32)data << 15) >> 17;
112  }
113 
114  // Set the pi,pj coords of predecessor, given i,j coords of this tile
115  inline void SetPred(int pi, int pj, int i, int j)
116  {
117  int di = pi - i;
118  int dj = pj - j;
119  ASSERT(-16384 <= di && di < 16384);
120  ASSERT(-16384 <= dj && dj < 16384);
121  data &= 3;
122  data |= (((u32)di & 0x7FFF) << 17) | (((u32)dj & 0x7FFF) << 2);
123  }
124 };
125 
127 {
128  CircularRegion(entity_pos_t x, entity_pos_t z, entity_pos_t r) : x(x), z(z), r(r) {}
130 };
131 
134 
135 class JumpPointCache;
136 
138 {
139  u32 steps; // number of algorithm iterations
140 
142 
143  u16 iGoal, jGoal; // goal tile
144 
146 
148  // (there's no explicit closed list; it's encoded in PathfindTile)
149 
152 
153  PathCost hBest; // heuristic of closest discovered tile to goal
154  u16 iBest, jBest; // closest tile
155 
157 };
158 
159 class LongOverlay;
160 
162 {
163 public:
164  LongPathfinder();
165  ~LongPathfinder();
166 
167  void SetDebugOverlay(bool enabled);
168 
169  void SetHierDebugOverlay(bool enabled, const CSimContext *simContext)
170  {
171  m_PathfinderHier.SetDebugOverlay(enabled, simContext);
172  }
173 
174  void SetDebugPath(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass)
175  {
176  if (!m_DebugOverlay)
177  return;
178 
179  SAFE_DELETE(m_DebugGrid);
180  delete m_DebugPath;
181  m_DebugPath = new WaypointPath();
182  ComputePath(x0, z0, goal, passClass, *m_DebugPath);
183  m_DebugPassClass = passClass;
184  }
185 
186  void Reload(Grid<NavcellData>* passabilityGrid,
187  const std::map<std::string, pass_class_t>& nonPathfindingPassClassMasks,
188  const std::map<std::string, pass_class_t>& pathfindingPassClassMasks)
189  {
190  m_Grid = passabilityGrid;
191  ASSERT(passabilityGrid->m_H == passabilityGrid->m_W);
192  m_GridSize = passabilityGrid->m_W;
193 
194  m_JumpPointCache.clear();
195 
196  m_PathfinderHier.Recompute(passabilityGrid, nonPathfindingPassClassMasks, pathfindingPassClassMasks);
197  }
198 
199  void Update(Grid<NavcellData>* passabilityGrid, const Grid<u8>& dirtinessGrid)
200  {
201  m_Grid = passabilityGrid;
202  ASSERT(passabilityGrid->m_H == passabilityGrid->m_W);
203  ASSERT(m_GridSize == passabilityGrid->m_H);
204 
205  m_JumpPointCache.clear();
206 
207  m_PathfinderHier.Update(passabilityGrid, dirtinessGrid);
208  }
209 
211  {
212  for (size_t i = 0; i < m_PathfinderHier.m_DebugOverlayLines.size(); ++i)
213  collector.Submit(&m_PathfinderHier.m_DebugOverlayLines[i]);
214  }
215 
216  /**
217  * Compute a tile-based path from the given point to the goal, and return the set of waypoints.
218  * The waypoints correspond to the centers of horizontally/vertically adjacent tiles
219  * along the path.
220  */
221  void ComputePath(entity_pos_t x0, entity_pos_t z0, const PathGoal& origGoal,
222  pass_class_t passClass, WaypointPath& path)
223  {
224  if (!m_Grid)
225  {
226  LOGERROR("The pathfinder grid hasn't been setup yet, aborting ComputePath");
227  return;
228  }
229 
230  ComputeJPSPath(x0, z0, origGoal, passClass, path);
231  }
232 
233  /**
234  * Compute a tile-based path from the given point to the goal, excluding the regions
235  * specified in excludedRegions (which are treated as impassable) and return the set of waypoints.
236  * The waypoints correspond to the centers of horizontally/vertically adjacent tiles
237  * along the path.
238  */
239  void ComputePath(entity_pos_t x0, entity_pos_t z0, const PathGoal& origGoal,
240  pass_class_t passClass, std::vector<CircularRegion> excludedRegions, WaypointPath& path);
241 
243  {
244  return m_PathfinderHier.GetConnectivityGrid(passClass);
245  }
246 
247  void GetDebugData(u32& steps, double& time, Grid<u8>& grid) const
248  {
249  GetDebugDataJPS(steps, time, grid);
250  }
251 
254 
255  // Debugging - output from last pathfind operation:
259  double m_DebugTime;
263 
264 private:
265  PathCost CalculateHeuristic(int i, int j, int iGoal, int jGoal);
266  void ProcessNeighbour(int pi, int pj, int i, int j, PathCost pg, PathfinderState& state);
267 
268  /**
269  * JPS algorithm helper functions
270  * @param detectGoal is not used if m_UseJPSCache is true
271  */
272  void AddJumpedHoriz(int i, int j, int di, PathCost g, PathfinderState& state, bool detectGoal);
273  int HasJumpedHoriz(int i, int j, int di, PathfinderState& state, bool detectGoal);
274  void AddJumpedVert(int i, int j, int dj, PathCost g, PathfinderState& state, bool detectGoal);
275  int HasJumpedVert(int i, int j, int dj, PathfinderState& state, bool detectGoal);
276  void AddJumpedDiag(int i, int j, int di, int dj, PathCost g, PathfinderState& state);
277 
278  /**
279  * See LongPathfinder.cpp for implementation details
280  * TODO: cleanup documentation
281  */
282  void ComputeJPSPath(entity_pos_t x0, entity_pos_t z0, const PathGoal& origGoal, pass_class_t passClass, WaypointPath& path);
283  void GetDebugDataJPS(u32& steps, double& time, Grid<u8>& grid) const;
284 
285  // Helper functions for ComputePath
286 
287  /**
288  * Given a path with an arbitrary collection of waypoints, updates the
289  * waypoints to be nicer. Calls "Testline" between waypoints
290  * so that bended paths can become straight if there's nothing in between
291  * (this happens because A* is 8-direction, and the map isn't actually a grid).
292  * If @param maxDist is non-zero, path waypoints will be espaced by at most @param maxDist.
293  * In that case the distance between (x0, z0) and the first waypoint will also be made less than maxDist.
294  */
295  void ImprovePathWaypoints(WaypointPath& path, pass_class_t passClass, entity_pos_t maxDist, entity_pos_t x0, entity_pos_t z0);
296 
297  /**
298  * Generate a passability map, stored in the 16th bit of navcells, based on passClass,
299  * but with a set of impassable circular regions.
300  */
301  void GenerateSpecialMap(pass_class_t passClass, std::vector<CircularRegion> excludedRegions);
302 
304  std::map<pass_class_t, shared_ptr<JumpPointCache> > m_JumpPointCache;
305 
307 };
308 
309 /**
310  * Terrain overlay for pathfinder debugging.
311  * Renders a representation of the most recent pathfinding operation.
312  */
314 {
315 public:
317 
319  TerrainTextureOverlay(Pathfinding::NAVCELLS_PER_TILE), m_Pathfinder(pathfinder)
320  {
321  }
322 
323  virtual void BuildTextureRGBA(u8* data, size_t w, size_t h)
324  {
325  // Grab the debug data for the most recently generated path
326  u32 steps;
327  double time;
328  Grid<u8> debugGrid;
329  m_Pathfinder.GetDebugData(steps, time, debugGrid);
330 
331  // Render navcell passability
332  u8* p = data;
333  for (size_t j = 0; j < h; ++j)
334  {
335  for (size_t i = 0; i < w; ++i)
336  {
337  SColor4ub color(0, 0, 0, 0);
338  if (!IS_PASSABLE(m_Pathfinder.m_Grid->get((int)i, (int)j), m_Pathfinder.m_DebugPassClass))
339  color = SColor4ub(255, 0, 0, 127);
340 
341  if (debugGrid.m_W && debugGrid.m_H)
342  {
343  u8 n = debugGrid.get((int)i, (int)j);
344 
345  if (n == 1)
346  color = SColor4ub(255, 255, 0, 127);
347  else if (n == 2)
348  color = SColor4ub(0, 255, 0, 127);
349 
350  if (m_Pathfinder.m_DebugGoal.NavcellContainsGoal(i, j))
351  color = SColor4ub(0, 0, 255, 127);
352  }
353 
354  *p++ = color.R;
355  *p++ = color.G;
356  *p++ = color.B;
357  *p++ = color.A;
358  }
359  }
360 
361  // Render the most recently generated path
362  if (m_Pathfinder.m_DebugPath && !m_Pathfinder.m_DebugPath->m_Waypoints.empty())
363  {
364  std::vector<Waypoint>& waypoints = m_Pathfinder.m_DebugPath->m_Waypoints;
365  u16 ip = 0, jp = 0;
366  for (size_t k = 0; k < waypoints.size(); ++k)
367  {
368  u16 i, j;
369  Pathfinding::NearestNavcell(waypoints[k].x, waypoints[k].z, i, j, m_Pathfinder.m_GridSize, m_Pathfinder.m_GridSize);
370  if (k == 0)
371  {
372  ip = i;
373  jp = j;
374  }
375  else
376  {
377  bool firstCell = true;
378  do
379  {
380  if (data[(jp*w + ip)*4+3] == 0)
381  {
382  data[(jp*w + ip)*4+0] = 0xFF;
383  data[(jp*w + ip)*4+1] = 0xFF;
384  data[(jp*w + ip)*4+2] = 0xFF;
385  data[(jp*w + ip)*4+3] = firstCell ? 0xA0 : 0x60;
386  }
387  ip = ip < i ? ip+1 : ip > i ? ip-1 : ip;
388  jp = jp < j ? jp+1 : jp > j ? jp-1 : jp;
389  firstCell = false;
390  }
391  while (ip != i || jp != j);
392  }
393  }
394  }
395  }
396 };
397 
398 #endif // INCLUDED_LONGPATHFINDER
Represents the 2D coordinates of a tile.
Definition: LongPathfinder.h:37
A simple fixed-point number class.
Definition: Fixed.h:115
static enum @39 state
CircularRegion(entity_pos_t x, entity_pos_t z, entity_pos_t r)
Definition: LongPathfinder.h:128
u8 G
Definition: SColor.h:33
bool operator==(const TileID &b) const
Definition: LongPathfinder.h:43
#define LOGERROR(...)
Definition: CLogger.h:36
Similar to Grid, except optimised for sparse usage (the grid is subdivided into buckets whose content...
Definition: Grid.h:136
Grid< u16 > GetConnectivityGrid(pass_class_t passClass)
Definition: LongPathfinder.h:242
void SetDebugPath(entity_pos_t x0, entity_pos_t z0, const PathGoal &goal, pass_class_t passClass)
Definition: LongPathfinder.h:174
u16 pass_class_t
Definition: Pathfinding.h:29
bool m_UseJPSCache
Definition: LongPathfinder.h:303
Definition: Pathfinding.h:105
void GetDebugData(u32 &steps, double &time, Grid< u8 > &grid) const
Definition: LongPathfinder.h:247
Returned path.
Definition: Pathfinding.h:40
u32 data
Definition: LongPathfinder.h:89
u32 m_DebugSteps
Definition: LongPathfinder.h:258
uint16_t u16
Definition: types.h:38
pass_class_t m_DebugPassClass
Definition: LongPathfinder.h:262
u32 data
Definition: LongPathfinder.h:58
int GetPredDJ() const
Definition: LongPathfinder.h:109
u16 m_H
Definition: Grid.h:125
#define IS_PASSABLE(item, classmask)
Definition: Pathfinding.h:101
bool IsClosed() const
Definition: LongPathfinder.h:76
std::vector< Waypoint > m_Waypoints
Definition: Pathfinding.h:42
u16 jBest
Definition: LongPathfinder.h:154
void Update(Grid< NavcellData > *passabilityGrid, const Grid< u8 > &dirtinessGrid)
Definition: LongPathfinder.h:199
void ComputePath(entity_pos_t x0, entity_pos_t z0, const PathGoal &origGoal, pass_class_t passClass, WaypointPath &path)
Compute a tile-based path from the given point to the goal, and return the set of waypoints...
Definition: LongPathfinder.h:221
PathCost hBest
Definition: LongPathfinder.h:153
Pathfinder goal.
Definition: PathGoal.h:32
#define ASSERT(expr)
same as ENSURE in debug mode, does nothing in release mode.
Definition: debug.h:315
Contains pointers to various &#39;global&#39; objects that are needed by the simulation code, to allow easy access without using real (evil) global variables.
Definition: SimContext.h:32
u8 B
Definition: SColor.h:34
bool NavcellContainsGoal(int i, int j) const
Returns true if the given navcell contains a part of the goal area.
Definition: PathGoal.cpp:95
uint8_t u8
Definition: types.h:37
void HierarchicalRenderSubmit(SceneCollector &collector)
Definition: LongPathfinder.h:210
u32 steps
Definition: LongPathfinder.h:139
This interface accepts renderable objects.
Definition: Scene.h:83
u8 A
Definition: SColor.h:35
Jump point cache.
Definition: LongPathfinder.cpp:56
Definition: LongPathfinder.h:161
PathGoal m_DebugGoal
Definition: LongPathfinder.h:260
LongOverlay * m_DebugOverlay
Definition: LongPathfinder.h:256
int GetPredI(int i) const
Definition: LongPathfinder.h:81
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
Definition: HierarchicalPathfinder.h:47
void SetStatusOpen()
Definition: LongPathfinder.h:77
double m_DebugTime
Definition: LongPathfinder.h:259
PathCost g
Definition: LongPathfinder.h:88
void SetHierDebugOverlay(bool enabled, const CSimContext *simContext)
Definition: LongPathfinder.h:169
int GetPredDI() const
Definition: LongPathfinder.h:104
PathCost GetCost() const
Definition: LongPathfinder.h:84
Definition: LongPathfinder.h:137
virtual void BuildTextureRGBA(u8 *data, size_t w, size_t h)
Called each frame to generate the texture to render on the terrain.
Definition: LongPathfinder.h:323
TileID()
Definition: LongPathfinder.h:39
Definition: SColor.h:30
void SetStatus(u8 s)
Definition: LongPathfinder.h:97
int GetPredJ(int j) const
Definition: LongPathfinder.h:82
WaypointPath * m_DebugPath
Definition: LongPathfinder.h:261
u8 GetStatus() const
Definition: LongPathfinder.h:92
TileID(u16 i, u16 j)
Definition: LongPathfinder.h:41
#define SAFE_DELETE(p)
delete memory ensuing from new and set the pointer to zero (thus making double-frees safe / a no-op) ...
Definition: code_generation.h:111
pass_class_t passClass
Definition: LongPathfinder.h:145
Tile data for A* computation.
Definition: LongPathfinder.h:65
u8 R
Definition: SColor.h:32
u16 i() const
Definition: LongPathfinder.h:54
bool operator<(const TileID &b) const
Returns lexicographic order over (i,j)
Definition: LongPathfinder.h:49
Terrain overlay for pathfinder debugging.
Definition: LongPathfinder.h:313
int32_t i32
Definition: types.h:34
u16 m_GridSize
Definition: LongPathfinder.h:253
SparseGrid< PathfindTile > PathfindTileGrid
Definition: LongPathfinder.h:133
PathfindTileGrid * tiles
Definition: LongPathfinder.h:150
JumpPointCache * jpc
Definition: LongPathfinder.h:156
PathGoal goal
Definition: LongPathfinder.h:141
void Reload(Grid< NavcellData > *passabilityGrid, const std::map< std::string, pass_class_t > &nonPathfindingPassClassMasks, const std::map< std::string, pass_class_t > &pathfindingPassClassMasks)
Definition: LongPathfinder.h:186
void SetStatusClosed()
Definition: LongPathfinder.h:78
HierarchicalPathfinder m_PathfinderHier
Definition: LongPathfinder.h:306
Grid< NavcellData > * m_Grid
Definition: LongPathfinder.h:252
T & get(int i, int j) const
Definition: Grid.h:117
Base class for texture-based terrain overlays, with an arbitrary number of texels per terrain tile...
Definition: TerrainOverlay.h:174
bool IsUnexplored() const
Definition: LongPathfinder.h:74
Definition: LongPathfinder.h:126
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
PriorityQueue open
Definition: LongPathfinder.h:147
std::map< pass_class_t, shared_ptr< JumpPointCache > > m_JumpPointCache
Definition: LongPathfinder.h:304
u16 m_W
Definition: Grid.h:125
bool IsOpen() const
Definition: LongPathfinder.h:75
void SetCost(PathCost cost)
Definition: LongPathfinder.h:85
LongPathfinder & m_Pathfinder
Definition: LongPathfinder.h:316
Represents the cost of a path consisting of horizontal/vertical and diagonal movements over a uniform...
Definition: Pathfinding.h:50
PathfindTileGrid * m_DebugGrid
Definition: LongPathfinder.h:257
u16 j() const
Definition: LongPathfinder.h:55
virtual void Submit(CPatch *patch)=0
Submit a terrain patch that is part of the scene.
LongOverlay(LongPathfinder &pathfinder)
Definition: LongPathfinder.h:318
Grid< NavcellData > * terrain
Definition: LongPathfinder.h:151
u16 jGoal
Definition: LongPathfinder.h:143
void SetPred(int pi, int pj, int i, int j)
Definition: LongPathfinder.h:115
entity_pos_t z
Definition: LongPathfinder.h:129
PriorityQueueHeap< TileID, PathCost, PathCost > PriorityQueue
Definition: LongPathfinder.h:132