1 #include "RoutingLumpLoader.h"
2 #include "RoutingLump.h"
3 #include "../../../shared/autoptr.h"
4 #include "ifilesystem.h"
5 #include "gtkutil/dialog.h"
6 #include "radiant_i18n.h"
7 
8 #include "../../../shared/ufotypes.h"
9 #include "../../../shared/typedefs.h"
10 #include "../../../game/q_sizes.h"
11 #include "../../../common/qfiles.h"
12 #define COMPILE_MAP
13 typedef int mapTiles_t;
14 #include "../../../common/routing.h"
15 
16 namespace routing
17 {
18 
19 	/**
20 	 * @param[in] source Source will be set to the end of the compressed data block!
21 	 * @sa CompressRouting (ufo2map)
22 	 * @sa CMod_LoadRouting
23 	 */
CMod_DeCompressRouting(byte ** source,byte * dataStart)24 	static int CMod_DeCompressRouting (byte**  source, byte*  dataStart)
25 	{
26 		int i, c;
27 		byte* data_p;
28 		byte* src;
29 
30 		data_p = dataStart;
31 		src = *source;
32 
33 		while (*src) {
34 			if (*src & 0x80) {
35 				/* repetitions */
36 				c = *src++ & ~0x80;
37 				/* Remember that the total bytes that are the same is c + 2 */
38 				for (i = 0; i < c + 2; i++)
39 					*data_p++ = *src;
40 				src++;
41 			} else {
42 				/* identities */
43 				c = *src++;
44 				for (i = 0; i < c; i++)
45 					*data_p++ = *src++;
46 			}
47 		}
48 
49 		src++;
50 		*source = src;
51 
52 		return data_p - dataStart;
53 	}
54 
55 	/**
56 	 * @brief evaluate access state for given position for given routing data
57 	 * @param routing The routing tables
58 	 * @param pos position to evaluate
59 	 * @return access state as enum value for later rendering
60 	 */
evaluateAccessState(const Routing & routing,const pos3_t pos,const int actorSize)61 	static EAccessState evaluateAccessState (const Routing &routing, const pos3_t pos, const int actorSize)
62 	{
63 		const int height = QuantToModel(routing.getCeiling(actorSize, pos[0], pos[1], pos[2] & (PATHFINDING_HEIGHT - 1))
64 				- routing.getFloor(actorSize, pos[0], pos[1], pos[2] & (PATHFINDING_HEIGHT - 1)));
65 		if (height >= PLAYER_STANDING_HEIGHT)
66 			return ACC_STAND;
67 		else if (height >= PLAYER_CROUCHING_HEIGHT)
68 			return ACC_CROUCH;
69 		else
70 			return ACC_DISABLED;
71 	}
72 
evaluateConnectionState(const Routing & routing,const pos3_t pos,const int actorSize,const EDirection direction)73 	static EConnectionState evaluateConnectionState (const Routing &routing, const pos3_t pos,
74 			const int actorSize, const EDirection direction)
75 	{
76 		byte route = 0;
77 		byte stepup = 0;
78 
79 		switch (direction) {
80 		case DIR_WEST:
81 			route = RT_CONN_NY(routing,actorSize,pos[0],pos[1],pos[2]);
82 			stepup = RT_STEPUP_NY(routing,actorSize,pos[0],pos[1],pos[2]);
83 			break;
84 		case DIR_NORTHWEST:
85 			route = RT_CONN_PX_NY(routing,actorSize,pos[0],pos[1],pos[2]);
86 			stepup = RT_STEPUP_PX_NY(routing,actorSize,pos[0],pos[1],pos[2]);
87 			break;
88 		case DIR_NORTH:
89 			route = RT_CONN_PX(routing,actorSize,pos[0],pos[1],pos[2]);
90 			stepup = RT_STEPUP_PX(routing,actorSize,pos[0],pos[1],pos[2]);
91 			break;
92 		case DIR_NORTHEAST:
93 			route = RT_CONN_PX_PY(routing,actorSize,pos[0],pos[1],pos[2]);
94 			stepup = RT_STEPUP_PX_PY(routing,actorSize,pos[0],pos[1],pos[2]);
95 			break;
96 		case DIR_EAST:
97 			route = RT_CONN_PY(routing,actorSize,pos[0],pos[1],pos[2]);
98 			stepup = RT_STEPUP_PY(routing,actorSize,pos[0],pos[1],pos[2]);
99 			break;
100 		case DIR_SOUTHEAST:
101 			route = RT_CONN_NX_PY(routing,actorSize,pos[0],pos[1],pos[2]);
102 			stepup = RT_STEPUP_NX_PY(routing,actorSize,pos[0],pos[1],pos[2]);
103 			break;
104 		case DIR_SOUTH:
105 			route = RT_CONN_NX(routing,actorSize,pos[0],pos[1],pos[2]);
106 			stepup = RT_STEPUP_NX(routing,actorSize,pos[0],pos[1],pos[2]);
107 			break;
108 		case DIR_SOUTHWEST:
109 			route = RT_CONN_NX_NY(routing,actorSize,pos[0],pos[1],pos[2]);
110 			stepup = RT_STEPUP_NX_NY(routing,actorSize,pos[0],pos[1],pos[2]);
111 			break;
112 		case MAX_DIRECTIONS:
113 			break;
114 		}
115 
116 		if (stepup == PATHFINDING_NO_STEPUP)
117 			return CON_DISABLE;
118 		else if (route >= ModelCeilingToQuant(PLAYER_STANDING_HEIGHT))
119 			return CON_WALKABLE;
120 		else if (route >= ModelCeilingToQuant(PLAYER_CROUCHING_HEIGHT))
121 			return CON_CROUCHABLE;
122 		else
123 			return CON_DISABLE;
124 	}
125 
126 	/**
127 	 * @brief evaluate map data to set access and connectivity state to given lump entry.
128 	 * @param entry entry to fill values into
129 	 * @param routing The routing tables
130 	 * @param pos position to evaluate
131 	 */
FillRoutingLumpEntry(RoutingLumpEntry & entry,Routing & routing,pos3_t pos)132 	static void FillRoutingLumpEntry (RoutingLumpEntry &entry, Routing &routing, pos3_t pos)
133 	{
134 		entry.setAccessState(evaluateAccessState(routing, pos, ACTOR_SIZE_NORMAL));
135 		for (EDirection direction = DIR_WEST; direction < MAX_DIRECTIONS; direction++) {
136 			entry.setConnectionState(direction, evaluateConnectionState(routing, pos, ACTOR_SIZE_NORMAL, direction));
137 		}
138 	}
139 
140 	/**
141 	 * @param[in] l Routing lump ... (routing data lump from bsp file)
142 	 * @param[in] sX The x position on the world plane (grid position) - values from -(PATHFINDING_WIDTH/2) up to PATHFINDING_WIDTH/2 are allowed
143 	 * @param[in] sY The y position on the world plane (grid position) - values from -(PATHFINDING_WIDTH/2) up to PATHFINDING_WIDTH/2 are allowed
144 	 * @param[in] sZ The height level on the world plane (grid position) - values from 0 - PATHFINDING_HEIGHT are allowed
145 	 * @sa CM_AddMapTile
146 	 * @todo TEST z-level routing
147 	 */
CMod_LoadRouting(RoutingLump & routingLump,const std::string & name,const lump_t * l,byte * cModelBase,int sX,int sY,int sZ)148 	static void CMod_LoadRouting (RoutingLump& routingLump, const std::string& name, const lump_t* l,
149 			byte* cModelBase, int sX, int sY, int sZ)
150 	{
151 		static Routing tempMap;
152 		static Routing clMap;
153 		static MapTile curTile;
154 		byte* source;
155 		int length;
156 		int x, y, z, size;
157 		int minX, minY, minZ;
158 		int maxX, maxY, maxZ;
159 		unsigned int i;
160 		double start, end;
161 		const int targetLength = sizeof(curTile.wpMins) + sizeof(curTile.wpMaxs) + sizeof(tempMap);
162 
163 		start = time(NULL);
164 
165 		if (!GUINT32_TO_LE(l->filelen)) {
166 			g_error("CMod_LoadRouting: Map has NO routing lump");
167 			return;
168 		}
169 
170 		source = cModelBase + GUINT32_TO_LE(l->fileofs);
171 
172 		i = CMod_DeCompressRouting(&source, (byte*) curTile.wpMins);
173 		length = i;
174 		i = CMod_DeCompressRouting(&source, (byte*) curTile.wpMaxs);
175 		length += i;
176 		i = CMod_DeCompressRouting(&source, (byte*) &tempMap);
177 		length += i;
178 
179 		if (length != targetLength) {
180 			g_error("CMod_LoadRouting: Map has BAD routing lump; expected %i got %i", targetLength, length);
181 			return;
182 		}
183 
184 		/* endian swap possibly necessary */
185 		for (i = 0; i < 3; i++) {
186 			curTile.wpMins[i] = GUINT32_TO_LE(curTile.wpMins[i]);
187 			curTile.wpMaxs[i] = GUINT32_TO_LE(curTile.wpMaxs[i]);
188 		}
189 
190 		g_debug("wpMins:(%i, %i, %i) wpMaxs:(%i, %i, %i)\n", curTile.wpMins[0], curTile.wpMins[1], curTile.wpMins[2],
191 				curTile.wpMaxs[0], curTile.wpMaxs[1], curTile.wpMaxs[2]);
192 
193 		/* Things that need to be done:
194 		 * The floor, ceiling, and route data can be copied over from the map.
195 		 * All data must be regenerated for cells with overlapping content or where new
196 		 * model data is adjacent to a cell with existing model data. */
197 
198 		/* Copy the routing information into our master table */
199 		minX = std::max(curTile.wpMins[0], 0);
200 		minY = std::max(curTile.wpMins[1], 0);
201 		minZ = std::max(curTile.wpMins[2], 0);
202 		maxX = std::min(curTile.wpMaxs[0], PATHFINDING_WIDTH - 1);
203 		maxY = std::min(curTile.wpMaxs[1], PATHFINDING_WIDTH - 1);
204 		maxZ = std::min(curTile.wpMaxs[2], PATHFINDING_HEIGHT - 1);
205 
206 		/** @todo check whether we need this copy code */
207 		for (size = 0; size < ACTOR_MAX_SIZE; size++)
208 			/* Adjust starting x and y by size to catch large actor cell overlap. */
209 			for (y = minY - size; y <= maxY; y++)
210 				for (x = minX - size; x <= maxX; x++) {
211 					/* Just incase x or y start negative. */
212 					if (x < 0 || y < 0)
213 						continue;
214 					for (z = minZ; z <= maxZ; z++) {
215 						clMap.copyPosData(tempMap, size + 1, x, y, z, sX, sY, sZ);
216 					}
217 					/* Update the reroute table */
218 					/*if (!reroute[size][y][x]) {
219 					 reroute[size][y][x] = numTiles + 1;
220 					 } else {
221 					 reroute[size][y][x] = ROUTING_NOT_REACHABLE;
222 					 }*/
223 				}
224 
225 		g_message("Done copying data.\n");
226 		for (size = 0; size < 1; size++)
227 			for (x = minX; x <= maxX; x++)
228 				for (y = minY; y <= maxY; y++)
229 					for (z = minZ; z <= maxZ; z++) {
230 						pos3_t pos = { static_cast<pos_t>(x), static_cast<pos_t>(y), static_cast<pos_t>(z) };
231 						vec3_t vect;
232 						PosToVec(pos,vect);
233 						/**@todo add other data to constructor: accessibility + connection states */
234 						RoutingLumpEntry entry = RoutingLumpEntry(Vector3(vect), (z + 1));
235 						FillRoutingLumpEntry(entry, clMap, pos);
236 						/**@todo perhaps there is a better way than creating a const object for adding */
237 						const RoutingLumpEntry toAdd = RoutingLumpEntry(entry);
238 						routingLump.add(toAdd);
239 					}
240 		end = time(NULL);
241 		g_message("Loaded routing for tile %s in %5.1fs\n", name.c_str(), end - start);
242 	}
243 
loadRoutingLump(ArchiveFile & file)244 	void RoutingLumpLoader::loadRoutingLump (ArchiveFile& file)
245 	{
246 		/* load the file */
247 		InputStream &stream = file.getInputStream();
248 		const std::size_t size = file.size();
249 		byte* buf = (byte*) malloc(size + 1);
250 		dBspHeader_t* header = (dBspHeader_t*) buf;
251 		stream.read(buf, size);
252 
253 		CMod_LoadRouting(_routingLump, file.getName(), &header->lumps[LUMP_ROUTING], (byte*) buf, 0, 0, 0);
254 		free(buf);
255 	}
256 
loadRouting(const std::string & bspFileName)257 	void RoutingLumpLoader::loadRouting (const std::string& bspFileName)
258 	{
259 		/**@todo try to reduce loading, store latest file + mtime and load only if that changed */
260 		// Open an ArchiveFile to load
261 		AutoPtr<ArchiveFile> file(GlobalFileSystem().openFile(bspFileName));
262 		if (file) {
263 			// Load the model and return the RenderablePtr
264 			_routingLump = RoutingLump();
265 			loadRoutingLump(*file);
266 		} else {
267 			gtkutil::errorDialog(_("No compiled version of the map found"));
268 		}
269 	}
270 
RoutingLumpLoader()271 	RoutingLumpLoader::RoutingLumpLoader ()
272 	{
273 	}
274 
getRoutingLump()275 	routing::RoutingLump& RoutingLumpLoader::getRoutingLump ()
276 	{
277 		return _routingLump;
278 	}
279 
~RoutingLumpLoader()280 	RoutingLumpLoader::~RoutingLumpLoader ()
281 	{
282 	}
283 }
284