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