From 6320fd8ab01b08f7e5bf3d5ea56f321ba815fa06 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Fri, 19 Sep 2025 14:35:11 +0200 Subject: [PATCH 1/4] generate button from icon in config (big & small) --- client/adventureMap/AdventureMapWidget.cpp | 9 +++ client/render/AssetGenerator.cpp | 85 ++++++++++++++++------ client/render/AssetGenerator.h | 12 +-- client/render/IRenderHandler.h | 3 + client/renderSDL/RenderHandler.cpp | 5 ++ client/renderSDL/RenderHandler.h | 4 +- 6 files changed, 88 insertions(+), 30 deletions(-) diff --git a/client/adventureMap/AdventureMapWidget.cpp b/client/adventureMap/AdventureMapWidget.cpp index 65c0828a0..4773e11e3 100644 --- a/client/adventureMap/AdventureMapWidget.cpp +++ b/client/adventureMap/AdventureMapWidget.cpp @@ -21,6 +21,7 @@ #include "../GameInstance.h" #include "../gui/Shortcut.h" #include "../mapView/MapView.h" +#include "../render/AssetGenerator.h" #include "../render/IImage.h" #include "../render/IRenderHandler.h" #include "../widgets/Buttons.h" @@ -153,6 +154,14 @@ std::shared_ptr AdventureMapWidget::buildMapButton(const JsonNode & auto help = readHintText(input["help"]); bool playerColored = input["playerColored"].Bool(); + if(!input["generateFromBaseImage"].isNull()) + { + bool small = input["generateSmall"].Bool(); + auto assetGenerator = ENGINE->renderHandler().getAssetGenerator(); + auto layout = assetGenerator->createAdventureMapButton(ImagePath::fromJson(input["generateFromBaseImage"]), small); + assetGenerator->addAnimationFile(AnimationPath::builtin("SPRITES/" + input["image"].String()), layout); + } + auto button = std::make_shared(position.topLeft(), image, help, 0, EShortcut::NONE, playerColored); loadButtonBorderColor(button, input["borderColor"]); diff --git a/client/render/AssetGenerator.cpp b/client/render/AssetGenerator.cpp index 0f8119911..24e849335 100644 --- a/client/render/AssetGenerator.cpp +++ b/client/render/AssetGenerator.cpp @@ -68,7 +68,7 @@ void AssetGenerator::initialize() for(int i = 1; i < 9; i++) imageFiles[ImagePath::builtin("CampaignHc" + std::to_string(i) + "Image.png")] = [this, i](){ return createChroniclesCampaignImages(i);}; - animationFiles[AnimationPath::builtin("SPRITES/adventureLayersButton")] = createAdventureMapButton(ImagePath::builtin("adventureLayers.png")); + animationFiles[AnimationPath::builtin("SPRITES/adventureLayersButton")] = createAdventureMapButton(ImagePath::builtin("adventureLayers.png"), true); createPaletteShiftedSprites(); } @@ -96,6 +96,16 @@ std::map AssetGenerator::gene return animationFiles; } +void AssetGenerator::addImageFile(const ImagePath & path, ImageGenerationFunctor & img) +{ + imageFiles[path] = img; +} + +void AssetGenerator::addAnimationFile(const AnimationPath & path, AnimationLayoutMap & anim) +{ + animationFiles[path] = anim; +} + AssetGenerator::CanvasPtr AssetGenerator::createAdventureOptionsCleanBackground() const { auto locator = ImageLocator(ImagePath::builtin("ADVOPTBK"), EImageBlitMode::OPAQUE); @@ -467,30 +477,56 @@ void meanImage(AssetGenerator::CanvasPtr dst, std::vector & images) } } -AssetGenerator::CanvasPtr AssetGenerator::createAdventureMapButtonClear(const PlayerColor & player) const +AssetGenerator::CanvasPtr AssetGenerator::createAdventureMapButtonClear(const PlayerColor & player, bool small) const { - auto imageNames = { "iam002", "iam003", "iam004", "iam005", "iam006", "iam007", "iam008", "iam009", "iam010", "iam011" }; - std::vector images; - CanvasPtr dst = nullptr; - for(auto & imageName : imageNames) + if(small) { - auto animation = ENGINE->renderHandler().loadAnimation(AnimationPath::builtin(imageName), EImageBlitMode::COLORKEY); - animation->playerColored(player); - auto image = ENGINE->renderHandler().createImage(animation->getImage(2)->dimensions(), CanvasScalingPolicy::IGNORE); - if(!dst) - dst = ENGINE->renderHandler().createImage(animation->getImage(2)->dimensions(), CanvasScalingPolicy::IGNORE); - Canvas canvas = image->getCanvas(); - canvas.draw(animation->getImage(2), Point(0, 0)); - images.push_back(image->getCanvas()); - } + auto imageNames = { "iam002", "iam003", "iam004", "iam005", "iam006", "iam007", "iam008", "iam009", "iam010", "iam011" }; + std::vector images; - meanImage(dst, images); + for(auto & imageName : imageNames) + { + auto animation = ENGINE->renderHandler().loadAnimation(AnimationPath::builtin(imageName), EImageBlitMode::COLORKEY); + animation->playerColored(player); + auto image = ENGINE->renderHandler().createImage(animation->getImage(2)->dimensions(), CanvasScalingPolicy::IGNORE); + if(!dst) + dst = ENGINE->renderHandler().createImage(animation->getImage(2)->dimensions(), CanvasScalingPolicy::IGNORE); + Canvas canvas = image->getCanvas(); + canvas.draw(animation->getImage(2), Point(0, 0)); + images.push_back(image->getCanvas()); + } + + meanImage(dst, images); + } + else + { + auto animation = ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("iam001"), EImageBlitMode::COLORKEY); + animation->playerColored(player); + auto image = animation->getImage(2); + + dst = ENGINE->renderHandler().createImage(image->dimensions(), CanvasScalingPolicy::IGNORE); + Canvas canvas = dst->getCanvas(); + canvas.draw(image, Point(0, 0)); + + auto tmp = ENGINE->renderHandler().createImage(Point(5, 22), CanvasScalingPolicy::IGNORE); + std::vector meanImages; + auto tmpLeft = ENGINE->renderHandler().createImage(Point(5, 22), CanvasScalingPolicy::IGNORE); + tmpLeft->getCanvas().draw(image, Point(0, 0), Rect(18, 6, 5, 22)); + meanImages.push_back(tmpLeft->getCanvas()); + auto tmpRight = ENGINE->renderHandler().createImage(Point(5, 22), CanvasScalingPolicy::IGNORE); + tmpRight->getCanvas().draw(image, Point(0, 0), Rect(42, 6, 5, 22)); + meanImages.push_back(tmpRight->getCanvas()); + meanImage(tmp, meanImages); + + for(int i = 0; i < 4; i++) + canvas.draw(tmp, Point(23 + i * 5, 6)); + } return dst; } -AssetGenerator::AnimationLayoutMap AssetGenerator::createAdventureMapButton(const ImagePath & overlay) +AssetGenerator::AnimationLayoutMap AssetGenerator::createAdventureMapButton(const ImagePath & overlay, bool small) { std::shared_ptr overlayImg = ENGINE->renderHandler().loadImage(ImageLocator(overlay, EImageBlitMode::OPAQUE)); auto overlayCanvasImg = ENGINE->renderHandler().createImage(overlayImg->dimensions(), CanvasScalingPolicy::IGNORE); @@ -500,34 +536,35 @@ AssetGenerator::AnimationLayoutMap AssetGenerator::createAdventureMapButton(cons AnimationLayoutMap layout; for (PlayerColor color(0); color < PlayerColor::PLAYER_LIMIT; ++color) { - auto clearButtonImg = createAdventureMapButtonClear(color); + int offs = small ? 0 : 16; + auto clearButtonImg = createAdventureMapButtonClear(color, small); for(int i = 0; i < 4; i++) { ImagePath spriteName = ImagePath::builtin(overlay.getOriginalName() + "Btn" + std::to_string(i) + ".png"); ImagePath spriteNameColor = ImagePath::builtin(overlay.getOriginalName() + "Btn" + std::to_string(i) + "-" + color.toString() + ".png"); - imageFiles[spriteNameColor] = [overlayCanvasImg, clearButtonImg, i](){ - auto newImg = ENGINE->renderHandler().createImage(overlayCanvasImg->dimensions(), CanvasScalingPolicy::IGNORE); + imageFiles[spriteNameColor] = [overlayCanvasImg, clearButtonImg, i, offs](){ + auto newImg = ENGINE->renderHandler().createImage(clearButtonImg->dimensions(), CanvasScalingPolicy::IGNORE); auto canvas = newImg->getCanvas(); canvas.draw(clearButtonImg, Point(0, 0)); switch (i) { case 0: - canvas.draw(overlayCanvasImg, Point(0, 0)); + canvas.draw(overlayCanvasImg, Point(offs, 0)); return newImg; case 1: canvas.draw(clearButtonImg, Point(1, 1)); - canvas.draw(overlayCanvasImg, Point(1, 1)); + canvas.draw(overlayCanvasImg, Point(offs + 1, 1)); canvas.drawLine(Point(0, 0), Point(newImg->width() - 1, 0), ColorRGBA(0, 0, 0), ColorRGBA(0, 0, 0)); canvas.drawLine(Point(0, 0), Point(0, newImg->height() - 1), ColorRGBA(0, 0, 0), ColorRGBA(0, 0, 0)); canvas.drawColorBlended(Rect(0, 0, newImg->width(), 4), ColorRGBA(0, 0, 0, 160)); canvas.drawColorBlended(Rect(0, 0, 4, newImg->height()), ColorRGBA(0, 0, 0, 160)); return newImg; case 2: - canvas.drawTransparent(overlayCanvasImg->getCanvas(), Point(0, 0), 0.25); + canvas.drawTransparent(overlayCanvasImg->getCanvas(), Point(offs, 0), 0.25); return newImg; default: - canvas.draw(overlayCanvasImg, Point(0, 0)); + canvas.draw(overlayCanvasImg, Point(offs, 0)); canvas.drawLine(Point(0, 0), Point(newImg->width() - 1, 0), ColorRGBA(255, 255, 255), ColorRGBA(255, 255, 255)); canvas.drawLine(Point(newImg->width() - 1, 0), Point(newImg->width() - 1, newImg->height() - 1), ColorRGBA(255, 255, 255), ColorRGBA(255, 255, 255)); canvas.drawLine(Point(newImg->width() - 1, newImg->height() - 1), Point(0, newImg->height() - 1), ColorRGBA(255, 255, 255), ColorRGBA(255, 255, 255)); diff --git a/client/render/AssetGenerator.h b/client/render/AssetGenerator.h index 0259db88f..91e93f832 100644 --- a/client/render/AssetGenerator.h +++ b/client/render/AssetGenerator.h @@ -23,6 +23,7 @@ class AssetGenerator public: using AnimationLayoutMap = std::map>; using CanvasPtr = std::shared_ptr; + using ImageGenerationFunctor = std::function; void initialize(); @@ -31,9 +32,12 @@ public: std::map> generateAllImages(); std::map generateAllAnimations(); -private: - using ImageGenerationFunctor = std::function; + void addImageFile(const ImagePath & path, ImageGenerationFunctor & img); + void addAnimationFile(const AnimationPath & path, AnimationLayoutMap & anim); + AnimationLayoutMap createAdventureMapButton(const ImagePath & overlay, bool small); + +private: struct PaletteAnimation { /// index of first color to cycle @@ -53,14 +57,12 @@ private: CanvasPtr createSpellTabNone() const; CanvasPtr createChroniclesCampaignImages(int chronicle) const; CanvasPtr createPaletteShiftedImage(const AnimationPath & source, const std::vector & animation, int frameIndex, int paletteShiftCounter) const; - CanvasPtr createAdventureMapButtonClear(const PlayerColor & player) const; + CanvasPtr createAdventureMapButtonClear(const PlayerColor & player, bool small) const; CanvasPtr createCreatureInfoPanel(int boxesAmount) const; enum CreatureInfoPanelElement{ BONUS_EFFECTS, SPELL_EFFECTS, BUTTON_PANEL, COMMANDER_BACKGROUND, COMMANDER_ABILITIES }; CanvasPtr createCreatureInfoPanelElement(CreatureInfoPanelElement element) const; CanvasPtr createQuestWindow() const; - AnimationLayoutMap createAdventureMapButton(const ImagePath & overlay); void createPaletteShiftedSprites(); void generatePaletteShiftedAnimation(const AnimationPath & source, const std::vector & animation); - }; diff --git a/client/render/IRenderHandler.h b/client/render/IRenderHandler.h index 475d56883..dc2e41445 100644 --- a/client/render/IRenderHandler.h +++ b/client/render/IRenderHandler.h @@ -22,6 +22,7 @@ class IImage; class CAnimation; class CanvasImage; class SDLImageShared; +class AssetGenerator; enum class EImageBlitMode : uint8_t; enum class CanvasScalingPolicy; enum EFonts : int8_t; @@ -52,4 +53,6 @@ public: virtual std::shared_ptr loadFont(EFonts font) = 0; virtual void exportGeneratedAssets() = 0; + + virtual std::shared_ptr getAssetGenerator() = 0; }; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 27fbdfed6..63a937a1d 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -540,3 +540,8 @@ void RenderHandler::exportGeneratedAssets() for (const auto & entry : assetGenerator->generateAllImages()) entry.second->exportBitmap(VCMIDirs::get().userDataPath() / "Generated" / (entry.first.getOriginalName() + ".png"), nullptr); } + +std::shared_ptr RenderHandler::getAssetGenerator() +{ + return assetGenerator; +} diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index e2f24b901..72d95dc5f 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -28,7 +28,7 @@ class RenderHandler final : public IRenderHandler std::map animationLayouts; std::map> imageFiles; std::map> fonts; - std::unique_ptr assetGenerator; + std::shared_ptr assetGenerator; std::shared_ptr getAnimationFile(const AnimationPath & path); AnimationLayoutMap & getAnimationLayout(const AnimationPath & path, int scalingFactor, EImageBlitMode mode); @@ -67,4 +67,6 @@ public: std::shared_ptr loadFont(EFonts font) override; void exportGeneratedAssets() override; + + std::shared_ptr getAssetGenerator() override; }; From a4b09f831068913743c3637b0204e059db9e47d8 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Fri, 19 Sep 2025 14:48:17 +0200 Subject: [PATCH 2/4] correctly register them --- client/adventureMap/AdventureMapWidget.cpp | 1 + client/render/IRenderHandler.h | 2 ++ client/renderSDL/RenderHandler.cpp | 7 ++++++- client/renderSDL/RenderHandler.h | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/client/adventureMap/AdventureMapWidget.cpp b/client/adventureMap/AdventureMapWidget.cpp index 4773e11e3..7f9c4b9ff 100644 --- a/client/adventureMap/AdventureMapWidget.cpp +++ b/client/adventureMap/AdventureMapWidget.cpp @@ -160,6 +160,7 @@ std::shared_ptr AdventureMapWidget::buildMapButton(const JsonNode & auto assetGenerator = ENGINE->renderHandler().getAssetGenerator(); auto layout = assetGenerator->createAdventureMapButton(ImagePath::fromJson(input["generateFromBaseImage"]), small); assetGenerator->addAnimationFile(AnimationPath::builtin("SPRITES/" + input["image"].String()), layout); + ENGINE->renderHandler().updateGeneratedAssets(); } auto button = std::make_shared(position.topLeft(), image, help, 0, EShortcut::NONE, playerColored); diff --git a/client/render/IRenderHandler.h b/client/render/IRenderHandler.h index dc2e41445..8806ef356 100644 --- a/client/render/IRenderHandler.h +++ b/client/render/IRenderHandler.h @@ -55,4 +55,6 @@ public: virtual void exportGeneratedAssets() = 0; virtual std::shared_ptr getAssetGenerator() = 0; + + virtual void updateGeneratedAssets() = 0; }; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 63a937a1d..78bb2e68b 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -486,7 +486,7 @@ void RenderHandler::onLibraryLoadingFinished(const Services * services) { assert(animationLayouts.empty()); assetGenerator->initialize(); - animationLayouts = assetGenerator->generateAllAnimations(); + updateGeneratedAssets(); addImageListEntries(services->creatures()); addImageListEntries(services->heroTypes()); @@ -545,3 +545,8 @@ std::shared_ptr RenderHandler::getAssetGenerator() { return assetGenerator; } + +void RenderHandler::updateGeneratedAssets() +{ + animationLayouts = assetGenerator->generateAllAnimations(); +} diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index 72d95dc5f..492f1dcd2 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -69,4 +69,5 @@ public: void exportGeneratedAssets() override; std::shared_ptr getAssetGenerator() override; + void updateGeneratedAssets() override; }; From 0e7a067a34e8a689ba5ffbad068db24b16db76fc Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Fri, 19 Sep 2025 15:23:00 +0200 Subject: [PATCH 3/4] fix naming --- client/render/AssetGenerator.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/render/AssetGenerator.cpp b/client/render/AssetGenerator.cpp index 24e849335..afadbdfe1 100644 --- a/client/render/AssetGenerator.cpp +++ b/client/render/AssetGenerator.cpp @@ -540,8 +540,9 @@ AssetGenerator::AnimationLayoutMap AssetGenerator::createAdventureMapButton(cons auto clearButtonImg = createAdventureMapButtonClear(color, small); for(int i = 0; i < 4; i++) { - ImagePath spriteName = ImagePath::builtin(overlay.getOriginalName() + "Btn" + std::to_string(i) + ".png"); - ImagePath spriteNameColor = ImagePath::builtin(overlay.getOriginalName() + "Btn" + std::to_string(i) + "-" + color.toString() + ".png"); + std::string baseName = overlay.getOriginalName() + "Btn" + (small ? "Small" : "Big") + std::to_string(i); + ImagePath spriteName = ImagePath::builtin(baseName + ".png"); + ImagePath spriteNameColor = ImagePath::builtin(baseName + "-" + color.toString() + ".png"); imageFiles[spriteNameColor] = [overlayCanvasImg, clearButtonImg, i, offs](){ auto newImg = ENGINE->renderHandler().createImage(clearButtonImg->dimensions(), CanvasScalingPolicy::IGNORE); From f094cddcb3719e2806a40abe4ec38852cc0d6a51 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Fri, 19 Sep 2025 21:23:52 +0200 Subject: [PATCH 4/4] fix for adding animationLayouts later (after init) --- client/renderSDL/RenderHandler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 78bb2e68b..582fcccc7 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -548,5 +548,6 @@ std::shared_ptr RenderHandler::getAssetGenerator() void RenderHandler::updateGeneratedAssets() { - animationLayouts = assetGenerator->generateAllAnimations(); + for (const auto& [key, value] : assetGenerator->generateAllAnimations()) + animationLayouts[key] = value; }