mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-02 23:07:36 +02:00
In-memory assets generation
All assets generation (large spellbook, terrain animations, etc) are now done in memory and used as it, without saving to disk. This should slightly improve load times since there is no encode png / decode png, and should help with avoiding strange bug when vcmi fails to load recently saved assets. If needed, such assets can be force-dumped on disk using already existing console command
This commit is contained in:
parent
1bf1aa8489
commit
cca4c0888c
@ -18,7 +18,6 @@
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "gui/WindowHandler.h"
|
||||
#include "render/IRenderHandler.h"
|
||||
#include "render/AssetGenerator.h"
|
||||
#include "ClientNetPackVisitors.h"
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/gameState/CGameState.h"
|
||||
@ -510,7 +509,7 @@ void ClientCommandManager::handleVsLog(std::istringstream & singleWordBuffer)
|
||||
|
||||
void ClientCommandManager::handleGenerateAssets()
|
||||
{
|
||||
AssetGenerator::generateAll();
|
||||
GH.renderHandler().exportGeneratedAssets();
|
||||
printCommandMessage("All assets generated");
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include "../render/IImage.h"
|
||||
#include "../render/IRenderHandler.h"
|
||||
#include "../render/IScreenHandler.h"
|
||||
#include "../render/AssetGenerator.h"
|
||||
#include "../CMT.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
@ -65,8 +64,6 @@ AdventureMapInterface::AdventureMapInterface():
|
||||
pos.w = GH.screenDimensions().x;
|
||||
pos.h = GH.screenDimensions().y;
|
||||
|
||||
AssetGenerator::createPaletteShiftedSprites();
|
||||
|
||||
shortcuts = std::make_shared<AdventureMapShortcuts>(*this);
|
||||
|
||||
widget = std::make_shared<AdventureMapWidget>(shortcuts);
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../media/ISoundPlayer.h"
|
||||
#include "../render/AssetGenerator.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../render/IRenderHandler.h"
|
||||
@ -80,7 +79,6 @@ BattleStacksController::BattleStacksController(BattleInterface & owner):
|
||||
stackToActivate(nullptr),
|
||||
animIDhelper(0)
|
||||
{
|
||||
AssetGenerator::createCombatUnitNumberWindow();
|
||||
//preparing graphics for displaying amounts of creatures
|
||||
amountNormal = GH.renderHandler().loadImage(ImagePath::builtin("combatUnitNumberWindowDefault"), EImageBlitMode::COLORKEY);
|
||||
amountPositive = GH.renderHandler().loadImage(ImagePath::builtin("combatUnitNumberWindowPositive"), EImageBlitMode::COLORKEY);
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../render/AssetGenerator.h"
|
||||
|
||||
#include "../../lib/StartInfo.h"
|
||||
#include "../../lib/texts/CGeneralTextHandler.h"
|
||||
@ -69,8 +68,6 @@ std::vector<SimturnsInfo> OptionsTabBase::getSimturnsPresets() const
|
||||
|
||||
OptionsTabBase::OptionsTabBase(const JsonPath & configPath)
|
||||
{
|
||||
AssetGenerator::createAdventureOptionsCleanBackground();
|
||||
|
||||
recActions = 0;
|
||||
|
||||
auto setTimerPresetCallback = [this](int index){
|
||||
|
@ -38,7 +38,6 @@
|
||||
#include "../widgets/VideoWidget.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../render/AssetGenerator.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
@ -428,9 +427,6 @@ void CMainMenu::openCampaignScreen(std::string name)
|
||||
{
|
||||
auto const & config = CMainMenuConfig::get().getCampaigns();
|
||||
|
||||
AssetGenerator::createCampaignBackground();
|
||||
AssetGenerator::createChroniclesCampaignImages();
|
||||
|
||||
if(!vstd::contains(config.Struct(), name))
|
||||
{
|
||||
logGlobal->error("Unknown campaign set: %s", name);
|
||||
|
@ -29,36 +29,60 @@
|
||||
#include "../lib/RoadHandler.h"
|
||||
#include "../lib/TerrainHandler.h"
|
||||
|
||||
void AssetGenerator::clear()
|
||||
AssetGenerator::AssetGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
void AssetGenerator::initialize()
|
||||
{
|
||||
// clear to avoid non updated sprites after mod change (if base imnages are used)
|
||||
if(boost::filesystem::is_directory(VCMIDirs::get().userDataPath() / "Generated"))
|
||||
boost::filesystem::remove_all(VCMIDirs::get().userDataPath() / "Generated");
|
||||
}
|
||||
|
||||
void AssetGenerator::generateAll()
|
||||
{
|
||||
createBigSpellBook();
|
||||
createAdventureOptionsCleanBackground();
|
||||
for (int i = 0; i < PlayerColor::PLAYER_LIMIT_I; ++i)
|
||||
createPlayerColoredBackground(PlayerColor(i));
|
||||
createCombatUnitNumberWindow();
|
||||
createCampaignBackground();
|
||||
createChroniclesCampaignImages();
|
||||
imageFiles[ImagePath::builtin("AdventureOptionsBackgroundClear.png")] = [this](){ return createAdventureOptionsCleanBackground();};
|
||||
imageFiles[ImagePath::builtin("SpellBookLarge.png")] = [this](){ return createBigSpellBook();};
|
||||
|
||||
imageFiles[ImagePath::builtin("combatUnitNumberWindowDefault.png")] = [this](){ return createCombatUnitNumberWindow(0.6f, 0.2f, 1.0f);};
|
||||
imageFiles[ImagePath::builtin("combatUnitNumberWindowNeutral.png")] = [this](){ return createCombatUnitNumberWindow(1.0f, 1.0f, 2.0f);};
|
||||
imageFiles[ImagePath::builtin("combatUnitNumberWindowPositive.png")] = [this](){ return createCombatUnitNumberWindow(0.2f, 1.0f, 0.2f);};
|
||||
imageFiles[ImagePath::builtin("combatUnitNumberWindowNegative.png")] = [this](){ return createCombatUnitNumberWindow(1.0f, 0.2f, 0.2f);};
|
||||
|
||||
imageFiles[ImagePath::builtin("CampaignBackground8.png")] = [this](){ return createCampaignBackground();};
|
||||
|
||||
for (PlayerColor color(0); color < PlayerColor::PLAYER_LIMIT; ++color)
|
||||
imageFiles[ImagePath::builtin("DialogBoxBackground_" + color.toString())] = [this, color](){ return createPlayerColoredBackground(color);};
|
||||
|
||||
for(int i = 1; i < 9; i++)
|
||||
imageFiles[ImagePath::builtin("CampaignHc" + std::to_string(i) + "Image.png")] = [this, i](){ return createChroniclesCampaignImages(i);};
|
||||
|
||||
createPaletteShiftedSprites();
|
||||
}
|
||||
|
||||
void AssetGenerator::createAdventureOptionsCleanBackground()
|
||||
std::shared_ptr<ISharedImage> AssetGenerator::generateImage(const ImagePath & image)
|
||||
{
|
||||
std::string filename = "data/AdventureOptionsBackgroundClear.png";
|
||||
if (imageFiles.count(image))
|
||||
return imageFiles.at(image)()->toSharedImage(); // TODO: cache?
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(CResourceHandler::get()->existsResource(ResourcePath(filename))) // overridden by mod, no generation
|
||||
return;
|
||||
std::map<ImagePath, std::shared_ptr<ISharedImage>> AssetGenerator::generateAllImages()
|
||||
{
|
||||
std::map<ImagePath, std::shared_ptr<ISharedImage>> result;
|
||||
|
||||
if(!CResourceHandler::get("local")->createResource(filename))
|
||||
return;
|
||||
ResourcePath savePath(filename, EResType::IMAGE);
|
||||
for (const auto & entry : imageFiles)
|
||||
result[entry.first] = entry.second()->toSharedImage();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::map<AnimationPath, AssetGenerator::AnimationLayoutMap> AssetGenerator::generateAllAnimations()
|
||||
{
|
||||
return animationFiles;
|
||||
}
|
||||
|
||||
AssetGenerator::CanvasPtr AssetGenerator::createAdventureOptionsCleanBackground()
|
||||
{
|
||||
auto locator = ImageLocator(ImagePath::builtin("ADVOPTBK"), EImageBlitMode::OPAQUE);
|
||||
|
||||
std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator);
|
||||
@ -74,20 +98,11 @@ void AssetGenerator::createAdventureOptionsCleanBackground()
|
||||
canvas.draw(img, Point(53, 567), Rect(53, 520, 339, 3));
|
||||
canvas.draw(img, Point(53, 520), Rect(53, 264, 339, 47));
|
||||
|
||||
image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
|
||||
return image;
|
||||
}
|
||||
|
||||
void AssetGenerator::createBigSpellBook()
|
||||
AssetGenerator::CanvasPtr AssetGenerator::createBigSpellBook()
|
||||
{
|
||||
std::string filename = "data/SpellBookLarge.png";
|
||||
|
||||
if(CResourceHandler::get()->existsResource(ResourcePath(filename))) // overridden by mod, no generation
|
||||
return;
|
||||
|
||||
if(!CResourceHandler::get("local")->createResource(filename))
|
||||
return;
|
||||
ResourcePath savePath(filename, EResType::IMAGE);
|
||||
|
||||
auto locator = ImageLocator(ImagePath::builtin("SpelBack"), EImageBlitMode::OPAQUE);
|
||||
|
||||
std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator);
|
||||
@ -135,21 +150,11 @@ void AssetGenerator::createBigSpellBook()
|
||||
canvas.draw(img, Point(575, 465), Rect(417, 406, 37, 45));
|
||||
canvas.draw(img, Point(667, 465), Rect(478, 406, 37, 47));
|
||||
|
||||
image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
|
||||
return image;
|
||||
}
|
||||
|
||||
void AssetGenerator::createPlayerColoredBackground(const PlayerColor & player)
|
||||
AssetGenerator::CanvasPtr AssetGenerator::createPlayerColoredBackground(const PlayerColor & player)
|
||||
{
|
||||
std::string filename = "data/DialogBoxBackground_" + player.toString() + ".png";
|
||||
|
||||
if(CResourceHandler::get()->existsResource(ResourcePath(filename))) // overridden by mod, no generation
|
||||
return;
|
||||
|
||||
if(!CResourceHandler::get("local")->createResource(filename))
|
||||
return;
|
||||
|
||||
ResourcePath savePath(filename, EResType::IMAGE);
|
||||
|
||||
auto locator = ImageLocator(ImagePath::builtin("DiBoxBck"), EImageBlitMode::OPAQUE);
|
||||
|
||||
std::shared_ptr<IImage> texture = GH.renderHandler().loadImage(locator);
|
||||
@ -169,71 +174,44 @@ void AssetGenerator::createPlayerColoredBackground(const PlayerColor & player)
|
||||
|
||||
assert(player.isValidPlayer());
|
||||
if (!player.isValidPlayer())
|
||||
{
|
||||
logGlobal->error("Unable to colorize to invalid player color %d!", player.getNum());
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error("Unable to colorize to invalid player color" + std::to_string(player.getNum()));
|
||||
|
||||
texture->adjustPalette(filters[player.getNum()], 0);
|
||||
texture->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
|
||||
|
||||
auto image = GH.renderHandler().createImage(texture->dimensions(), CanvasScalingPolicy::IGNORE);
|
||||
Canvas canvas = image->getCanvas();
|
||||
canvas.draw(texture, Point(0,0));
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void AssetGenerator::createCombatUnitNumberWindow()
|
||||
AssetGenerator::CanvasPtr AssetGenerator::createCombatUnitNumberWindow(float multR, float multG, float multB)
|
||||
{
|
||||
std::string filenameToSave = "data/combatUnitNumberWindow";
|
||||
|
||||
ResourcePath savePathDefault(filenameToSave + "Default.png", EResType::IMAGE);
|
||||
ResourcePath savePathNeutral(filenameToSave + "Neutral.png", EResType::IMAGE);
|
||||
ResourcePath savePathPositive(filenameToSave + "Positive.png", EResType::IMAGE);
|
||||
ResourcePath savePathNegative(filenameToSave + "Negative.png", EResType::IMAGE);
|
||||
|
||||
if(CResourceHandler::get()->existsResource(savePathDefault)) // overridden by mod, no generation
|
||||
return;
|
||||
|
||||
if(!CResourceHandler::get("local")->createResource(savePathDefault.getOriginalName() + ".png") ||
|
||||
!CResourceHandler::get("local")->createResource(savePathNeutral.getOriginalName() + ".png") ||
|
||||
!CResourceHandler::get("local")->createResource(savePathPositive.getOriginalName() + ".png") ||
|
||||
!CResourceHandler::get("local")->createResource(savePathNegative.getOriginalName() + ".png"))
|
||||
return;
|
||||
|
||||
auto locator = ImageLocator(ImagePath::builtin("CMNUMWIN"), EImageBlitMode::OPAQUE);
|
||||
locator.layer = EImageBlitMode::OPAQUE;
|
||||
|
||||
std::shared_ptr<IImage> texture = GH.renderHandler().loadImage(locator);
|
||||
|
||||
static const auto shifterNormal = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.6f, 0.2f, 1.0f );
|
||||
static const auto shifterPositive = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.2f, 1.0f, 0.2f );
|
||||
static const auto shifterNegative = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 1.0f, 0.2f, 0.2f );
|
||||
static const auto shifterNeutral = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 1.0f, 1.0f, 0.2f );
|
||||
const auto shifter= ColorFilter::genRangeShifter(0.f, 0.f, 0.f, multR, multG, multB);
|
||||
|
||||
// do not change border color
|
||||
static const int32_t ignoredMask = 1 << 26;
|
||||
|
||||
texture->adjustPalette(shifterNormal, ignoredMask);
|
||||
texture->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePathDefault));
|
||||
texture->adjustPalette(shifterPositive, ignoredMask);
|
||||
texture->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePathPositive));
|
||||
texture->adjustPalette(shifterNegative, ignoredMask);
|
||||
texture->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePathNegative));
|
||||
texture->adjustPalette(shifterNeutral, ignoredMask);
|
||||
texture->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePathNeutral));
|
||||
texture->adjustPalette(shifter, ignoredMask);
|
||||
|
||||
auto image = GH.renderHandler().createImage(texture->dimensions(), CanvasScalingPolicy::IGNORE);
|
||||
Canvas canvas = image->getCanvas();
|
||||
canvas.draw(texture, Point(0,0));
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void AssetGenerator::createCampaignBackground()
|
||||
AssetGenerator::CanvasPtr AssetGenerator::createCampaignBackground()
|
||||
{
|
||||
std::string filename = "data/CampaignBackground8.png";
|
||||
|
||||
if(CResourceHandler::get()->existsResource(ResourcePath(filename))) // overridden by mod, no generation
|
||||
return;
|
||||
|
||||
if(!CResourceHandler::get("local")->createResource(filename))
|
||||
return;
|
||||
ResourcePath savePath(filename, EResType::IMAGE);
|
||||
|
||||
auto locator = ImageLocator(ImagePath::builtin("CAMPBACK"), EImageBlitMode::OPAQUE);
|
||||
|
||||
std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator);
|
||||
auto image = GH.renderHandler().createImage(Point(800, 600), CanvasScalingPolicy::IGNORE);
|
||||
auto image = GH.renderHandler().createImage(Point(200, 116), CanvasScalingPolicy::IGNORE);
|
||||
Canvas canvas = image->getCanvas();
|
||||
|
||||
canvas.draw(img, Point(0, 0), Rect(0, 0, 800, 600));
|
||||
@ -264,171 +242,112 @@ void AssetGenerator::createCampaignBackground()
|
||||
std::shared_ptr<IImage> imgSkull = GH.renderHandler().loadImage(locatorSkull);
|
||||
canvas.draw(imgSkull, Point(562, 509), Rect(178, 108, 43, 19));
|
||||
|
||||
image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
|
||||
return image;
|
||||
}
|
||||
|
||||
void AssetGenerator::createChroniclesCampaignImages()
|
||||
AssetGenerator::CanvasPtr AssetGenerator::createChroniclesCampaignImages(int chronicle)
|
||||
{
|
||||
for(int i = 1; i < 9; i++)
|
||||
auto imgPathBg = ImagePath::builtin("data/chronicles_" + std::to_string(chronicle) + "/GamSelBk");
|
||||
auto locator = ImageLocator(imgPathBg, EImageBlitMode::OPAQUE);
|
||||
|
||||
std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator);
|
||||
auto image = GH.renderHandler().createImage(Point(800, 600), CanvasScalingPolicy::IGNORE);
|
||||
Canvas canvas = image->getCanvas();
|
||||
|
||||
std::array sourceRect = {
|
||||
Rect(149, 144, 200, 116),
|
||||
Rect(156, 150, 200, 116),
|
||||
Rect(171, 153, 200, 116),
|
||||
Rect(35, 358, 200, 116),
|
||||
Rect(216, 248, 200, 116),
|
||||
Rect(58, 234, 200, 116),
|
||||
Rect(184, 219, 200, 116),
|
||||
Rect(268, 210, 200, 116),
|
||||
};
|
||||
|
||||
canvas.draw(img, Point(0, 0), sourceRect.at(chronicle-1));
|
||||
|
||||
if (chronicle == 8)
|
||||
{
|
||||
std::string filename = "data/CampaignHc" + std::to_string(i) + "Image.png";
|
||||
|
||||
if(CResourceHandler::get()->existsResource(ResourcePath(filename))) // overridden by mod, no generation
|
||||
continue;
|
||||
|
||||
auto imgPathBg = ImagePath::builtin("data/chronicles_" + std::to_string(i) + "/GamSelBk");
|
||||
if(!CResourceHandler::get()->existsResource(imgPathBg)) // Chronicle episode not installed
|
||||
continue;
|
||||
|
||||
if(!CResourceHandler::get("local")->createResource(filename))
|
||||
continue;
|
||||
ResourcePath savePath(filename, EResType::IMAGE);
|
||||
|
||||
auto locator = ImageLocator(imgPathBg, EImageBlitMode::OPAQUE);
|
||||
|
||||
std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator);
|
||||
auto image = GH.renderHandler().createImage(Point(800, 600), CanvasScalingPolicy::IGNORE);
|
||||
Canvas canvas = image->getCanvas();
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 1:
|
||||
canvas.draw(img, Point(0, 0), Rect(149, 144, 200, 116));
|
||||
break;
|
||||
case 2:
|
||||
canvas.draw(img, Point(0, 0), Rect(156, 150, 200, 116));
|
||||
break;
|
||||
case 3:
|
||||
canvas.draw(img, Point(0, 0), Rect(171, 153, 200, 116));
|
||||
break;
|
||||
case 4:
|
||||
canvas.draw(img, Point(0, 0), Rect(35, 358, 200, 116));
|
||||
break;
|
||||
case 5:
|
||||
canvas.draw(img, Point(0, 0), Rect(216, 248, 200, 116));
|
||||
break;
|
||||
case 6:
|
||||
canvas.draw(img, Point(0, 0), Rect(58, 234, 200, 116));
|
||||
break;
|
||||
case 7:
|
||||
canvas.draw(img, Point(0, 0), Rect(184, 219, 200, 116));
|
||||
break;
|
||||
case 8:
|
||||
canvas.draw(img, Point(0, 0), Rect(268, 210, 200, 116));
|
||||
|
||||
//skull
|
||||
auto locatorSkull = ImageLocator(ImagePath::builtin("CampSP1"), EImageBlitMode::OPAQUE);
|
||||
std::shared_ptr<IImage> imgSkull = GH.renderHandler().loadImage(locatorSkull);
|
||||
canvas.draw(imgSkull, Point(162, 94), Rect(162, 94, 41, 22));
|
||||
canvas.draw(img, Point(162, 94), Rect(424, 304, 14, 4));
|
||||
canvas.draw(img, Point(162, 98), Rect(424, 308, 10, 4));
|
||||
canvas.draw(img, Point(158, 102), Rect(424, 312, 10, 4));
|
||||
canvas.draw(img, Point(154, 106), Rect(424, 316, 10, 4));
|
||||
break;
|
||||
}
|
||||
|
||||
image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
|
||||
//skull
|
||||
auto locatorSkull = ImageLocator(ImagePath::builtin("CampSP1"), EImageBlitMode::OPAQUE);
|
||||
std::shared_ptr<IImage> imgSkull = GH.renderHandler().loadImage(locatorSkull);
|
||||
canvas.draw(imgSkull, Point(162, 94), Rect(162, 94, 41, 22));
|
||||
canvas.draw(img, Point(162, 94), Rect(424, 304, 14, 4));
|
||||
canvas.draw(img, Point(162, 98), Rect(424, 308, 10, 4));
|
||||
canvas.draw(img, Point(158, 102), Rect(424, 312, 10, 4));
|
||||
canvas.draw(img, Point(154, 106), Rect(424, 316, 10, 4));
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void AssetGenerator::createPaletteShiftedSprites()
|
||||
{
|
||||
std::vector<std::string> tiles;
|
||||
std::vector<std::vector<std::variant<TerrainPaletteAnimation, RiverPaletteAnimation>>> paletteAnimations;
|
||||
for(auto entity : VLC->terrainTypeHandler->objects)
|
||||
{
|
||||
if(entity->paletteAnimation.size())
|
||||
{
|
||||
tiles.push_back(entity->tilesFilename.getName());
|
||||
std::vector<std::variant<TerrainPaletteAnimation, RiverPaletteAnimation>> tmpAnim;
|
||||
for(auto & animEntity : entity->paletteAnimation)
|
||||
tmpAnim.push_back(animEntity);
|
||||
paletteAnimations.push_back(tmpAnim);
|
||||
}
|
||||
if(entity->paletteAnimation.empty())
|
||||
continue;
|
||||
|
||||
std::vector<PaletteAnimation> paletteShifts;
|
||||
for(auto & animEntity : entity->paletteAnimation)
|
||||
paletteShifts.push_back({animEntity.start, animEntity.length});
|
||||
|
||||
generatePaletteShiftedAnimation(entity->tilesFilename, paletteShifts);
|
||||
|
||||
}
|
||||
for(auto entity : VLC->riverTypeHandler->objects)
|
||||
{
|
||||
if(entity->paletteAnimation.size())
|
||||
{
|
||||
tiles.push_back(entity->tilesFilename.getName());
|
||||
std::vector<std::variant<TerrainPaletteAnimation, RiverPaletteAnimation>> tmpAnim;
|
||||
for(auto & animEntity : entity->paletteAnimation)
|
||||
tmpAnim.push_back(animEntity);
|
||||
paletteAnimations.push_back(tmpAnim);
|
||||
}
|
||||
}
|
||||
if(entity->paletteAnimation.empty())
|
||||
continue;
|
||||
|
||||
for(int i = 0; i < tiles.size(); i++)
|
||||
{
|
||||
auto sprite = tiles[i];
|
||||
std::vector<PaletteAnimation> paletteShifts;
|
||||
for(auto & animEntity : entity->paletteAnimation)
|
||||
paletteShifts.push_back({animEntity.start, animEntity.length});
|
||||
|
||||
JsonNode config;
|
||||
config["basepath"].String() = sprite + "_Shifted/";
|
||||
config["images"].Vector();
|
||||
|
||||
auto filename = AnimationPath::builtin(sprite).addPrefix("SPRITES/");
|
||||
auto filenameNew = AnimationPath::builtin(sprite + "_Shifted").addPrefix("SPRITES/");
|
||||
|
||||
if(CResourceHandler::get()->existsResource(ResourcePath(filenameNew.getName(), EResType::JSON))) // overridden by mod, no generation
|
||||
return;
|
||||
|
||||
auto anim = GH.renderHandler().loadAnimation(filename, EImageBlitMode::COLORKEY);
|
||||
for(int j = 0; j < anim->size(); j++)
|
||||
{
|
||||
int maxLen = 1;
|
||||
for(int k = 0; k < paletteAnimations[i].size(); k++)
|
||||
{
|
||||
auto element = paletteAnimations[i][k];
|
||||
if(std::holds_alternative<TerrainPaletteAnimation>(element))
|
||||
maxLen = std::lcm(maxLen, std::get<TerrainPaletteAnimation>(element).length);
|
||||
else
|
||||
maxLen = std::lcm(maxLen, std::get<RiverPaletteAnimation>(element).length);
|
||||
}
|
||||
for(int l = 0; l < maxLen; l++)
|
||||
{
|
||||
std::string spriteName = sprite + boost::str(boost::format("%02d") % j) + "_" + std::to_string(l) + ".png";
|
||||
std::string filenameNewImg = "sprites/" + sprite + "_Shifted" + "/" + spriteName;
|
||||
ResourcePath savePath(filenameNewImg, EResType::IMAGE);
|
||||
|
||||
if(!CResourceHandler::get("local")->createResource(filenameNewImg))
|
||||
return;
|
||||
|
||||
auto imgLoc = anim->getImageLocator(j, 0);
|
||||
auto img = GH.renderHandler().loadImage(imgLoc);
|
||||
for(int k = 0; k < paletteAnimations[i].size(); k++)
|
||||
{
|
||||
auto element = paletteAnimations[i][k];
|
||||
if(std::holds_alternative<TerrainPaletteAnimation>(element))
|
||||
{
|
||||
auto tmp = std::get<TerrainPaletteAnimation>(element);
|
||||
img->shiftPalette(tmp.start, tmp.length, l % tmp.length);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = std::get<RiverPaletteAnimation>(element);
|
||||
img->shiftPalette(tmp.start, tmp.length, l % tmp.length);
|
||||
}
|
||||
}
|
||||
|
||||
auto image = GH.renderHandler().createImage(Point(32, 32), CanvasScalingPolicy::IGNORE);
|
||||
Canvas canvas = image->getCanvas();
|
||||
canvas.draw(img, Point((32 - img->dimensions().x) / 2, (32 - img->dimensions().y) / 2));
|
||||
image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
|
||||
|
||||
JsonNode node(JsonMap{
|
||||
{ "group", JsonNode(l) },
|
||||
{ "frame", JsonNode(j) },
|
||||
{ "file", JsonNode(spriteName) }
|
||||
});
|
||||
config["images"].Vector().push_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
ResourcePath savePath(filenameNew.getOriginalName(), EResType::JSON);
|
||||
if(!CResourceHandler::get("local")->createResource(filenameNew.getOriginalName() + ".json"))
|
||||
return;
|
||||
|
||||
std::fstream file(CResourceHandler::get("local")->getResourceName(savePath)->c_str(), std::ofstream::out | std::ofstream::trunc);
|
||||
file << config.toString();
|
||||
generatePaletteShiftedAnimation(entity->tilesFilename, paletteShifts);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetGenerator::generatePaletteShiftedAnimation(const AnimationPath & sprite, const std::vector<PaletteAnimation> & paletteAnimations)
|
||||
{
|
||||
AnimationLayoutMap layout;
|
||||
|
||||
auto animation = GH.renderHandler().loadAnimation(sprite, EImageBlitMode::COLORKEY);
|
||||
|
||||
int paletteTransformLength = 1;
|
||||
for (const auto & transform : paletteAnimations)
|
||||
paletteTransformLength = std::lcm(paletteTransformLength, transform.length);
|
||||
|
||||
for(int tileIndex = 0; tileIndex < animation->size(); tileIndex++)
|
||||
{
|
||||
for(int paletteIndex = 0; paletteIndex < paletteTransformLength; paletteIndex++)
|
||||
{
|
||||
ImagePath spriteName = ImagePath::builtin(sprite.getName() + boost::str(boost::format("%02d") % tileIndex) + "_" + std::to_string(paletteIndex) + ".png");
|
||||
layout[paletteIndex].push_back(ImageLocator(spriteName, EImageBlitMode::SIMPLE));
|
||||
|
||||
imageFiles[spriteName] = [=](){ return createPaletteShiftedImage(sprite, paletteAnimations, tileIndex, paletteIndex);};
|
||||
}
|
||||
}
|
||||
|
||||
AnimationPath shiftedPath = AnimationPath::builtin("SPRITES/" + sprite.getName() + "_SHIFTED");
|
||||
animationFiles[shiftedPath] = layout;
|
||||
}
|
||||
|
||||
AssetGenerator::CanvasPtr AssetGenerator::createPaletteShiftedImage(const AnimationPath & source, const std::vector<PaletteAnimation> & palette, int frameIndex, int paletteShiftCounter)
|
||||
{
|
||||
auto animation = GH.renderHandler().loadAnimation(source, EImageBlitMode::COLORKEY);
|
||||
|
||||
auto imgLoc = animation->getImageLocator(frameIndex, 0);
|
||||
auto img = GH.renderHandler().loadImage(imgLoc);
|
||||
|
||||
for(const auto & element : palette)
|
||||
img->shiftPalette(element.start, element.length, paletteShiftCounter % element.length);
|
||||
|
||||
auto image = GH.renderHandler().createImage(Point(32, 32), CanvasScalingPolicy::IGNORE);
|
||||
Canvas canvas = image->getCanvas();
|
||||
canvas.draw(img, Point((32 - img->dimensions().x) / 2, (32 - img->dimensions().y) / 2));
|
||||
|
||||
return image;
|
||||
|
||||
}
|
||||
|
@ -9,20 +9,53 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ImageLocator.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class PlayerColor;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class ISharedImage;
|
||||
class CanvasImage;
|
||||
|
||||
class AssetGenerator
|
||||
{
|
||||
public:
|
||||
static void clear();
|
||||
static void generateAll();
|
||||
static void createAdventureOptionsCleanBackground();
|
||||
static void createBigSpellBook();
|
||||
static void createPlayerColoredBackground(const PlayerColor & player);
|
||||
static void createCombatUnitNumberWindow();
|
||||
static void createCampaignBackground();
|
||||
static void createChroniclesCampaignImages();
|
||||
static void createPaletteShiftedSprites();
|
||||
using AnimationLayoutMap = std::map<size_t, std::vector<ImageLocator>>;
|
||||
using CanvasPtr = std::shared_ptr<CanvasImage>;
|
||||
|
||||
AssetGenerator();
|
||||
|
||||
void initialize();
|
||||
|
||||
std::shared_ptr<ISharedImage> generateImage(const ImagePath & image);
|
||||
|
||||
std::map<ImagePath, std::shared_ptr<ISharedImage>> generateAllImages();
|
||||
std::map<AnimationPath, AnimationLayoutMap> generateAllAnimations();
|
||||
|
||||
private:
|
||||
using ImageGenerationFunctor = std::function<CanvasPtr()>;
|
||||
|
||||
struct PaletteAnimation
|
||||
{
|
||||
/// index of first color to cycle
|
||||
int32_t start;
|
||||
/// total numbers of colors to cycle
|
||||
int32_t length;
|
||||
};
|
||||
|
||||
std::map<ImagePath, ImageGenerationFunctor> imageFiles;
|
||||
std::map<AnimationPath, AnimationLayoutMap> animationFiles;
|
||||
|
||||
CanvasPtr createAdventureOptionsCleanBackground();
|
||||
CanvasPtr createBigSpellBook();
|
||||
CanvasPtr createPlayerColoredBackground(const PlayerColor & player);
|
||||
CanvasPtr createCombatUnitNumberWindow(float multR, float multG, float multB);
|
||||
CanvasPtr createCampaignBackground();
|
||||
CanvasPtr createChroniclesCampaignImages(int chronicle);
|
||||
CanvasPtr createPaletteShiftedImage(const AnimationPath & source, const std::vector<PaletteAnimation> & animation, int frameIndex, int paletteShiftCounter);
|
||||
|
||||
void createPaletteShiftedSprites();
|
||||
void generatePaletteShiftedAnimation(const AnimationPath & source, const std::vector<PaletteAnimation> & animation);
|
||||
|
||||
};
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "../render/IScreenHandler.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
#include "../renderSDL/SDLImageScaler.h"
|
||||
#include "../renderSDL/SDLImage.h"
|
||||
|
||||
#include <SDL_image.h>
|
||||
#include <SDL_surface.h>
|
||||
@ -61,3 +62,8 @@ Point CanvasImage::dimensions() const
|
||||
{
|
||||
return {surface->w, surface->h};
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> CanvasImage::toSharedImage()
|
||||
{
|
||||
return std::make_shared<SDLImageShared>(surface);
|
||||
}
|
||||
|
@ -34,6 +34,8 @@ public:
|
||||
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override{};
|
||||
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override{};
|
||||
|
||||
std::shared_ptr<ISharedImage> toSharedImage();
|
||||
|
||||
private:
|
||||
SDL_Surface * surface;
|
||||
CanvasScalingPolicy scalingPolicy;
|
||||
|
@ -50,4 +50,6 @@ public:
|
||||
|
||||
/// Returns font with specified identifer
|
||||
virtual std::shared_ptr<const IFont> loadFont(EFonts font) = 0;
|
||||
|
||||
virtual void exportGeneratedAssets() = 0;
|
||||
};
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
|
||||
#include "../render/AssetGenerator.h"
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/CanvasImage.h"
|
||||
#include "../render/CDefFile.h"
|
||||
@ -43,6 +44,13 @@
|
||||
#include <vcmi/SkillService.h>
|
||||
#include <vcmi/spells/Service.h>
|
||||
|
||||
RenderHandler::RenderHandler()
|
||||
:assetGenerator(std::make_unique<AssetGenerator>())
|
||||
{
|
||||
}
|
||||
|
||||
RenderHandler::~RenderHandler() = default;
|
||||
|
||||
std::shared_ptr<CDefFile> RenderHandler::getAnimationFile(const AnimationPath & path)
|
||||
{
|
||||
AnimationPath actualPath = boost::starts_with(path.getName(), "SPRITES") ? path : path.addPrefix("SPRITES/");
|
||||
@ -201,12 +209,28 @@ std::shared_ptr<ScalableImageShared> RenderHandler::loadImageImpl(const ImageLoc
|
||||
return scaledImage;
|
||||
}
|
||||
|
||||
std::shared_ptr<SDLImageShared> RenderHandler::loadImageFromFileUncached(const ImageLocator & locator)
|
||||
std::shared_ptr<ISharedImage> RenderHandler::loadImageFromFileUncached(const ImageLocator & locator)
|
||||
{
|
||||
if(locator.image)
|
||||
{
|
||||
// TODO: create EmptySharedImage class that will be instantiated if image does not exists or fails to load
|
||||
return std::make_shared<SDLImageShared>(*locator.image);
|
||||
auto imagePath = *locator.image;
|
||||
auto imagePathSprites = imagePath.addPrefix("SPRITES/");
|
||||
auto imagePathData = imagePath.addPrefix("DATA/");
|
||||
|
||||
if(CResourceHandler::get()->existsResource(imagePathSprites))
|
||||
return std::make_shared<SDLImageShared>(imagePathSprites);
|
||||
|
||||
if(CResourceHandler::get()->existsResource(imagePathData))
|
||||
return std::make_shared<SDLImageShared>(imagePathData);
|
||||
|
||||
if(CResourceHandler::get()->existsResource(imagePath))
|
||||
return std::make_shared<SDLImageShared>(imagePath);
|
||||
|
||||
auto generated = assetGenerator->generateImage(imagePath);
|
||||
if (generated)
|
||||
return generated;
|
||||
|
||||
return std::make_shared<SDLImageShared>(ImagePath::builtin("DEFAULT"));
|
||||
}
|
||||
|
||||
if(locator.defFile)
|
||||
@ -423,6 +447,10 @@ static void detectOverlappingBuildings(RenderHandler * renderHandler, const Fact
|
||||
|
||||
void RenderHandler::onLibraryLoadingFinished(const Services * services)
|
||||
{
|
||||
assert(animationLayouts.empty());
|
||||
assetGenerator->initialize();
|
||||
animationLayouts = assetGenerator->generateAllAnimations();
|
||||
|
||||
addImageListEntries(services->creatures());
|
||||
addImageListEntries(services->heroTypes());
|
||||
addImageListEntries(services->artifacts());
|
||||
@ -469,3 +497,9 @@ std::shared_ptr<const IFont> RenderHandler::loadFont(EFonts font)
|
||||
fonts[font] = loadedFont;
|
||||
return loadedFont;
|
||||
}
|
||||
|
||||
void RenderHandler::exportGeneratedAssets()
|
||||
{
|
||||
for (const auto & entry : assetGenerator->generateAllImages())
|
||||
entry.second->exportBitmap(VCMIDirs::get().userDataPath() / "Generated" / (entry.first.getOriginalName() + ".png"), nullptr);
|
||||
}
|
||||
|
@ -18,8 +18,9 @@ VCMI_LIB_NAMESPACE_END
|
||||
class CDefFile;
|
||||
class SDLImageShared;
|
||||
class ScalableImageShared;
|
||||
class AssetGenerator;
|
||||
|
||||
class RenderHandler : public IRenderHandler
|
||||
class RenderHandler final : public IRenderHandler
|
||||
{
|
||||
using AnimationLayoutMap = std::map<size_t, std::vector<ImageLocator>>;
|
||||
|
||||
@ -27,6 +28,7 @@ class RenderHandler : public IRenderHandler
|
||||
std::map<AnimationPath, AnimationLayoutMap> animationLayouts;
|
||||
std::map<SharedImageLocator, std::shared_ptr<ScalableImageShared>> imageFiles;
|
||||
std::map<EFonts, std::shared_ptr<const IFont>> fonts;
|
||||
std::unique_ptr<AssetGenerator> assetGenerator;
|
||||
|
||||
std::shared_ptr<CDefFile> getAnimationFile(const AnimationPath & path);
|
||||
AnimationLayoutMap & getAnimationLayout(const AnimationPath & path, int scalingFactor, EImageBlitMode mode);
|
||||
@ -38,13 +40,15 @@ class RenderHandler : public IRenderHandler
|
||||
|
||||
std::shared_ptr<ScalableImageShared> loadImageImpl(const ImageLocator & config);
|
||||
|
||||
std::shared_ptr<SDLImageShared> loadImageFromFileUncached(const ImageLocator & locator);
|
||||
std::shared_ptr<ISharedImage> loadImageFromFileUncached(const ImageLocator & locator);
|
||||
|
||||
ImageLocator getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group, int scaling, EImageBlitMode mode);
|
||||
|
||||
int getScalingFactor() const;
|
||||
|
||||
public:
|
||||
RenderHandler();
|
||||
~RenderHandler();
|
||||
|
||||
// IRenderHandler implementation
|
||||
void onLibraryLoadingFinished(const Services * services) override;
|
||||
@ -61,4 +65,6 @@ public:
|
||||
|
||||
/// Returns font with specified identifer
|
||||
std::shared_ptr<const IFont> loadFont(EFonts font) override;
|
||||
|
||||
void exportGeneratedAssets() override;
|
||||
};
|
||||
|
@ -306,6 +306,10 @@ std::shared_ptr<const ISharedImage> SDLImageShared::scaleTo(const Point & size,
|
||||
|
||||
void SDLImageShared::exportBitmap(const boost::filesystem::path& path, SDL_Palette * palette) const
|
||||
{
|
||||
auto directory = path;
|
||||
directory.remove_filename();
|
||||
boost::filesystem::create_directories(directory);
|
||||
|
||||
assert(upscalingInProgress == false);
|
||||
if (!surf)
|
||||
return;
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "MiscWidgets.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../render/AssetGenerator.h"
|
||||
#include "../render/IImage.h"
|
||||
#include "../render/IRenderHandler.h"
|
||||
#include "../render/CAnimation.h"
|
||||
@ -184,8 +183,6 @@ FilledTexturePlayerColored::FilledTexturePlayerColored(Rect position)
|
||||
|
||||
void FilledTexturePlayerColored::setPlayerColor(PlayerColor player)
|
||||
{
|
||||
AssetGenerator::createPlayerColoredBackground(player);
|
||||
|
||||
ImagePath imagePath = ImagePath::builtin("DialogBoxBackground_" + player.toString() + ".bmp");
|
||||
|
||||
texture = GH.renderHandler().loadImage(imagePath, EImageBlitMode::COLORKEY);
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/VideoWidget.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../render/AssetGenerator.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
||||
@ -118,7 +117,6 @@ CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _m
|
||||
|
||||
if(isBigSpellbook)
|
||||
{
|
||||
AssetGenerator::createBigSpellBook();
|
||||
background = std::make_shared<CPicture>(ImagePath::builtin("SpellBookLarge"), 0, 0);
|
||||
updateShadow();
|
||||
}
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include "../client/media/CMusicHandler.h"
|
||||
#include "../client/media/CSoundHandler.h"
|
||||
#include "../client/media/CVideoHandler.h"
|
||||
#include "../client/render/AssetGenerator.h"
|
||||
#include "../client/render/Graphics.h"
|
||||
#include "../client/render/IRenderHandler.h"
|
||||
#include "../client/render/IScreenHandler.h"
|
||||
@ -235,8 +234,6 @@ int main(int argc, char * argv[])
|
||||
logGlobal->info("Creating console and configuring logger: %d ms", pomtime.getDiff());
|
||||
logGlobal->info("The log file will be saved to %s", logPath);
|
||||
|
||||
AssetGenerator::clear();
|
||||
|
||||
// Init filesystem and settings
|
||||
try
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user