RenderHandler.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. /*
  2. * RenderHandler.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 "RenderHandler.h"
  12. #include "SDLImage.h"
  13. #include "ScalableImage.h"
  14. #include "FontChain.h"
  15. #include "../GameEngine.h"
  16. #include "../render/AssetGenerator.h"
  17. #include "../render/CAnimation.h"
  18. #include "../render/CanvasImage.h"
  19. #include "../render/CDefFile.h"
  20. #include "../render/Colors.h"
  21. #include "../render/ColorFilter.h"
  22. #include "../render/IScreenHandler.h"
  23. #include "../render/hdEdition/HdImageLoader.h"
  24. #include "../../lib/CConfigHandler.h"
  25. #include "../../lib/CThreadHelper.h"
  26. #include "../../lib/ExceptionsCommon.h"
  27. #include "../../lib/VCMIDirs.h"
  28. #include "../../lib/constants/StringConstants.h"
  29. #include "../../lib/entities/building/CBuilding.h"
  30. #include "../../lib/entities/faction/CTown.h"
  31. #include "../../lib/entities/faction/CTownHandler.h"
  32. #include "../../lib/filesystem/Filesystem.h"
  33. #include "../../lib/json/JsonUtils.h"
  34. #include <vcmi/ArtifactService.h>
  35. #include <vcmi/CreatureService.h>
  36. #include <vcmi/Entity.h>
  37. #include <vcmi/FactionService.h>
  38. #include <vcmi/HeroTypeService.h>
  39. #include <vcmi/Services.h>
  40. #include <vcmi/SkillService.h>
  41. #include <vcmi/spells/Service.h>
  42. #include <vcmi/ResourceTypeService.h>
  43. RenderHandler::RenderHandler()
  44. :assetGenerator(std::make_unique<AssetGenerator>())
  45. {
  46. }
  47. RenderHandler::~RenderHandler() = default;
  48. std::shared_ptr<CDefFile> RenderHandler::getAnimationFile(const AnimationPath & path)
  49. {
  50. AnimationPath actualPath = boost::starts_with(path.getName(), "SPRITES") ? path : path.addPrefix("SPRITES/");
  51. auto it = animationFiles.find(actualPath);
  52. if (it != animationFiles.end())
  53. {
  54. auto locked = it->second.lock();
  55. if (locked)
  56. return locked;
  57. }
  58. if (!CResourceHandler::get()->existsResource(actualPath))
  59. return nullptr;
  60. auto result = std::make_shared<CDefFile>(actualPath);
  61. auto entries = result->getEntries();
  62. for(const auto& entry : entries)
  63. for(size_t i = 0; i < entry.second; ++i)
  64. animationSpriteDefs[actualPath][entry.first][i] = {result->getName(i, entry.first), result->getFrameInfo(i, entry.first)};
  65. animationFiles[actualPath] = result;
  66. return result;
  67. }
  68. std::pair<std::string, CDefFile::SSpriteDef> RenderHandler::getAnimationSpriteDef(const AnimationPath & path, int frame, int group)
  69. {
  70. AnimationPath actualPath = boost::starts_with(path.getName(), "SPRITES") ? path : path.addPrefix("SPRITES/");
  71. return animationSpriteDefs[actualPath][group][frame];
  72. }
  73. ImagePath RenderHandler::getAnimationFrameName(const AnimationPath & path, int frame, int group)
  74. {
  75. auto info = getAnimationSpriteDef(path, frame, group);
  76. auto frameName = info.first;
  77. boost::iterator_range<std::string::iterator> sub = boost::find_first(frameName, ".");
  78. if(!sub.empty())
  79. frameName = std::string(frameName.begin(), sub.begin());
  80. boost::to_upper(frameName);
  81. return ImagePath::builtin(frameName);
  82. }
  83. void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & config, EImageBlitMode mode) const
  84. {
  85. std::string basepath;
  86. basepath = config["basepath"].String();
  87. JsonNode base;
  88. base["margins"] = config["margins"];
  89. base["width"] = config["width"];
  90. base["height"] = config["height"];
  91. for(const JsonNode & group : config["sequences"].Vector())
  92. {
  93. size_t groupID = group["group"].Integer();//TODO: string-to-value conversion("moving" -> MOVING)
  94. source[groupID].clear();
  95. for(const JsonNode & frame : group["frames"].Vector())
  96. {
  97. JsonNode toAdd = frame;
  98. JsonUtils::inherit(toAdd, base);
  99. toAdd["file"].String() = basepath + frame.String();
  100. if(group["generateShadow"].isNumber())
  101. toAdd["generateShadow"].Integer() = group["generateShadow"].Integer();
  102. if(group["generateOverlay"].isNumber())
  103. toAdd["generateOverlay"].Integer() = group["generateOverlay"].Integer();
  104. source[groupID].emplace_back(toAdd, mode);
  105. }
  106. }
  107. for(const JsonNode & node : config["images"].Vector())
  108. {
  109. size_t group = node["group"].Integer();
  110. size_t frame = node["frame"].Integer();
  111. if (source[group].size() <= frame)
  112. source[group].resize(frame+1);
  113. JsonNode toAdd = node;
  114. JsonUtils::inherit(toAdd, base);
  115. if (toAdd.Struct().count("file"))
  116. toAdd["file"].String() = basepath + node["file"].String();
  117. if (toAdd.Struct().count("defFile"))
  118. toAdd["defFile"].String() = basepath + node["defFile"].String();
  119. source[group][frame] = ImageLocator(toAdd, mode);
  120. }
  121. }
  122. RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const AnimationPath & path, int scalingFactor, EImageBlitMode mode)
  123. {
  124. static constexpr std::array scaledSpritesPath = {
  125. "", // 0x
  126. "SPRITES/",
  127. "SPRITES2X/",
  128. "SPRITES3X/",
  129. "SPRITES4X/",
  130. };
  131. std::string pathString = path.getName();
  132. if (boost::starts_with(pathString, "SPRITES/"))
  133. pathString = pathString.substr(std::string("SPRITES/").length());
  134. AnimationPath actualPath = AnimationPath::builtin(scaledSpritesPath.at(scalingFactor) + pathString);
  135. auto it = animationLayouts.find(actualPath);
  136. if (it != animationLayouts.end() && (settings["video"]["useHdTextures"].Bool() || scalingFactor == 1))
  137. return it->second;
  138. AnimationLayoutMap result;
  139. auto defFile = getAnimationFile(actualPath);
  140. if(defFile)
  141. {
  142. const std::map<size_t, size_t> defEntries = defFile->getEntries();
  143. for (const auto & defEntry : defEntries)
  144. result[defEntry.first].resize(defEntry.second);
  145. }
  146. auto jsonResource = actualPath.toType<EResType::JSON>();
  147. auto configList = CResourceHandler::get()->getResourcesWithName(jsonResource);
  148. for(auto & loader : configList)
  149. {
  150. auto stream = loader->load(jsonResource);
  151. std::unique_ptr<ui8[]> textData(new ui8[stream->getSize()]);
  152. stream->read(textData.get(), stream->getSize());
  153. const JsonNode config(reinterpret_cast<const std::byte*>(textData.get()), stream->getSize(), path.getOriginalName());
  154. initFromJson(result, config, mode);
  155. }
  156. animationLayouts[actualPath] = result;
  157. return animationLayouts[actualPath];
  158. }
  159. int RenderHandler::getScalingFactor() const
  160. {
  161. return ENGINE->screenHandler().getScalingFactor();
  162. }
  163. ImageLocator RenderHandler::getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group, int scaling, EImageBlitMode mode)
  164. {
  165. const auto & layout = getAnimationLayout(path, scaling, mode);
  166. if (!layout.count(group))
  167. return ImageLocator();
  168. if (frame >= layout.at(group).size())
  169. return ImageLocator();
  170. const auto & locator = layout.at(group).at(frame);
  171. if (locator.image || locator.defFile)
  172. return locator;
  173. return ImageLocator(path, frame, group, mode);
  174. }
  175. std::shared_ptr<ScalableImageShared> RenderHandler::loadImageImpl(const ImageLocator & locator)
  176. {
  177. auto it = imageFiles.find(locator);
  178. if (it != imageFiles.end())
  179. {
  180. auto locked = it->second.lock();
  181. if (locked)
  182. return locked;
  183. }
  184. auto sdlImage = loadImageFromFileUncached(locator);
  185. auto scaledImage = std::make_shared<ScalableImageShared>(locator, sdlImage);
  186. storeCachedImage(locator, scaledImage);
  187. return scaledImage;
  188. }
  189. std::shared_ptr<ISharedImage> RenderHandler::loadImageFromFileUncached(const ImageLocator & locator)
  190. {
  191. if(locator.image)
  192. {
  193. auto imagePath = *locator.image;
  194. auto imagePathSprites = imagePath.addPrefix("SPRITES/");
  195. auto imagePathData = imagePath.addPrefix("DATA/");
  196. if(CResourceHandler::get()->existsResource(imagePathSprites))
  197. return std::make_shared<SDLImageShared>(imagePathSprites);
  198. if(CResourceHandler::get()->existsResource(imagePathData))
  199. return std::make_shared<SDLImageShared>(imagePathData);
  200. if(CResourceHandler::get()->existsResource(imagePath))
  201. return std::make_shared<SDLImageShared>(imagePath);
  202. auto generated = assetGenerator->generateImage(imagePath);
  203. if (generated)
  204. {
  205. generated->setAsyncUpscale(false); // do not async upscale base image for generated images -> fixes #6201
  206. return generated;
  207. }
  208. logGlobal->error("Failed to load image %s", locator.image->getOriginalName());
  209. return std::make_shared<SDLImageShared>(ImagePath::builtin("DEFAULT"));
  210. }
  211. if(locator.defFile)
  212. {
  213. auto defFile = getAnimationFile(*locator.defFile);
  214. if(defFile->hasFrame(locator.defFrame, locator.defGroup))
  215. {
  216. auto img = std::make_shared<SDLImageShared>(defFile.get(), locator.defFrame, locator.defGroup);
  217. auto pathForDefFrame = getAnimationFrameName(*locator.defFile, locator.defFrame, locator.defGroup);
  218. if(hdImageLoader->exists(pathForDefFrame))
  219. img->setAsyncUpscale(false); // avoids flickering graphics when hd textures are enabled
  220. return img;
  221. }
  222. else
  223. {
  224. logGlobal->error("Frame %d in group %d not found in file: %s",
  225. locator.defFrame, locator.defGroup, locator.defFile->getName().c_str());
  226. return std::make_shared<SDLImageShared>(ImagePath::builtin("DEFAULT"));
  227. }
  228. }
  229. throw std::runtime_error("Invalid image locator received!");
  230. }
  231. void RenderHandler::storeCachedImage(const ImageLocator & locator, std::shared_ptr<ScalableImageShared> image)
  232. {
  233. imageFiles[locator] = image;
  234. }
  235. std::shared_ptr<SDLImageShared> RenderHandler::loadScaledImage(const ImageLocator & locator)
  236. {
  237. static constexpr std::array scaledDataPath = {
  238. "", // 0x
  239. "DATA/",
  240. "DATA2X/",
  241. "DATA3X/",
  242. "DATA4X/",
  243. };
  244. static constexpr std::array scaledSpritesPath = {
  245. "", // 0x
  246. "SPRITES/",
  247. "SPRITES2X/",
  248. "SPRITES3X/",
  249. "SPRITES4X/",
  250. };
  251. ImagePath pathToLoad;
  252. Point defMargins(0, 0);
  253. Point defFullSize(0, 0);
  254. if(locator.defFile)
  255. {
  256. auto remappedLocator = getLocatorForAnimationFrame(*locator.defFile, locator.defFrame, locator.defGroup, locator.scalingFactor, locator.layer);
  257. // we expect that .def's are only used for 1x data, upscaled assets should use standalone images
  258. if (!remappedLocator.image)
  259. {
  260. if(!settings["video"]["useHdTextures"].Bool() || locator.scalingFactor == 1)
  261. return nullptr;
  262. auto info = getAnimationSpriteDef(*locator.defFile, locator.defFrame, locator.defGroup);
  263. defMargins = Point(info.second.leftMargin, info.second.topMargin);
  264. defFullSize = Point(info.second.fullWidth, info.second.fullHeight);
  265. auto pathForDefFrame = getAnimationFrameName(*locator.defFile, locator.defFrame, locator.defGroup);
  266. if(hdImageLoader->exists(pathForDefFrame))
  267. pathToLoad = pathForDefFrame;
  268. }
  269. else
  270. pathToLoad = *remappedLocator.image;
  271. }
  272. if(locator.image)
  273. pathToLoad = *locator.image;
  274. if (pathToLoad.empty())
  275. return nullptr;
  276. std::string imagePathString = pathToLoad.getName();
  277. auto imagePathOriginal = ImagePath::builtin(imagePathString);
  278. bool generateShadow = locator.generateShadow && (*locator.generateShadow) != SharedImageLocator::ShadowMode::SHADOW_NONE;
  279. bool generateOverlay = locator.generateOverlay && (*locator.generateOverlay) != SharedImageLocator::OverlayMode::OVERLAY_NONE;
  280. bool isShadow = locator.layer == EImageBlitMode::ONLY_SHADOW_HIDE_SELECTION || locator.layer == EImageBlitMode::ONLY_SHADOW_HIDE_FLAG_COLOR;
  281. bool isOverlay = locator.layer == EImageBlitMode::ONLY_FLAG_COLOR || locator.layer == EImageBlitMode::ONLY_SELECTION;
  282. bool optimizeImage = !(isShadow && generateShadow) && !(isOverlay && generateOverlay); // images needs to expanded
  283. bool overlay = isOverlay && !generateOverlay;
  284. bool shadow = isShadow && !generateShadow;
  285. if(overlay)
  286. imagePathString += "-OVERLAY";
  287. if(shadow)
  288. imagePathString += "-SHADOW";
  289. if(locator.playerColored.isValidPlayer())
  290. imagePathString += "-" + boost::to_upper_copy(GameConstants::PLAYER_COLOR_NAMES[locator.playerColored.getNum()]);
  291. if(locator.playerColored == PlayerColor::NEUTRAL)
  292. imagePathString += "-NEUTRAL";
  293. auto imagePath = ImagePath::builtin(imagePathString);
  294. auto imagePathSprites = ImagePath::builtin(imagePathString).addPrefix(scaledSpritesPath.at(locator.scalingFactor));
  295. auto imagePathData = ImagePath::builtin(imagePathString).addPrefix(scaledDataPath.at(locator.scalingFactor));
  296. std::shared_ptr<SDLImageShared> img = nullptr;
  297. if(!img && CResourceHandler::get()->existsResource(imagePathSprites) && (settings["video"]["useHdTextures"].Bool() || locator.scalingFactor == 1))
  298. img = std::make_shared<SDLImageShared>(imagePathSprites, optimizeImage);
  299. if(!img && CResourceHandler::get()->existsResource(imagePathData) && (settings["video"]["useHdTextures"].Bool() || locator.scalingFactor == 1))
  300. img = std::make_shared<SDLImageShared>(imagePathData, optimizeImage);
  301. if(!img && hdImageLoader->exists(imagePathOriginal) && settings["video"]["useHdTextures"].Bool() && locator.scalingFactor > 1)
  302. {
  303. if((!isOverlay || !isShadow) || overlay || shadow)
  304. img = hdImageLoader->getImage(imagePathOriginal, defFullSize, defMargins, shadow, overlay);
  305. }
  306. if(!img && CResourceHandler::get()->existsResource(imagePath))
  307. img = std::make_shared<SDLImageShared>(imagePath, optimizeImage);
  308. if(!img && locator.scalingFactor == 1)
  309. img = std::dynamic_pointer_cast<SDLImageShared>(assetGenerator->generateImage(imagePath));
  310. if(img)
  311. {
  312. // TODO: Performance improvement - Run algorithm on optimized ("trimmed") images
  313. // Not implemented yet because different frame image sizes seems to cause wobbeling shadow -> needs a way around this
  314. if(isShadow && generateShadow)
  315. img = img->drawShadow((*locator.generateShadow) == SharedImageLocator::ShadowMode::SHADOW_SHEAR);
  316. if(isOverlay && generateOverlay && (*locator.generateOverlay) == SharedImageLocator::OverlayMode::OVERLAY_OUTLINE)
  317. img = img->drawOutline(Colors::WHITE, 1);
  318. if(locator.scalingFactor == 1)
  319. img->setAsyncUpscale(false); // no base image, needs to be done in sync
  320. }
  321. return img;
  322. }
  323. std::shared_ptr<IImage> RenderHandler::loadImage(const ImageLocator & locator)
  324. {
  325. ImageLocator adjustedLocator = locator;
  326. if(locator.image)
  327. {
  328. std::vector<std::string> splitted;
  329. boost::split(splitted, (*locator.image).getOriginalName(), boost::is_any_of(":"));
  330. if(splitted.size() == 3)
  331. {
  332. // allows image from def file with following filename (first group, then frame): "deffile.def:0:5"
  333. adjustedLocator.defFile = AnimationPath::builtin(splitted[0]);
  334. adjustedLocator.defGroup = std::stoi(splitted[1]);
  335. adjustedLocator.defFrame = std::stoi(splitted[2]);
  336. adjustedLocator.image = std::nullopt;
  337. }
  338. }
  339. std::shared_ptr<ScalableImageInstance> result;
  340. if (adjustedLocator.scalingFactor == 0)
  341. {
  342. auto scaledLocator = adjustedLocator;
  343. scaledLocator.scalingFactor = getScalingFactor();
  344. result = loadImageImpl(scaledLocator)->createImageReference();
  345. }
  346. else
  347. result = loadImageImpl(adjustedLocator)->createImageReference();
  348. if (locator.horizontalFlip)
  349. result->horizontalFlip();
  350. if (locator.verticalFlip)
  351. result->verticalFlip();
  352. return result;
  353. }
  354. std::shared_ptr<IImage> RenderHandler::loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode)
  355. {
  356. ImageLocator locator = getLocatorForAnimationFrame(path, frame, group, 1, mode);
  357. if (!locator.empty())
  358. return loadImage(locator);
  359. else
  360. {
  361. logGlobal->error("Failed to load non-existing image");
  362. return loadImage(ImageLocator(ImagePath::builtin("DEFAULT"), mode));
  363. }
  364. }
  365. std::shared_ptr<IImage> RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode)
  366. {
  367. auto name = path.getOriginalName();
  368. std::vector<std::string> splitted;
  369. boost::split(splitted, name, boost::is_any_of(":"));
  370. if(splitted.size() == 3)
  371. {
  372. // allows image from def file with following filename (first group, then frame): "deffile.def:0:5"
  373. ImageLocator locator = getLocatorForAnimationFrame(AnimationPath::builtin(splitted[0]), std::stoi(splitted[2]), std::stoi(splitted[1]), 1, mode);
  374. return loadImage(locator);
  375. }
  376. ImageLocator locator(path, mode);
  377. return loadImage(locator);
  378. }
  379. std::shared_ptr<CanvasImage> RenderHandler::createImage(const Point & size, CanvasScalingPolicy scalingPolicy)
  380. {
  381. return std::make_shared<CanvasImage>(size, scalingPolicy);
  382. }
  383. std::shared_ptr<CAnimation> RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode)
  384. {
  385. return std::make_shared<CAnimation>(path, getAnimationLayout(path, 1, mode), mode);
  386. }
  387. void RenderHandler::addImageListEntries(const EntityService * service)
  388. {
  389. service->forEachBase([this](const Entity * entity, bool & stop)
  390. {
  391. entity->registerIcons([this](size_t index, size_t group, const std::string & listName, const std::string & imageName)
  392. {
  393. if (imageName.empty())
  394. return;
  395. auto & layout = getAnimationLayout(AnimationPath::builtin("SPRITES/" + listName), 1, EImageBlitMode::COLORKEY);
  396. JsonNode entry;
  397. entry["file"].String() = imageName;
  398. if (index >= layout[group].size())
  399. layout[group].resize(index + 1);
  400. layout[group][index] = ImageLocator(entry, EImageBlitMode::SIMPLE);
  401. });
  402. });
  403. }
  404. static void detectOverlappingBuildings(RenderHandler * renderHandler, const Faction * factionBase)
  405. {
  406. if (!factionBase->hasTown())
  407. return;
  408. auto faction = dynamic_cast<const CFaction*>(factionBase);
  409. for (const auto & left : faction->town->clientInfo.structures)
  410. {
  411. for (const auto & right : faction->town->clientInfo.structures)
  412. {
  413. if (left->identifier <= right->identifier)
  414. continue; // only a<->b comparison is needed, not a<->a or b<->a
  415. if (left->building && right->building && left->building->getBase() == right->building->getBase())
  416. {
  417. if (left->pos.z != right->pos.z)
  418. logMod->warn("Town %s: Upgrades of same building have different z-index: '%s' and '%s'", faction->getJsonKey(), left->identifier, right->identifier);
  419. continue; // upgrades of the same buildings are expected to overlap
  420. }
  421. if (left->pos.z != right->pos.z)
  422. continue; // buildings already have different z-index and have well-defined overlap logic
  423. auto leftImage = renderHandler->loadImage(left->defName, 0, 0, EImageBlitMode::COLORKEY);
  424. auto rightImage = renderHandler->loadImage(right->defName, 0, 0, EImageBlitMode::COLORKEY);
  425. Rect leftRect( left->pos.x, left->pos.y, leftImage->width(), leftImage->height());
  426. Rect rightRect( right->pos.x, right->pos.y, rightImage->width(), rightImage->height());
  427. Rect intersection = leftRect.intersect(rightRect);
  428. Point intersectionPosition;
  429. bool intersectionFound = false;
  430. for (int y = 0; y < intersection.h && !intersectionFound; ++y)
  431. {
  432. for (int x = 0; x < intersection.w && !intersectionFound; ++x)
  433. {
  434. Point leftPoint = Point(x,y) - leftRect.topLeft() + intersection.topLeft();
  435. Point rightPoint = Point(x,y) - rightRect.topLeft() + intersection.topLeft();
  436. if (!leftImage->isTransparent(leftPoint) && !rightImage->isTransparent(rightPoint))
  437. {
  438. intersectionFound = true;
  439. intersectionPosition = intersection.topLeft() + Point(x,y);
  440. }
  441. }
  442. }
  443. if (intersectionFound)
  444. logMod->warn("Town %s: Detected overlapping buildings '%s' and '%s' at (%d, %d) with same z-index!", faction->getJsonKey(), left->identifier, right->identifier, intersectionPosition.x, intersectionPosition.y);
  445. }
  446. }
  447. };
  448. void RenderHandler::onLibraryLoadingFinished(const Services * services)
  449. {
  450. hdImageLoader = std::make_unique<HdImageLoader>(); // needs to initialize after class construction because we need loaded screenHandler for getScalingFactor()
  451. assert(animationLayouts.empty());
  452. assetGenerator->initialize();
  453. updateGeneratedAssets();
  454. addImageListEntries(services->creatures());
  455. addImageListEntries(services->heroTypes());
  456. addImageListEntries(services->artifacts());
  457. addImageListEntries(services->factions());
  458. addImageListEntries(services->spells());
  459. addImageListEntries(services->skills());
  460. addImageListEntries(services->resources());
  461. if (settings["mods"]["validation"].String() == "full")
  462. {
  463. services->factions()->forEach([this](const Faction * factionBase, bool & stop)
  464. {
  465. detectOverlappingBuildings(this, factionBase);
  466. });
  467. }
  468. }
  469. std::shared_ptr<const IFont> RenderHandler::loadFont(EFonts font)
  470. {
  471. if (fonts.count(font))
  472. return fonts.at(font);
  473. const int8_t index = static_cast<int8_t>(font);
  474. logGlobal->debug("Loading font %d", static_cast<int>(index));
  475. auto configList = CResourceHandler::get()->getResourcesWithName(JsonPath::builtin("config/fonts.json"));
  476. std::shared_ptr<FontChain> loadedFont = std::make_shared<FontChain>();
  477. std::string bitmapPath;
  478. for(auto & loader : configList)
  479. {
  480. auto stream = loader->load(JsonPath::builtin("config/fonts.json"));
  481. std::unique_ptr<ui8[]> textData(new ui8[stream->getSize()]);
  482. stream->read(textData.get(), stream->getSize());
  483. const JsonNode config(reinterpret_cast<const std::byte*>(textData.get()), stream->getSize(), "config/fonts.json");
  484. const JsonVector & bmpConf = config["bitmap"].Vector();
  485. const JsonNode & ttfConf = config["trueType"];
  486. bitmapPath = bmpConf[index].String();
  487. if (!ttfConf[bitmapPath].isNull())
  488. loadedFont->addTrueTypeFont(ttfConf[bitmapPath], !config["lowPriority"].Bool());
  489. }
  490. loadedFont->addBitmapFont(bitmapPath);
  491. fonts[font] = loadedFont;
  492. return loadedFont;
  493. }
  494. void RenderHandler::exportGeneratedAssets()
  495. {
  496. for (const auto & entry : assetGenerator->generateAllImages())
  497. entry.second->exportBitmap(VCMIDirs::get().userDataPath() / "Generated" / (entry.first.getOriginalName() + ".png"), nullptr);
  498. }
  499. std::shared_ptr<AssetGenerator> RenderHandler::getAssetGenerator()
  500. {
  501. return assetGenerator;
  502. }
  503. void RenderHandler::updateGeneratedAssets()
  504. {
  505. for (const auto& [key, value] : assetGenerator->generateAllAnimations())
  506. animationLayouts[key] = value;
  507. }