MapRenderer.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. /*
  2. * MapRenderer.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "MapRenderer.h"
  12. #include "MapRendererContext.h"
  13. #include "mapHandler.h"
  14. #include "../CGameInfo.h"
  15. #include "../render/CAnimation.h"
  16. #include "../render/Canvas.h"
  17. #include "../render/IImage.h"
  18. #include "../../CCallback.h"
  19. #include "../../lib/RiverHandler.h"
  20. #include "../../lib/RoadHandler.h"
  21. #include "../../lib/TerrainHandler.h"
  22. #include "../../lib/mapObjects/CGHeroInstance.h"
  23. #include "../../lib/mapping/CMap.h"
  24. struct NeighborTilesInfo
  25. {
  26. bool d7, //789
  27. d8, //456
  28. d9, //123
  29. d4,
  30. d5,
  31. d6,
  32. d1,
  33. d2,
  34. d3;
  35. NeighborTilesInfo( const IMapRendererContext & context, const int3 & pos)
  36. {
  37. auto getTile = [&](int dx, int dy)->bool
  38. {
  39. if ( dx + pos.x < 0 || dx + pos.x >= context.getMapSize().x
  40. || dy + pos.y < 0 || dy + pos.y >= context.getMapSize().y)
  41. return false;
  42. //FIXME: please do not read settings for every tile...
  43. return context.isVisible( pos + int3(dx, dy, 0));
  44. };
  45. d7 = getTile(-1, -1); //789
  46. d8 = getTile( 0, -1); //456
  47. d9 = getTile(+1, -1); //123
  48. d4 = getTile(-1, 0);
  49. d5 = getTile( 0, 0);
  50. d6 = getTile(+1, 0);
  51. d1 = getTile(-1, +1);
  52. d2 = getTile( 0, +1);
  53. d3 = getTile(+1, +1);
  54. }
  55. bool areAllHidden() const
  56. {
  57. return !(d1 || d2 || d3 || d4 || d5 || d6 || d7 || d8 || d9);
  58. }
  59. int getBitmapID() const
  60. {
  61. //NOTE: some images have unused in VCMI pair (same blockmap but a bit different look)
  62. // 0-1, 2-3, 4-5, 11-13, 12-14
  63. static const int visBitmaps[256] = {
  64. -1, 34, 4, 4, 22, 23, 4, 4, 36, 36, 38, 38, 47, 47, 38, 38, //16
  65. 3, 25, 12, 12, 3, 25, 12, 12, 9, 9, 6, 6, 9, 9, 6, 6, //32
  66. 35, 39, 48, 48, 41, 43, 48, 48, 36, 36, 38, 38, 47, 47, 38, 38, //48
  67. 26, 49, 28, 28, 26, 49, 28, 28, 9, 9, 6, 6, 9, 9, 6, 6, //64
  68. 0, 45, 29, 29, 24, 33, 29, 29, 37, 37, 7, 7, 50, 50, 7, 7, //80
  69. 13, 27, 44, 44, 13, 27, 44, 44, 8, 8, 10, 10, 8, 8, 10, 10, //96
  70. 0, 45, 29, 29, 24, 33, 29, 29, 37, 37, 7, 7, 50, 50, 7, 7, //112
  71. 13, 27, 44, 44, 13, 27, 44, 44, 8, 8, 10, 10, 8, 8, 10, 10, //128
  72. 15, 17, 30, 30, 16, 19, 30, 30, 46, 46, 40, 40, 32, 32, 40, 40, //144
  73. 2, 25, 12, 12, 2, 25, 12, 12, 9, 9, 6, 6, 9, 9, 6, 6, //160
  74. 18, 42, 31, 31, 20, 21, 31, 31, 46, 46, 40, 40, 32, 32, 40, 40, //176
  75. 26, 49, 28, 28, 26, 49, 28, 28, 9, 9, 6, 6, 9, 9, 6, 6, //192
  76. 0, 45, 29, 29, 24, 33, 29, 29, 37, 37, 7, 7, 50, 50, 7, 7, //208
  77. 13, 27, 44, 44, 13, 27, 44, 44, 8, 8, 10, 10, 8, 8, 10, 10, //224
  78. 0, 45, 29, 29, 24, 33, 29, 29, 37, 37, 7, 7, 50, 50, 7, 7, //240
  79. 13, 27, 44, 44, 13, 27, 44, 44, 8, 8, 10, 10, 8, 8, 10, 10 //256
  80. };
  81. return visBitmaps[d1 + d2 * 2 + d3 * 4 + d4 * 8 + d6 * 16 + d7 * 32 + d8 * 64 + d9 * 128]; // >=0 -> partial hide, <0 - full hide
  82. }
  83. };
  84. MapObjectsSorter::MapObjectsSorter(const IMapRendererContext & context)
  85. : context(context)
  86. {
  87. }
  88. bool MapObjectsSorter::operator()(const ObjectInstanceID & left, const ObjectInstanceID & right) const
  89. {
  90. return (*this)(context.getObject(left), context.getObject(right));
  91. }
  92. bool MapObjectsSorter::operator()(const CGObjectInstance * left, const CGObjectInstance * right) const
  93. {
  94. //FIXME: remove mh access
  95. return CGI->mh->compareObjectBlitOrder(left, right);
  96. }
  97. MapTileStorage::MapTileStorage(size_t capacity)
  98. : animations(capacity)
  99. {
  100. }
  101. void MapTileStorage::load(size_t index, const std::string & filename)
  102. {
  103. auto & terrainAnimations = animations[index];
  104. for(auto & entry : terrainAnimations)
  105. {
  106. entry = std::make_unique<CAnimation>(filename);
  107. entry->preload();
  108. }
  109. for(size_t i = 0; i < terrainAnimations[0]->size(); ++i)
  110. {
  111. terrainAnimations[1]->getImage(i)->verticalFlip();
  112. terrainAnimations[3]->getImage(i)->verticalFlip();
  113. terrainAnimations[2]->getImage(i)->horizontalFlip();
  114. terrainAnimations[3]->getImage(i)->horizontalFlip();
  115. }
  116. }
  117. std::shared_ptr<IImage> MapTileStorage::find(size_t fileIndex, size_t rotationIndex, size_t imageIndex)
  118. {
  119. const auto & animation = animations[fileIndex][rotationIndex];
  120. return animation->getImage(imageIndex);
  121. }
  122. MapRendererTerrain::MapRendererTerrain()
  123. : storage(VLC->terrainTypeHandler->objects.size())
  124. {
  125. for(const auto & terrain : VLC->terrainTypeHandler->objects)
  126. storage.load(terrain->getIndex(), terrain->tilesFilename);
  127. }
  128. void MapRendererTerrain::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  129. {
  130. const TerrainTile & mapTile = context.getMapTile(coordinates);
  131. int32_t terrainIndex = mapTile.terType->getIndex();
  132. int32_t imageIndex = mapTile.terView;
  133. int32_t rotationIndex = mapTile.extTileFlags % 4;
  134. const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
  135. target.draw(image, Point(0, 0));
  136. }
  137. MapRendererRiver::MapRendererRiver()
  138. : storage(VLC->riverTypeHandler->objects.size())
  139. {
  140. for(const auto & river : VLC->riverTypeHandler->objects)
  141. storage.load(river->getIndex(), river->tilesFilename);
  142. }
  143. void MapRendererRiver::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  144. {
  145. const TerrainTile & mapTile = context.getMapTile(coordinates);
  146. if(mapTile.riverType->getId() != River::NO_RIVER)
  147. {
  148. int32_t terrainIndex = mapTile.riverType->getIndex();
  149. int32_t imageIndex = mapTile.riverDir;
  150. int32_t rotationIndex = (mapTile.extTileFlags >> 2) % 4;
  151. const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
  152. target.draw(image, Point(0, 0));
  153. }
  154. }
  155. MapRendererRoad::MapRendererRoad():
  156. storage(VLC->roadTypeHandler->objects.size())
  157. {
  158. for(const auto & road : VLC->roadTypeHandler->objects)
  159. storage.load(road->getIndex(), road->tilesFilename);
  160. }
  161. void MapRendererRoad::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  162. {
  163. const int3 coordinatesAbove = coordinates - int3(0,1,0);
  164. if (context.isInMap(coordinatesAbove))
  165. {
  166. const TerrainTile & mapTileAbove = context.getMapTile(coordinatesAbove);
  167. if (mapTileAbove.roadType->getId() != Road::NO_ROAD)
  168. {
  169. int32_t terrainIndex = mapTileAbove.roadType->getIndex();
  170. int32_t imageIndex = mapTileAbove.roadDir;
  171. int32_t rotationIndex = (mapTileAbove.extTileFlags >> 4) % 4;
  172. const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
  173. target.draw(image, Point(0,0), Rect(0, 16, 32, 16));
  174. }
  175. }
  176. const TerrainTile & mapTile = context.getMapTile(coordinates);
  177. if(mapTile.roadType->getId() != Road::NO_ROAD)
  178. {
  179. int32_t terrainIndex = mapTile.roadType->getIndex();
  180. int32_t imageIndex = mapTile.roadDir;
  181. int32_t rotationIndex = (mapTile.extTileFlags >> 4) % 4;
  182. const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
  183. target.draw(image, Point(0,16), Rect(0, 0, 32, 16));
  184. }
  185. }
  186. MapRendererBorder::MapRendererBorder()
  187. {
  188. animation = std::make_unique<CAnimation>("EDG");
  189. animation->preload();
  190. }
  191. size_t MapRendererBorder::getIndexForTile(const IMapRendererContext & context, const int3 & tile)
  192. {
  193. assert(!context.isInMap(tile));
  194. int3 size = context.getMapSize();
  195. if (tile.x < -1 || tile.x > size.x || tile.y < -1 || tile.y > size.y)
  196. return std::abs(tile.x) % 4 + 4*(std::abs(tile.y) % 4);
  197. if (tile.x == -1 && tile.y == -1)
  198. return 16;
  199. if (tile.x == size.x && tile.y == -1)
  200. return 17;
  201. if (tile.x == size.x && tile.y == size.y)
  202. return 18;
  203. if (tile.x == -1 && tile.y == size.y)
  204. return 19;
  205. if (tile.y == -1)
  206. return 20 + (tile.x % 4);
  207. if (tile.x == size.x)
  208. return 24 + (tile.y % 4);
  209. if (tile.y == size.y)
  210. return 28 + (tile.x % 4);
  211. if (tile.x == -1)
  212. return 32 + (tile.y % 4);
  213. //else - visible area, no renderable border
  214. assert(0);
  215. return 0;
  216. }
  217. void MapRendererBorder::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  218. {
  219. const auto & image = animation->getImage(getIndexForTile(context, coordinates));
  220. target.draw(image, Point(0,0));
  221. }
  222. MapRendererFow::MapRendererFow()
  223. {
  224. fogOfWarFullHide = std::make_unique<CAnimation>("TSHRC");
  225. fogOfWarFullHide->preload();
  226. fogOfWarPartialHide = std::make_unique<CAnimation>("TSHRE");
  227. fogOfWarPartialHide->preload();
  228. static const std::vector<int> rotations = {22, 15, 2, 13, 12, 16, 28, 17, 20, 19, 7, 24, 26, 25, 30, 32, 27};
  229. size_t size = fogOfWarPartialHide->size(0);//group size after next rotation
  230. for(const int rotation : rotations)
  231. {
  232. fogOfWarPartialHide->duplicateImage(0, rotation, 0);
  233. auto image = fogOfWarPartialHide->getImage(size, 0);
  234. image->verticalFlip();
  235. size++;
  236. }
  237. }
  238. void MapRendererFow::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  239. {
  240. assert(!context.isVisible(coordinates));
  241. const NeighborTilesInfo neighborInfo(context, coordinates);
  242. int retBitmapID = neighborInfo.getBitmapID();// >=0 -> partial hide, <0 - full hide
  243. if (retBitmapID < 0)
  244. {
  245. // generate a number that is predefined for each tile,
  246. // but appears random to break visible pattern in large areas of fow
  247. // current approach (use primes as magic numbers for formula) looks to be suitable
  248. size_t pseudorandomNumber = ((coordinates.x * 997) ^ (coordinates.y * 1009)) / 101;
  249. size_t imageIndex = pseudorandomNumber % fogOfWarFullHide->size();
  250. target.draw(fogOfWarFullHide->getImage(imageIndex), Point(0,0));
  251. }
  252. else
  253. {
  254. target.draw(fogOfWarPartialHide->getImage(retBitmapID), Point(0,0));
  255. }
  256. }
  257. std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const CGObjectInstance* obj)
  258. {
  259. const auto & info = obj->appearance;
  260. //the only(?) invisible object
  261. if(info->id == Obj::EVENT)
  262. return std::shared_ptr<CAnimation>();
  263. if(info->animationFile.empty())
  264. {
  265. logGlobal->warn("Def name for obj (%d,%d) is empty!", info->id, info->subid);
  266. return std::shared_ptr<CAnimation>();
  267. }
  268. return getAnimation(info->animationFile);
  269. }
  270. std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const std::string & filename)
  271. {
  272. if (animations.count(filename))
  273. return animations[filename];
  274. auto ret = std::make_shared<CAnimation>(filename);
  275. animations[filename] = ret;
  276. ret->preload();
  277. return ret;
  278. }
  279. void MapRendererObjects::initializeObjects(const IMapRendererContext & context)
  280. {
  281. auto mapSize = context.getMapSize();
  282. objects.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
  283. for(const auto & obj : context.getAllObjects())
  284. {
  285. if(!obj)
  286. continue;
  287. if(obj->ID == Obj::HERO && dynamic_cast<const CGHeroInstance *>(obj.get())->inTownGarrison)
  288. continue;
  289. if(obj->ID == Obj::BOAT && dynamic_cast<const CGBoat *>(obj.get())->hero)
  290. continue;
  291. std::shared_ptr<CAnimation> animation = getAnimation(obj);
  292. //no animation at all, e.g. Event
  293. if(!animation)
  294. continue;
  295. //empty animation. Illegal?
  296. assert(animation->size(0) > 0);
  297. if(animation->size(0) == 0)
  298. continue;
  299. auto image = animation->getImage(0, 0);
  300. int imageWidthTiles = (image->width() + 31) / 32;
  301. int imageHeightTiles = (image->height() + 31) / 32;
  302. int objectWidth = std::min(obj->getWidth(), imageWidthTiles);
  303. int objectHeight = std::min(obj->getHeight(), imageHeightTiles);
  304. for(int fx = 0; fx < objectWidth; ++fx)
  305. {
  306. for(int fy = 0; fy < objectHeight; ++fy)
  307. {
  308. int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);
  309. if(context.isInMap(currTile) && obj->coveringAt(currTile.x, currTile.y))
  310. objects[currTile.z][currTile.x][currTile.y].push_back(obj->id);
  311. }
  312. }
  313. }
  314. for(int z = 0; z < mapSize.z; z++)
  315. {
  316. for(int x = 0; x < mapSize.x; x++)
  317. {
  318. for(int y = 0; y < mapSize.y; y++)
  319. {
  320. auto & array = objects[z][x][y];
  321. std::sort(array.begin(), array.end(), MapObjectsSorter(context));
  322. }
  323. }
  324. }
  325. }
  326. MapRendererObjects::MapRendererObjects(const IMapRendererContext & context)
  327. {
  328. initializeObjects(context);
  329. }
  330. std::shared_ptr<CAnimation> MapRendererObjects::getFlagAnimation(const CGObjectInstance* obj)
  331. {
  332. //TODO: relocate to config file?
  333. static const std::vector<std::string> heroFlags = {
  334. "AF00", "AF01", "AF02", "AF03", "AF04", "AF05", "AF06", "AF07"
  335. };
  336. //TODO: relocate to config file?
  337. static const std::vector<std::vector<std::string>> boatFlags = {
  338. {"ABF01L", "ABF01G", "ABF01R", "ABF01D", "ABF01B", "ABF01P", "ABF01W", "ABF01K"},
  339. {"ABF02L", "ABF02G", "ABF02R", "ABF02D", "ABF02B", "ABF02P", "ABF02W", "ABF02K"},
  340. {"ABF03L", "ABF03G", "ABF03R", "ABF03D", "ABF03B", "ABF03P", "ABF03W", "ABF03K"}
  341. };
  342. if(obj->ID == Obj::HERO)
  343. {
  344. assert(dynamic_cast<const CGHeroInstance *>(obj) != nullptr);
  345. assert(obj->tempOwner.isValidPlayer());
  346. return getAnimation(heroFlags[obj->tempOwner.getNum()]);
  347. }
  348. if(obj->ID == Obj::BOAT)
  349. {
  350. const auto * boat = dynamic_cast<const CGBoat *>(obj);
  351. assert(boat);
  352. assert(boat->subID < boatFlags.size());
  353. assert(!boat->hero || boat->hero->tempOwner.isValidPlayer());
  354. if(boat->hero)
  355. return getAnimation(boatFlags[obj->subID][boat->hero->tempOwner.getNum()]);
  356. }
  357. return nullptr;
  358. }
  359. std::shared_ptr<IImage> MapRendererObjects::getImage(const IMapRendererContext & context, const CGObjectInstance * obj, const std::shared_ptr<CAnimation>& animation) const
  360. {
  361. if(!animation)
  362. return nullptr;
  363. size_t groupIndex = getAnimationGroup(context, obj);
  364. if(animation->size(groupIndex) == 0)
  365. return nullptr;
  366. size_t frameCounter = context.getAnimationTime() / context.getAnimationPeriod();
  367. size_t frameIndex = frameCounter % animation->size(groupIndex);
  368. return animation->getImage(frameIndex, groupIndex);
  369. }
  370. size_t MapRendererObjects::getAnimationGroup(const IMapRendererContext & context, const CGObjectInstance * obj) const
  371. {
  372. // TODO
  373. //static const std::vector<size_t> moveGroups = {99, 10, 5, 6, 7, 8, 9, 12, 11};
  374. static const std::vector<size_t> idleGroups = {99, 13, 0, 1, 2, 3, 4, 15, 14};
  375. if(obj->ID == Obj::HERO)
  376. {
  377. const auto * hero = dynamic_cast<const CGHeroInstance *>(obj);
  378. return idleGroups[hero->moveDir];
  379. }
  380. if(obj->ID == Obj::BOAT)
  381. {
  382. const auto * boat = dynamic_cast<const CGBoat *>(obj);
  383. return idleGroups[boat->direction];
  384. }
  385. return 0;
  386. }
  387. void MapRendererObjects::renderImage(Canvas & target, const int3 & coordinates, const CGObjectInstance * object, const std::shared_ptr<IImage>& image)
  388. {
  389. if(!image)
  390. return;
  391. image->setFlagColor(object->tempOwner);
  392. int3 offsetTiles(object->getPosition() - coordinates);
  393. Point offsetPixels(offsetTiles.x * 32, offsetTiles.y * 32);
  394. Point imagePos = image->dimensions() - offsetPixels - Point(32, 32);
  395. Point tileDimensions(32, 32);
  396. target.draw(image, Point(0, 0), Rect(imagePos, tileDimensions));
  397. }
  398. void MapRendererObjects::renderObject(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance* instance)
  399. {
  400. renderImage(target, coordinates, instance, getImage(context, instance, getAnimation(instance)));
  401. renderImage(target, coordinates, instance, getImage(context, instance, getFlagAnimation(instance)));
  402. }
  403. void MapRendererObjects::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  404. {
  405. for(const auto & objectID : objects[coordinates.z][coordinates.x][coordinates.y])
  406. {
  407. const auto * objectInstance = context.getObject(objectID);
  408. assert(objectInstance);
  409. if(!objectInstance)
  410. {
  411. logGlobal->error("Stray map object that isn't fading");
  412. continue;
  413. }
  414. renderObject(context, target, coordinates, objectInstance);
  415. }
  416. }
  417. void MapRendererObjects::addObject(const IMapRendererContext & context, const CGObjectInstance * object)
  418. {
  419. }
  420. void MapRendererObjects::removeObject(const IMapRendererContext & context, const CGObjectInstance * object)
  421. {
  422. }
  423. void MapRendererDebugGrid::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  424. {
  425. if(context.showGrid())
  426. {
  427. target.drawLine(Point(0, 0), Point(0, 31), {128, 128, 128, 128}, {128, 128, 128, 128});
  428. target.drawLine(Point(0, 0), Point(31, 0), {128, 128, 128, 128}, {128, 128, 128, 128});
  429. }
  430. }
  431. MapRenderer::MapRenderer(const IMapRendererContext & context)
  432. : rendererObjects(context)
  433. {
  434. }
  435. void MapRenderer::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
  436. {
  437. if (!context.isInMap(coordinates))
  438. {
  439. rendererBorder.renderTile(context, target, coordinates);
  440. return;
  441. }
  442. const NeighborTilesInfo neighborInfo(context, coordinates);
  443. if (neighborInfo.areAllHidden())
  444. {
  445. rendererFow.renderTile(context, target, coordinates);
  446. }
  447. else
  448. {
  449. rendererTerrain.renderTile(context, target, coordinates);
  450. rendererRiver.renderTile(context, target, coordinates);
  451. rendererRoad.renderTile(context, target, coordinates);
  452. rendererObjects.renderTile(context, target, coordinates);
  453. if (!context.isVisible(coordinates))
  454. rendererFow.renderTile(context, target, coordinates);
  455. }
  456. rendererDebugGrid.renderTile(context, target,coordinates);
  457. }
  458. void MapRenderer::addObject(const IMapRendererContext & context, const CGObjectInstance * object)
  459. {
  460. rendererObjects.addObject(context, object);
  461. }
  462. void MapRenderer::removeObject(const IMapRendererContext & context, const CGObjectInstance * object)
  463. {
  464. rendererObjects.addObject(context, object);
  465. }