1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3
4 #include <cstdlib>
5
6 #include "ReadMap.h"
7 #include "MapDamage.h"
8 #include "MapInfo.h"
9 #include "MetalMap.h"
10 // #include "SM3/SM3Map.h"
11 #include "SMF/SMFReadMap.h"
12 #include "Game/LoadScreen.h"
13 #include "System/bitops.h"
14 #include "System/EventHandler.h"
15 #include "System/Exceptions.h"
16 #include "System/myMath.h"
17 #include "System/TimeProfiler.h"
18 #include "System/ThreadPool.h"
19 #include "System/FileSystem/ArchiveScanner.h"
20 #include "System/FileSystem/FileHandler.h"
21 #include "System/FileSystem/FileSystem.h"
22 #include "System/Misc/RectangleOptimizer.h"
23
24 #ifdef USE_UNSYNCED_HEIGHTMAP
25 #include "Game/GlobalUnsynced.h"
26 #include "Sim/Misc/LosHandler.h"
27 #endif
28
29 //////////////////////////////////////////////////////////////////////
30 // Construction/Destruction
31 //////////////////////////////////////////////////////////////////////
32
33 // assigned to in CGame::CGame ("readMap = CReadMap::LoadMap(mapname)")
34 CReadMap* readMap = NULL;
35
36 #ifdef USE_UNSYNCED_HEIGHTMAP
37 #define HEIGHTMAP_DIGESTS \
38 CR_MEMBER(syncedHeightMapDigests), \
39 CR_MEMBER(unsyncedHeightMapDigests),
40 #else
41 #define HEIGHTMAP_DIGESTS
42 #endif
43
44 CR_BIND_INTERFACE(CReadMap)
45 CR_REG_METADATA(CReadMap, (
46 CR_MEMBER(metalMap),
47 CR_MEMBER(initMinHeight),
48 CR_MEMBER(initMaxHeight),
49 CR_IGNORED(currMinHeight),
50 CR_IGNORED(currMaxHeight),
51 CR_MEMBER(mapChecksum),
52 //CR_MEMBER(heightMapSyncedPtr),
53 //CR_MEMBER(heightMapUnsyncedPtr),
54 CR_MEMBER(originalHeightMap),
55 CR_IGNORED(centerHeightMap),
56 CR_IGNORED(mipCenterHeightMaps),
57 //CR_MEMBER(mipPointerHeightMaps),
58 CR_IGNORED(visVertexNormals),
59 CR_IGNORED(faceNormalsSynced),
60 CR_IGNORED(faceNormalsUnsynced),
61 CR_IGNORED(centerNormalsSynced),
62 CR_IGNORED(centerNormalsUnsynced),
63 CR_IGNORED(slopeMap),
64 CR_MEMBER(typeMap),
65 CR_MEMBER(unsyncedHeightMapUpdates),
66 CR_MEMBER(unsyncedHeightMapUpdatesTemp),
67 HEIGHTMAP_DIGESTS
68 CR_SERIALIZER(Serialize)
69 ))
70
71
LoadMap(const std::string & mapname)72 CReadMap* CReadMap::LoadMap(const std::string& mapname)
73 {
74 CReadMap* rm = NULL;
75
76 if (FileSystem::GetExtension(mapname) == "sm3") {
77 throw content_error("[CReadMap::LoadMap] SM3 maps are no longer supported as of Spring 95.0");
78 // rm = new CSM3ReadMap(mapname);
79 } else {
80 // assume SMF format by default
81 rm = new CSMFReadMap(mapname);
82 }
83
84 if (rm == NULL)
85 return NULL;
86
87 /* read metal- and type-map */
88 MapBitmapInfo mbi;
89 MapBitmapInfo tbi;
90
91 unsigned char* metalmapPtr = rm->GetInfoMap("metal", &mbi);
92 unsigned char* typemapPtr = rm->GetInfoMap("type", &tbi);
93
94 assert(mbi.width == (gs->mapx >> 1));
95 assert(mbi.height == (gs->mapy >> 1));
96
97 rm->metalMap = new CMetalMap(metalmapPtr, mbi.width, mbi.height, mapInfo->map.maxMetal);
98
99 if (metalmapPtr != NULL)
100 rm->FreeInfoMap("metal", metalmapPtr);
101
102 if (typemapPtr && tbi.width == (gs->mapx >> 1) && tbi.height == (gs->mapy >> 1)) {
103 assert(gs->hmapx == tbi.width && gs->hmapy == tbi.height);
104 rm->typeMap.resize(tbi.width * tbi.height);
105 memcpy(&rm->typeMap[0], typemapPtr, tbi.width * tbi.height);
106 } else
107 throw content_error("[CReadMap::LoadMap] bad/no terrain typemap");
108
109 if (typemapPtr != NULL)
110 rm->FreeInfoMap("type", typemapPtr);
111
112 return rm;
113 }
114
115
Serialize(creg::ISerializer & s)116 void CReadMap::Serialize(creg::ISerializer& s)
117 {
118 // remove the const
119 const float* cshm = GetCornerHeightMapSynced();
120 float* shm = const_cast<float*>(cshm);
121
122 s.Serialize(shm, 4 * gs->mapxp1 * gs->mapyp1);
123
124 if (!s.IsWriting())
125 mapDamage->RecalcArea(2, gs->mapx - 3, 2, gs->mapy - 3);
126 }
127
128
CReadMap()129 CReadMap::CReadMap()
130 : metalMap(NULL)
131 , heightMapSyncedPtr(NULL)
132 , heightMapUnsyncedPtr(NULL)
133 , mapChecksum(0)
134 , initMinHeight(0.0f)
135 , initMaxHeight(0.0f)
136 , currMinHeight(0.0f)
137 , currMaxHeight(0.0f)
138 {
139 }
140
141
~CReadMap()142 CReadMap::~CReadMap()
143 {
144 delete metalMap;
145 }
146
147
Initialize()148 void CReadMap::Initialize()
149 {
150 // set global map info (TODO: move these to ReadMap!)
151 gs->mapxm1 = gs->mapx - 1;
152 gs->mapxp1 = gs->mapx + 1;
153 gs->mapym1 = gs->mapy - 1;
154 gs->mapyp1 = gs->mapy + 1;
155 gs->mapSquares = gs->mapx * gs->mapy;
156 gs->hmapx = gs->mapx >> 1;
157 gs->hmapy = gs->mapy >> 1;
158 gs->pwr2mapx = next_power_of_2(gs->mapx);
159 gs->pwr2mapy = next_power_of_2(gs->mapy);
160
161 {
162 char loadMsg[512];
163 const char* fmtString = "Loading Map (%u MB)";
164 unsigned int reqMemFootPrintKB =
165 ((( gs->mapxp1) * gs->mapyp1 * 2 * sizeof(float)) / 1024) + // cornerHeightMap{Synced, Unsynced}
166 ((( gs->mapxp1) * gs->mapyp1 * sizeof(float)) / 1024) + // originalHeightMap
167 (( gs->mapx * gs->mapy * 2 * 2 * sizeof(float3)) / 1024) + // faceNormals{Synced, Unsynced}
168 (( gs->mapx * gs->mapy * 2 * sizeof(float3)) / 1024) + // centerNormals{Synced, Unsynced}
169 ((( gs->mapxp1) * gs->mapyp1 * sizeof(float3)) / 1024) + // VisVertexNormals
170 (( gs->mapx * gs->mapy * sizeof(float)) / 1024) + // centerHeightMap
171 (( gs->hmapx * gs->hmapy * sizeof(float)) / 1024) + // slopeMap
172 (( gs->hmapx * gs->hmapy * sizeof(float)) / 1024) + // MetalMap::extractionMap
173 (( gs->hmapx * gs->hmapy * sizeof(unsigned char)) / 1024); // MetalMap::metalMap
174
175 // mipCenterHeightMaps[i]
176 for (int i = 1; i < numHeightMipMaps; i++) {
177 reqMemFootPrintKB += ((((gs->mapx >> i) * (gs->mapy >> i)) * sizeof(float)) / 1024);
178 }
179
180 sprintf(loadMsg, fmtString, reqMemFootPrintKB / 1024);
181 loadscreen->SetLoadMessage(loadMsg);
182 }
183
184 float3::maxxpos = gs->mapx * SQUARE_SIZE - 1;
185 float3::maxzpos = gs->mapy * SQUARE_SIZE - 1;
186
187 originalHeightMap.resize(gs->mapxp1 * gs->mapyp1);
188 faceNormalsSynced.resize(gs->mapx * gs->mapy * 2);
189 faceNormalsUnsynced.resize(gs->mapx * gs->mapy * 2);
190 centerNormalsSynced.resize(gs->mapx * gs->mapy);
191 centerNormalsUnsynced.resize(gs->mapx * gs->mapy);
192 centerHeightMap.resize(gs->mapx * gs->mapy);
193
194 mipCenterHeightMaps.resize(numHeightMipMaps - 1);
195 mipPointerHeightMaps.resize(numHeightMipMaps, NULL);
196 mipPointerHeightMaps[0] = ¢erHeightMap[0];
197
198 for (int i = 1; i < numHeightMipMaps; i++) {
199 mipCenterHeightMaps[i - 1].resize((gs->mapx >> i) * (gs->mapy >> i));
200 mipPointerHeightMaps[i] = &mipCenterHeightMaps[i - 1][0];
201 }
202
203 slopeMap.resize(gs->hmapx * gs->hmapy);
204 visVertexNormals.resize(gs->mapxp1 * gs->mapyp1);
205
206 // note: if USE_UNSYNCED_HEIGHTMAP is false, then
207 // heightMapUnsyncedPtr points to an empty vector
208 // for SMF maps so indexing it is forbidden (!)
209 assert(heightMapSyncedPtr != NULL);
210 assert(heightMapUnsyncedPtr != NULL);
211
212 {
213 #ifndef USE_UNSYNCED_HEIGHTMAP
214 heightMapUnsyncedPtr = heightMapSyncedPtr;
215 #endif
216
217 sharedCornerHeightMaps[0] = &(*heightMapUnsyncedPtr)[0];
218 sharedCornerHeightMaps[1] = &(*heightMapSyncedPtr)[0];
219
220 sharedCenterHeightMaps[0] = ¢erHeightMap[0]; // NO UNSYNCED VARIANT
221 sharedCenterHeightMaps[1] = ¢erHeightMap[0];
222
223 sharedFaceNormals[0] = &faceNormalsUnsynced[0];
224 sharedFaceNormals[1] = &faceNormalsSynced[0];
225
226 sharedCenterNormals[0] = ¢erNormalsUnsynced[0];
227 sharedCenterNormals[1] = ¢erNormalsSynced[0];
228
229 sharedSlopeMaps[0] = &slopeMap[0]; // NO UNSYNCED VARIANT
230 sharedSlopeMaps[1] = &slopeMap[0];
231 }
232
233 CalcHeightmapChecksum();
234 UpdateHeightMapSynced(SRectangle(0, 0, gs->mapx, gs->mapy), true);
235 // FIXME can't call that yet cause sky & skyLight aren't created yet (crashes in SMFReadMap.cpp)
236 // UpdateDraw();
237 }
238
239
CalcHeightmapChecksum()240 void CReadMap::CalcHeightmapChecksum()
241 {
242 const float* heightmap = GetCornerHeightMapSynced();
243
244 initMinHeight = std::numeric_limits<float>::max();
245 initMaxHeight = -std::numeric_limits<float>::max();
246
247 mapChecksum = 0;
248 for (int i = 0; i < (gs->mapxp1 * gs->mapyp1); ++i) {
249 originalHeightMap[i] = heightmap[i];
250 if (heightmap[i] < initMinHeight) { initMinHeight = heightmap[i]; }
251 if (heightmap[i] > initMaxHeight) { initMaxHeight = heightmap[i]; }
252 mapChecksum += (unsigned int) (heightmap[i] * 100);
253 mapChecksum ^= *(unsigned int*) &heightmap[i];
254 }
255
256 for (unsigned int a = 0; a < mapInfo->map.name.size(); ++a) {
257 mapChecksum += mapInfo->map.name[a];
258 mapChecksum *= mapInfo->map.name[a];
259 }
260
261 currMinHeight = initMinHeight;
262 currMaxHeight = initMaxHeight;
263 }
264
265
UpdateDraw()266 void CReadMap::UpdateDraw()
267 {
268 if (unsyncedHeightMapUpdates.empty())
269 return;
270
271 std::list<SRectangle> ushmu;
272 std::list<SRectangle>::const_iterator ushmuIt;
273
274 {
275 if (!unsyncedHeightMapUpdates.empty())
276 unsyncedHeightMapUpdates.swap(unsyncedHeightMapUpdatesTemp); // swap to avoid Optimize() inside a mutex
277 }
278 {
279 static bool first = true;
280 if (!first) {
281 if (!unsyncedHeightMapUpdatesTemp.empty()) {
282 unsyncedHeightMapUpdatesTemp.Optimize();
283 int updateArea = unsyncedHeightMapUpdatesTemp.GetTotalArea() * 0.0625f + (50 * 50);
284
285 while (updateArea > 0 && !unsyncedHeightMapUpdatesTemp.empty()) {
286 const SRectangle& rect = unsyncedHeightMapUpdatesTemp.front();
287 updateArea -= rect.GetArea();
288 ushmu.push_back(rect);
289 unsyncedHeightMapUpdatesTemp.pop_front();
290 }
291 }
292 } else {
293 // first update is full map
294 unsyncedHeightMapUpdatesTemp.swap(ushmu);
295 first = false;
296 }
297 }
298 if (!unsyncedHeightMapUpdatesTemp.empty()) {
299 unsyncedHeightMapUpdates.splice(unsyncedHeightMapUpdates.end(), unsyncedHeightMapUpdatesTemp);
300 }
301 // unsyncedHeightMapUpdatesTemp is now guaranteed empty
302
303 for (ushmuIt = ushmu.begin(); ushmuIt != ushmu.end(); ++ushmuIt) {
304 UpdateHeightMapUnsynced(*ushmuIt);
305 }
306 for (ushmuIt = ushmu.begin(); ushmuIt != ushmu.end(); ++ushmuIt) {
307 eventHandler.UnsyncedHeightMapUpdate(*ushmuIt);
308 }
309 }
310
311
UpdateHeightMapSynced(SRectangle rect,bool initialize)312 void CReadMap::UpdateHeightMapSynced(SRectangle rect, bool initialize)
313 {
314 if (rect.GetArea() <= 0) {
315 // do not bother with zero-area updates
316 return;
317 }
318
319 rect.x1 = std::max( 0, rect.x1 - 1);
320 rect.z1 = std::max( 0, rect.z1 - 1);
321 rect.x2 = std::min(gs->mapxm1, rect.x2 + 1);
322 rect.z2 = std::min(gs->mapym1, rect.z2 + 1);
323
324 UpdateCenterHeightmap(rect, initialize);
325 UpdateMipHeightmaps(rect, initialize);
326 UpdateFaceNormals(rect, initialize);
327 UpdateSlopemap(rect, initialize); // must happen after UpdateFaceNormals()!
328
329 #ifdef USE_UNSYNCED_HEIGHTMAP
330 // push the unsynced update
331 if (initialize) {
332 // push 1st update through without LOS check
333 unsyncedHeightMapUpdates.push_back(rect);
334 } else {
335 InitHeightMapDigestsVectors();
336
337 const int losSquaresX = losHandler->losSizeX; // size of LOS square in heightmap coords
338 const SRectangle& lm = rect * (SQUARE_SIZE * losHandler->invLosDiv); // LOS space
339
340 // we updated the heightmap so change their digest (byte-overflow is intentional!)
341 for (int lmx = lm.x1; lmx <= lm.x2; ++lmx) {
342 for (int lmz = lm.z1; lmz <= lm.z2; ++lmz) {
343 const int idx = lmx + lmz * (losSquaresX + 1);
344 assert(idx < syncedHeightMapDigests.size());
345 syncedHeightMapDigests[idx]++;
346 }
347 }
348
349 HeightMapUpdateLOSCheck(rect);
350 }
351 #else
352 unsyncedHeightMapUpdates.push_back(rect);
353 #endif
354 }
355
356
UpdateCenterHeightmap(const SRectangle & rect,bool initialize)357 void CReadMap::UpdateCenterHeightmap(const SRectangle& rect, bool initialize)
358 {
359 const float* heightmapSynced = GetCornerHeightMapSynced();
360
361 for (int y = rect.z1; y <= rect.z2; y++) {
362 for (int x = rect.x1; x <= rect.x2; x++) {
363 const int idxTL = (y ) * gs->mapxp1 + x;
364 const int idxTR = (y ) * gs->mapxp1 + x + 1;
365 const int idxBL = (y + 1) * gs->mapxp1 + x;
366 const int idxBR = (y + 1) * gs->mapxp1 + x + 1;
367
368 const float height =
369 heightmapSynced[idxTL] +
370 heightmapSynced[idxTR] +
371 heightmapSynced[idxBL] +
372 heightmapSynced[idxBR];
373 centerHeightMap[y * gs->mapx + x] = height * 0.25f;
374 }
375 }
376 }
377
378
UpdateMipHeightmaps(const SRectangle & rect,bool initialize)379 void CReadMap::UpdateMipHeightmaps(const SRectangle& rect, bool initialize)
380 {
381 for (int i = 0; i < numHeightMipMaps - 1; i++) {
382 const int hmapx = gs->mapx >> i;
383
384 const int sx = (rect.x1 >> i) & (~1);
385 const int ex = (rect.x2 >> i);
386 const int sy = (rect.z1 >> i) & (~1);
387 const int ey = (rect.z2 >> i);
388
389 for (int y = sy; y < ey; y += 2) {
390 for (int x = sx; x < ex; x += 2) {
391 const float height =
392 mipPointerHeightMaps[i][(x ) + (y ) * hmapx] +
393 mipPointerHeightMaps[i][(x ) + (y + 1) * hmapx] +
394 mipPointerHeightMaps[i][(x + 1) + (y ) * hmapx] +
395 mipPointerHeightMaps[i][(x + 1) + (y + 1) * hmapx];
396 mipPointerHeightMaps[i + 1][(x / 2) + (y / 2) * hmapx / 2] = height * 0.25f;
397 }
398 }
399 }
400 }
401
402
UpdateFaceNormals(const SRectangle & rect,bool initialize)403 void CReadMap::UpdateFaceNormals(const SRectangle& rect, bool initialize)
404 {
405 const float* heightmapSynced = GetCornerHeightMapSynced();
406
407 const int z1 = std::max( 0, rect.z1 - 1);
408 const int x1 = std::max( 0, rect.x1 - 1);
409 const int z2 = std::min(gs->mapym1, rect.z2 + 1);
410 const int x2 = std::min(gs->mapxm1, rect.x2 + 1);
411
412 for_mt(z1, z2+1, [&](const int y) {
413 float3 fnTL;
414 float3 fnBR;
415
416 for (int x = x1; x <= x2; x++) {
417 const int idxTL = (y ) * gs->mapxp1 + x; // TL
418 const int idxBL = (y + 1) * gs->mapxp1 + x; // BL
419
420 const float& hTL = heightmapSynced[idxTL ];
421 const float& hTR = heightmapSynced[idxTL + 1];
422 const float& hBL = heightmapSynced[idxBL ];
423 const float& hBR = heightmapSynced[idxBL + 1];
424
425 // normal of top-left triangle (face) in square
426 //
427 // *---> e1
428 // |
429 // |
430 // v
431 // e2
432 //const float3 e1( SQUARE_SIZE, hTR - hTL, 0);
433 //const float3 e2( 0, hBL - hTL, SQUARE_SIZE);
434 //const float3 fnTL = (e2.cross(e1)).Normalize();
435 fnTL.y = SQUARE_SIZE;
436 fnTL.x = - (hTR - hTL);
437 fnTL.z = - (hBL - hTL);
438 fnTL.Normalize();
439
440 // normal of bottom-right triangle (face) in square
441 //
442 // e3
443 // ^
444 // |
445 // |
446 // e4 <---*
447 //const float3 e3(-SQUARE_SIZE, hBL - hBR, 0);
448 //const float3 e4( 0, hTR - hBR,-SQUARE_SIZE);
449 //const float3 fnBR = (e4.cross(e3)).Normalize();
450 fnBR.y = SQUARE_SIZE;
451 fnBR.x = (hBL - hBR);
452 fnBR.z = (hTR - hBR);
453 fnBR.Normalize();
454
455 faceNormalsSynced[(y * gs->mapx + x) * 2 ] = fnTL;
456 faceNormalsSynced[(y * gs->mapx + x) * 2 + 1] = fnBR;
457 // square-normal
458 centerNormalsSynced[y * gs->mapx + x] = (fnTL + fnBR).Normalize();
459
460 #ifdef USE_UNSYNCED_HEIGHTMAP
461 if (initialize) {
462 faceNormalsUnsynced[(y * gs->mapx + x) * 2 ] = faceNormalsSynced[(y * gs->mapx + x) * 2 ];
463 faceNormalsUnsynced[(y * gs->mapx + x) * 2 + 1] = faceNormalsSynced[(y * gs->mapx + x) * 2 + 1];
464 centerNormalsUnsynced[y * gs->mapx + x] = centerNormalsSynced[y * gs->mapx + x];
465 }
466 #endif
467 }
468 });
469 }
470
471
UpdateSlopemap(const SRectangle & rect,bool initialize)472 void CReadMap::UpdateSlopemap(const SRectangle& rect, bool initialize)
473 {
474 const int sx = std::max(0, (rect.x1 / 2) - 1);
475 const int ex = std::min(gs->hmapx - 1, (rect.x2 / 2) + 1);
476 const int sy = std::max(0, (rect.z1 / 2) - 1);
477 const int ey = std::min(gs->hmapy - 1, (rect.z2 / 2) + 1);
478
479 for (int y = sy; y <= ey; y++) {
480 for (int x = sx; x <= ex; x++) {
481 const int idx0 = (y*2 ) * (gs->mapx) + x*2;
482 const int idx1 = (y*2 + 1) * (gs->mapx) + x*2;
483
484 float avgslope = 0.0f;
485 avgslope += faceNormalsSynced[(idx0 ) * 2 ].y;
486 avgslope += faceNormalsSynced[(idx0 ) * 2 + 1].y;
487 avgslope += faceNormalsSynced[(idx0 + 1) * 2 ].y;
488 avgslope += faceNormalsSynced[(idx0 + 1) * 2 + 1].y;
489 avgslope += faceNormalsSynced[(idx1 ) * 2 ].y;
490 avgslope += faceNormalsSynced[(idx1 ) * 2 + 1].y;
491 avgslope += faceNormalsSynced[(idx1 + 1) * 2 ].y;
492 avgslope += faceNormalsSynced[(idx1 + 1) * 2 + 1].y;
493 avgslope *= 0.125f;
494
495 float maxslope = faceNormalsSynced[(idx0 ) * 2 ].y;
496 maxslope = std::min(maxslope, faceNormalsSynced[(idx0 ) * 2 + 1].y);
497 maxslope = std::min(maxslope, faceNormalsSynced[(idx0 + 1) * 2 ].y);
498 maxslope = std::min(maxslope, faceNormalsSynced[(idx0 + 1) * 2 + 1].y);
499 maxslope = std::min(maxslope, faceNormalsSynced[(idx1 ) * 2 ].y);
500 maxslope = std::min(maxslope, faceNormalsSynced[(idx1 ) * 2 + 1].y);
501 maxslope = std::min(maxslope, faceNormalsSynced[(idx1 + 1) * 2 ].y);
502 maxslope = std::min(maxslope, faceNormalsSynced[(idx1 + 1) * 2 + 1].y);
503
504 // smooth it a bit, so small holes don't block huge tanks
505 const float lerp = maxslope / avgslope;
506 const float slope = mix(maxslope, avgslope, lerp);
507
508 slopeMap[y * gs->hmapx + x] = 1.0f - slope;
509 }
510 }
511 }
512
513
514 /// split the update into multiple invididual (los-square) chunks:
HeightMapUpdateLOSCheck(const SRectangle & rect)515 void CReadMap::HeightMapUpdateLOSCheck(const SRectangle& rect)
516 {
517 InitHeightMapDigestsVectors();
518 const int losSqSize = losHandler->losDiv / SQUARE_SIZE; // size of LOS square in heightmap coords
519 const SRectangle& lm = rect * (SQUARE_SIZE * losHandler->invLosDiv); // LOS space
520
521 for (int lmz = lm.z1; lmz <= lm.z2; ++lmz) {
522 const int hmz = lmz * losSqSize;
523 int hmx = lm.x1 * losSqSize;
524
525 SRectangle subrect(hmx, hmz, hmx, hmz + losSqSize);
526 #define PUSH_RECT \
527 if (subrect.GetArea() > 0) { \
528 subrect.ClampIn(rect); \
529 unsyncedHeightMapUpdates.push_back(subrect); \
530 subrect = SRectangle(hmx + losSqSize, hmz, hmx + losSqSize, hmz + losSqSize); \
531 } else { \
532 subrect.x1 = hmx + losSqSize; \
533 subrect.x2 = hmx + losSqSize; \
534 }
535
536 for (int lmx = lm.x1; lmx <= lm.x2; ++lmx) {
537 hmx = lmx * losSqSize;
538
539 #ifdef USE_UNSYNCED_HEIGHTMAP
540 if (!(gu->spectatingFullView || losHandler->InLos(hmx, hmz, gu->myAllyTeam))) {
541 PUSH_RECT
542 continue;
543 }
544 #endif
545
546 if (!HasHeightMapChanged(lmx, lmz)) {
547 PUSH_RECT
548 continue;
549 }
550
551 // update rectangle size
552 subrect.x2 = hmx + losSqSize;
553 }
554
555 PUSH_RECT
556 }
557 }
558
559
InitHeightMapDigestsVectors()560 void CReadMap::InitHeightMapDigestsVectors()
561 {
562 #ifdef USE_UNSYNCED_HEIGHTMAP
563 if (syncedHeightMapDigests.empty()) {
564 const int losSquaresX = losHandler->losSizeX;
565 const int losSquaresY = losHandler->losSizeY;
566 const int size = (losSquaresX + 1) * (losSquaresY + 1);
567 syncedHeightMapDigests.resize(size, 0);
568 unsyncedHeightMapDigests.resize(size, 0);
569 }
570 #endif
571 }
572
573
HasHeightMapChanged(const int lmx,const int lmy)574 bool CReadMap::HasHeightMapChanged(const int lmx, const int lmy)
575 {
576 #ifdef USE_UNSYNCED_HEIGHTMAP
577 const int losSquaresX = losHandler->losSizeX;
578 const int idx = lmx + lmy * (losSquaresX + 1);
579 assert(idx < syncedHeightMapDigests.size());
580 const bool heightmapChanged = (unsyncedHeightMapDigests[idx] != syncedHeightMapDigests[idx]);
581 if (heightmapChanged) {
582 unsyncedHeightMapDigests[idx] = syncedHeightMapDigests[idx];
583 }
584 return heightmapChanged;
585 #else
586 return true;
587 #endif
588 }
589
590
UpdateLOS(const SRectangle & rect)591 void CReadMap::UpdateLOS(const SRectangle& rect)
592 {
593 #ifdef USE_UNSYNCED_HEIGHTMAP
594 if (gu->spectatingFullView)
595 return;
596
597 // currently we use the LOS for view updates (alternatives are AirLOS and/or radar)
598 // cause the others use different resolutions we must check it here for safety
599 // (if you want to use another source you need to change the res. of syncedHeightMapDigests etc.)
600 assert(rect.GetWidth() <= losHandler->losDiv / SQUARE_SIZE);
601
602 //HACK: UpdateLOS() is called for single LOS squares, but we use <= in HeightMapUpdateLOSCheck().
603 // This would make our update area 4x as large, so we need to make the rectangle a point. Better
604 // would be to use < instead of <= everywhere.
605 SRectangle r = rect;
606 r.x2 = r.x1;
607 r.z2 = r.z1;
608
609 HeightMapUpdateLOSCheck(rect);
610 #endif
611 }
612
613
BecomeSpectator()614 void CReadMap::BecomeSpectator()
615 {
616 #ifdef USE_UNSYNCED_HEIGHTMAP
617 HeightMapUpdateLOSCheck(SRectangle(0, 0, gs->mapx, gs->mapy));
618 #endif
619 }
620
HasVisibleWater() const621 bool CReadMap::HasVisibleWater() const { return (!mapInfo->map.voidWater && !IsAboveWater()); }
HasOnlyVoidWater() const622 bool CReadMap::HasOnlyVoidWater() const { return (mapInfo->map.voidWater && IsUnderWater()); }
623
624