File: HierarchicalPathfinder.h

package info (click to toggle)
0ad 0.0.21-2~bpo8%2B1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-backports
  • size: 54,068 kB
  • sloc: cpp: 230,527; ansic: 23,115; python: 13,559; perl: 2,499; sh: 948; xml: 776; makefile: 696; java: 533; ruby: 229; erlang: 53; sql: 21
file content (226 lines) | stat: -rw-r--r-- 7,079 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/* Copyright (C) 2015 Wildfire Games.
 * This file is part of 0 A.D.
 *
 * 0 A.D. is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * 0 A.D. is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef INCLUDED_HIERPATHFINDER
#define INCLUDED_HIERPATHFINDER

#include "Pathfinding.h"

#include "renderer/TerrainOverlay.h"
#include "Render.h"
#include "graphics/SColor.h"

/**
 * Hierarchical pathfinder.
 *
 * It doesn't find shortest paths, but deals with connectivity.
 *
 * The navcell-grid representation of the map is split into fixed-size chunks.
 * Within a chunk, each maximal set of adjacently-connected passable navcells
 * is defined as a region.
 * Each region is a vertex in the hierarchical pathfinder's graph.
 * When two regions in adjacent chunks are connected by passable navcells,
 * the graph contains an edge between the corresponding two vertexes.
 * (There will never be an edge between two regions in the same chunk.)
 *
 * Since regions are typically fairly large, it is possible to determine
 * connectivity between any two navcells by mapping them onto their appropriate
 * region and then doing a relatively small graph search.
 */

class HierarchicalOverlay;

class HierarchicalPathfinder
{
public:
	struct RegionID
	{
		u8 ci, cj; // chunk ID
		u16 r; // unique-per-chunk local region ID

		RegionID(u8 ci, u8 cj, u16 r) : ci(ci), cj(cj), r(r) { }

		bool operator<(RegionID b) const
		{
			// Sort by chunk ID, then by per-chunk region ID
			if (ci < b.ci)
				return true;
			if (b.ci < ci)
				return false;
			if (cj < b.cj)
				return true;
			if (b.cj < cj)
				return false;
			return r < b.r;
		}

		bool operator==(RegionID b) const
		{
			return ((ci == b.ci) && (cj == b.cj) && (r == b.r));
		}
	};

	HierarchicalPathfinder();
	~HierarchicalPathfinder();

	void SetDebugOverlay(bool enabled, const CSimContext* simContext);

	// Non-pathfinding grids will never be recomputed on calling HierarchicalPathfinder::Update
	void Recompute(Grid<NavcellData>* passabilityGrid,
		const std::map<std::string, pass_class_t>& nonPathfindingPassClassMasks,
		const std::map<std::string, pass_class_t>& pathfindingPassClassMasks);

	void Update(Grid<NavcellData>* grid, const Grid<u8>& dirtinessGrid);

	bool IsChunkDirty(int ci, int cj, const Grid<u8>& dirtinessGrid) const;

	RegionID Get(u16 i, u16 j, pass_class_t passClass);

	/**
	 * Updates @p goal so that it's guaranteed to be reachable from the navcell
	 * @p i0, @p j0 (which is assumed to be on a passable navcell).
	 *
	 * If the goal is not reachable, it is replaced with a point goal nearest to
	 * the goal center.
	 *
	 * In the case of a non-point reachable goal, it is replaced with a point goal
	 * at the reachable navcell of the goal which is nearest to the starting navcell.
	 */
	void MakeGoalReachable(u16 i0, u16 j0, PathGoal& goal, pass_class_t passClass);

	/**
	 * Updates @p i, @p j (which is assumed to be an impassable navcell)
	 * to the nearest passable navcell.
	 */
	void FindNearestPassableNavcell(u16& i, u16& j, pass_class_t passClass);

	/**
	 * Generates the connectivity grid associated with the given pass_class
	 */
	Grid<u16> GetConnectivityGrid(pass_class_t passClass);

	pass_class_t GetPassabilityClass(const std::string& name) const
	{
		auto it = m_PassClassMasks.find(name);
		if (it != m_PassClassMasks.end())
			return it->second;

		LOGERROR("Invalid passability class name '%s'", name.c_str());
		return 0;
	}

private:
	static const u8 CHUNK_SIZE = 96; // number of navcells per side
									 // TODO PATHFINDER: figure out best number. Probably 64 < n < 128

	struct Chunk
	{
		u8 m_ChunkI, m_ChunkJ; // chunk ID
		u16 m_NumRegions; // number of local region IDs (starting from 1)
		u16 m_Regions[CHUNK_SIZE][CHUNK_SIZE]; // local region ID per navcell

		cassert(CHUNK_SIZE*CHUNK_SIZE/2 < 65536); // otherwise we could overflow m_NumRegions with a checkerboard pattern

		void InitRegions(int ci, int cj, Grid<NavcellData>* grid, pass_class_t passClass);

		RegionID Get(int i, int j) const;

		void RegionCenter(u16 r, int& i, int& j) const;

		void RegionNavcellNearest(u16 r, int iGoal, int jGoal, int& iBest, int& jBest, u32& dist2Best) const;

		bool RegionNearestNavcellInGoal(u16 r, u16 i0, u16 j0, const PathGoal& goal, u16& iOut, u16& jOut, u32& dist2Best) const;
	};

	typedef std::map<RegionID, std::set<RegionID> > EdgesMap;

	void FindEdges(u8 ci, u8 cj, pass_class_t passClass, EdgesMap& edges);

	void FindReachableRegions(RegionID from, std::set<RegionID>& reachable, pass_class_t passClass);

	void FindPassableRegions(std::set<RegionID>& regions, pass_class_t passClass);

	/**
	 * Updates @p iGoal and @p jGoal to the navcell that is the nearest to the
	 * initial goal coordinates, in one of the given @p regions.
	 * (Assumes @p regions is non-empty.)
	 */
	void FindNearestNavcellInRegions(const std::set<RegionID>& regions, u16& iGoal, u16& jGoal, pass_class_t passClass);

	Chunk& GetChunk(u8 ci, u8 cj, pass_class_t passClass)
	{
		return m_Chunks[passClass].at(cj * m_ChunksW + ci);
	}

	void FillRegionOnGrid(const RegionID& region, pass_class_t passClass, u16 value, Grid<u16>& grid);

	u16 m_W, m_H;
	u16 m_ChunksW, m_ChunksH;
	std::map<pass_class_t, std::vector<Chunk> > m_Chunks;

	std::map<pass_class_t, EdgesMap> m_Edges;

	// Passability classes for which grids will be updated when calling Update
	std::map<std::string, pass_class_t> m_PassClassMasks;

	void AddDebugEdges(pass_class_t passClass);
	HierarchicalOverlay* m_DebugOverlay;
	const CSimContext* m_SimContext; // Used for drawing the debug lines

public:
	std::vector<SOverlayLine> m_DebugOverlayLines;
};

class HierarchicalOverlay : public TerrainTextureOverlay
{
public:
	HierarchicalPathfinder& m_PathfinderHier;

	HierarchicalOverlay(HierarchicalPathfinder& pathfinderHier) :
		TerrainTextureOverlay(Pathfinding::NAVCELLS_PER_TILE), m_PathfinderHier(pathfinderHier)
	{
	}

	virtual void BuildTextureRGBA(u8* data, size_t w, size_t h)
	{
		pass_class_t passClass = m_PathfinderHier.GetPassabilityClass("default");

		for (size_t j = 0; j < h; ++j)
		{
			for (size_t i = 0; i < w; ++i)
			{
				SColor4ub color;

				HierarchicalPathfinder::RegionID rid = m_PathfinderHier.Get(i, j, passClass);
				if (rid.r == 0)
					color = SColor4ub(0, 0, 0, 0);
				else if (rid.r == 0xFFFF)
					color = SColor4ub(255, 0, 255, 255);
				else
					color = GetColor(rid.r + rid.ci*5 + rid.cj*7, 127);

				*data++ = color.R;
				*data++ = color.G;
				*data++ = color.B;
				*data++ = color.A;
			}
		}
	}
};


#endif // INCLUDED_HIERPATHFINDER