diff options
Diffstat (limited to 'extension/src')
4 files changed, 147 insertions, 52 deletions
diff --git a/extension/src/openvic-extension/singletons/AssetManager.cpp b/extension/src/openvic-extension/singletons/AssetManager.cpp index 083d934..6646c8b 100644 --- a/extension/src/openvic-extension/singletons/AssetManager.cpp +++ b/extension/src/openvic-extension/singletons/AssetManager.cpp @@ -13,9 +13,14 @@ using OpenVic::Utilities::godot_to_std_string; using OpenVic::Utilities::std_to_godot_string; void AssetManager::_bind_methods() { - OV_BIND_METHOD(AssetManager::get_image, { "path", "cache", "flip_y" }, DEFVAL(true), DEFVAL(false)); - OV_BIND_METHOD(AssetManager::get_texture, { "path", "flip_y" }, DEFVAL(false)); + OV_BIND_METHOD(AssetManager::get_image, { "path", "load_flags" }, DEFVAL(LOAD_FLAG_CACHE_IMAGE)); + OV_BIND_METHOD(AssetManager::get_texture, { "path", "load_flags" }, DEFVAL(LOAD_FLAG_CACHE_TEXTURE)); OV_BIND_METHOD(AssetManager::get_font, { "name" }); + + BIND_ENUM_CONSTANT(LOAD_FLAG_NONE); + BIND_ENUM_CONSTANT(LOAD_FLAG_CACHE_IMAGE); + BIND_ENUM_CONSTANT(LOAD_FLAG_CACHE_TEXTURE); + BIND_ENUM_CONSTANT(LOAD_FLAG_FLIP_Y); } AssetManager* AssetManager::get_singleton() { @@ -32,55 +37,95 @@ AssetManager::~AssetManager() { _singleton = nullptr; } -Ref<Image> AssetManager::_load_image(StringName const& path) { +Ref<Image> AssetManager::_load_image(StringName const& path, bool flip_y) { GameSingleton* game_singleton = GameSingleton::get_singleton(); ERR_FAIL_NULL_V(game_singleton, nullptr); + const String lookedup_path = std_to_godot_string(game_singleton->get_dataloader().lookup_image_file(godot_to_std_string(path)).string()); ERR_FAIL_COND_V_MSG(lookedup_path.is_empty(), nullptr, vformat("Failed to look up image: %s", path)); + const Ref<Image> image = Utilities::load_godot_image(lookedup_path); ERR_FAIL_COND_V_MSG( - image.is_null() || image->is_empty(), nullptr, vformat("Failed to load image: %s (looked up: %s)", path, lookedup_path) + image.is_null() || image->is_empty(), nullptr, + vformat("Failed to load image: %s (looked up: %s)", path, lookedup_path) ); - return image; -} -AssetManager::image_asset_t* AssetManager::_get_image_asset(StringName const& path, bool flip_y) { - image_asset_map_t::iterator it = image_assets.find(path); - if (it != image_assets.end()) { - return &it.value(); - } - const Ref<Image> image = _load_image(path); - ERR_FAIL_NULL_V(image, nullptr); if (flip_y) { image->flip_y(); } - return &image_assets.emplace(std::move(path), AssetManager::image_asset_t { image, nullptr }).first.value(); + + return image; } -Ref<Image> AssetManager::get_image(StringName const& path, bool cache, bool flip_y) { - if (cache) { - image_asset_t const* asset = _get_image_asset(path, flip_y); - ERR_FAIL_NULL_V(asset, nullptr); - return asset->image; +Ref<Image> AssetManager::get_image(StringName const& path, LoadFlags load_flags) { + /* Check for an existing image entry indicating a previous load attempt, whether successful or not. */ + const image_asset_map_t::iterator it = image_assets.find(path); + if (it != image_assets.end()) { + std::optional<Ref<Image>> const& cached_image = it->second.image; + + if (cached_image.has_value()) { + ERR_FAIL_NULL_V_MSG(*cached_image, nullptr, vformat("Failed to load image previously: %s", path)); + + return *cached_image; + } + } + + /* No load attempt has been made yet, so we try now. */ + const Ref<Image> image = _load_image(path, load_flags & LOAD_FLAG_FLIP_Y); + + if (image.is_valid()) { + if (load_flags & LOAD_FLAG_CACHE_IMAGE) { + image_assets[path].image = image; + } + + return image; } else { - return _load_image(path); + /* Mark both image and texture as failures, regardless of cache flags, in case of future load/creation attempts. */ + image_assets[path] = { nullptr, nullptr }; + + ERR_FAIL_V_MSG(nullptr, vformat("Failed to load image: %s", path)); } } -Ref<ImageTexture> AssetManager::get_texture(StringName const& path, bool flip_y) { - image_asset_t* asset = _get_image_asset(path, flip_y); - ERR_FAIL_NULL_V(asset, nullptr); - if (asset->texture.is_null()) { - asset->texture = ImageTexture::create_from_image(asset->image); - ERR_FAIL_NULL_V_MSG(asset->texture, nullptr, vformat("Failed to turn image into texture: %s", path)); +Ref<ImageTexture> AssetManager::get_texture(StringName const& path, LoadFlags load_flags) { + /* Check for an existing texture entry indicating a previous creation attempt, whether successful or not. */ + const image_asset_map_t::const_iterator it = image_assets.find(path); + if (it != image_assets.end()) { + std::optional<Ref<ImageTexture>> const& cached_texture = it->second.texture; + + if (cached_texture.has_value()) { + ERR_FAIL_NULL_V_MSG(*cached_texture, nullptr, vformat("Failed to create texture previously: %s", path)); + + return *cached_texture; + } + } + + /* No creation attempt has yet been made, so we try now starting by finding the corresponding image. */ + const Ref<Image> image = get_image(path, load_flags); + ERR_FAIL_NULL_V_MSG(image, nullptr, vformat("Failed to load image for texture: %s", path)); + + const Ref<ImageTexture> texture = ImageTexture::create_from_image(image); + + if (texture.is_valid()) { + if (load_flags & LOAD_FLAG_CACHE_TEXTURE) { + image_assets[path].texture = texture; + } + + return texture; + } else { + /* Mark texture as a failure, regardless of cache flags, in case of future creation attempts. */ + image_assets[path].texture = nullptr; + + ERR_FAIL_V_MSG(nullptr, vformat("Failed to create texture: %s", path)); } - return asset->texture; } Ref<Font> AssetManager::get_font(StringName const& name) { const font_map_t::const_iterator it = fonts.find(name); if (it != fonts.end()) { + ERR_FAIL_NULL_V_MSG(it->second, nullptr, vformat("Failed to load font previously: %s", name)); + return it->second; } @@ -89,18 +134,35 @@ Ref<Font> AssetManager::get_font(StringName const& name) { static const String image_ext = ".tga"; const StringName image_path = font_dir + name + image_ext; - const Ref<Image> image = get_image(image_path); - ERR_FAIL_NULL_V_MSG(image, nullptr, vformat("Failed to load font image %s for the font named %s", image_path, name)); + const Ref<Image> image = get_image(image_path, LOAD_FLAG_NONE); + if (image.is_null()) { + fonts.emplace(name, nullptr); + + ERR_FAIL_V_MSG(nullptr, vformat("Failed to load font image %s for the font named %s", image_path, name)); + } + GameSingleton* game_singleton = GameSingleton::get_singleton(); ERR_FAIL_NULL_V(game_singleton, nullptr); + const String font_path = font_dir + name + font_ext; const String lookedup_font_path = std_to_godot_string(game_singleton->get_dataloader().lookup_file(godot_to_std_string(font_path)).string()); + if (lookedup_font_path.is_empty()) { + fonts.emplace(name, nullptr); + + ERR_FAIL_V_MSG(nullptr, vformat("Failed to look up font: %s", font_path)); + } + const Ref<Font> font = Utilities::load_godot_font(lookedup_font_path, image); - ERR_FAIL_NULL_V_MSG( - font, nullptr, - vformat("Failed to load font file %s (looked up: %s) for the font named %s", font_path, lookedup_font_path, name) - ); - fonts.emplace(std::move(name), font); + if (font.is_null()) { + fonts.emplace(name, nullptr); + + ERR_FAIL_V_MSG( + nullptr, + vformat("Failed to load font file %s (looked up: %s) for the font named %s", font_path, lookedup_font_path, name) + ); + } + + fonts.emplace(name, font); return font; } diff --git a/extension/src/openvic-extension/singletons/AssetManager.hpp b/extension/src/openvic-extension/singletons/AssetManager.hpp index c718d2a..0856d05 100644 --- a/extension/src/openvic-extension/singletons/AssetManager.hpp +++ b/extension/src/openvic-extension/singletons/AssetManager.hpp @@ -13,9 +13,22 @@ namespace OpenVic { static inline AssetManager* _singleton = nullptr; + public: + enum LoadFlags { + LOAD_FLAG_NONE = 0, + LOAD_FLAG_CACHE_IMAGE = 1 << 0, + LOAD_FLAG_CACHE_TEXTURE = 1 << 1, + LOAD_FLAG_FLIP_Y = 1 << 2 + }; + + constexpr friend LoadFlags operator|(LoadFlags lhs, LoadFlags rhs) { + return static_cast<LoadFlags>(static_cast<int>(lhs) | static_cast<int>(rhs)); + } + + private: struct image_asset_t { - godot::Ref<godot::Image> image; - godot::Ref<godot::ImageTexture> texture; + std::optional<godot::Ref<godot::Image>> image; + std::optional<godot::Ref<godot::ImageTexture>> texture; }; /* deque_ordered_map to avoid the need to reallocate. */ using image_asset_map_t = deque_ordered_map<godot::StringName, image_asset_t>; @@ -24,8 +37,7 @@ namespace OpenVic { image_asset_map_t image_assets; font_map_t fonts; - static godot::Ref<godot::Image> _load_image(godot::StringName const& path); - image_asset_t* _get_image_asset(godot::StringName const& path, bool flip_y); + static godot::Ref<godot::Image> _load_image(godot::StringName const& path, bool flip_y); protected: static void _bind_methods(); @@ -37,16 +49,27 @@ namespace OpenVic { ~AssetManager(); /* Search for and load an image at the specified path relative to the game defines, first checking the AssetManager's - * image cache (if cache is true) in case it has already been loaded, and returning nullptr if image loading fails. */ - godot::Ref<godot::Image> get_image(godot::StringName const& path, bool cache = true, bool flip_y = false); + * image cache in case it has already been loaded, and returning nullptr if image loading fails. If the cache image + * load flag is set then the loaded image will be stored in the AssetManager's image cache for future access; if the + * flip y load flag is set then the image will be flipped vertically before being returned (if the image is already + * in the cache then no flipping will occur, regardless of whether it was orginally flipped or not). */ + godot::Ref<godot::Image> get_image(godot::StringName const& path, LoadFlags load_flags = LOAD_FLAG_CACHE_IMAGE); - /* Create a texture from an image found at the specified path relative to the game defines, fist checking - * AssetManager's texture cache in case it has already been loaded, and returning nullptr if image loading - * or texture creation fails. */ - godot::Ref<godot::ImageTexture> get_texture(godot::StringName const& path, bool flip_y = false); + /* Create a texture from an image found at the specified path relative to the game defines, fist checking the + * AssetManager's texture cache in case it has already been loaded, and returning nullptr if image loading or texture + * creation fails. If the cache image load flag is set then the loaded image will be stored in the AssetManager's + * image cache for future access; if the cache texture load flag is set then the created texture will be stored in the + * AssetManager's texture cache for future access; if the flip y load flag is set then the image will be flipped + * vertically before being used to create the texture (if the image is already in the cache then no flipping will + * occur, regardless of whether it was orginally flipped or not). */ + godot::Ref<godot::ImageTexture> get_texture( + godot::StringName const& path, LoadFlags load_flags = LOAD_FLAG_CACHE_TEXTURE + ); /* Search for and load a font with the specified name from the game defines' font directory, first checking the * AssetManager's font cache in case it has already been loaded, and returning nullptr if font loading fails. */ godot::Ref<godot::Font> get_font(godot::StringName const& name); }; } + +VARIANT_ENUM_CAST(OpenVic::AssetManager::LoadFlags); diff --git a/extension/src/openvic-extension/singletons/GameSingleton.cpp b/extension/src/openvic-extension/singletons/GameSingleton.cpp index df0e9a0..b8aef4f 100644 --- a/extension/src/openvic-extension/singletons/GameSingleton.cpp +++ b/extension/src/openvic-extension/singletons/GameSingleton.cpp @@ -339,7 +339,7 @@ Error GameSingleton::_load_terrain_variants() { AssetManager* asset_manager = AssetManager::get_singleton(); ERR_FAIL_NULL_V(asset_manager, FAILED); // Load the terrain texture sheet and prepare to slice it up - Ref<Image> terrain_sheet = asset_manager->get_image(terrain_texturesheet_path); + Ref<Image> terrain_sheet = asset_manager->get_image(terrain_texturesheet_path, AssetManager::LOAD_FLAG_NONE); ERR_FAIL_NULL_V_MSG(terrain_sheet, FAILED, vformat("Failed to load terrain texture sheet: %s", terrain_texturesheet_path)); static constexpr int32_t SHEET_DIMS = 8, SHEET_SIZE = SHEET_DIMS * SHEET_DIMS; diff --git a/extension/src/openvic-extension/utility/UITools.cpp b/extension/src/openvic-extension/utility/UITools.cpp index 4af2b74..510c6da 100644 --- a/extension/src/openvic-extension/utility/UITools.cpp +++ b/extension/src/openvic-extension/utility/UITools.cpp @@ -187,10 +187,12 @@ static bool generate_icon(generate_gui_args_t&& args) { GFX::ProgressBar const* progress_bar = icon.get_sprite()->cast_to<GFX::ProgressBar>(); + using enum AssetManager::LoadFlags; + Ref<ImageTexture> back_texture; if (!progress_bar->get_back_texture_file().empty()) { const StringName back_texture_file = std_view_to_godot_string_name(progress_bar->get_back_texture_file()); - back_texture = args.asset_manager.get_texture(back_texture_file, true); + back_texture = args.asset_manager.get_texture(back_texture_file, LOAD_FLAG_CACHE_TEXTURE | LOAD_FLAG_FLIP_Y); if (back_texture.is_null()) { UtilityFunctions::push_error( "Failed to load progress bar sprite back texture ", back_texture_file, " for GUI icon ", icon_name @@ -221,11 +223,14 @@ static bool generate_icon(generate_gui_args_t&& args) { Ref<ImageTexture> progress_texture; if (!progress_bar->get_progress_texture_file().empty()) { - const StringName progress_texture_file = std_view_to_godot_string_name(progress_bar->get_progress_texture_file()); - progress_texture = args.asset_manager.get_texture(progress_texture_file, true); + const StringName progress_texture_file = + std_view_to_godot_string_name(progress_bar->get_progress_texture_file()); + progress_texture = + args.asset_manager.get_texture(progress_texture_file, LOAD_FLAG_CACHE_TEXTURE | LOAD_FLAG_FLIP_Y); if (progress_texture.is_null()) { UtilityFunctions::push_error( - "Failed to load progress bar sprite progress texture ", progress_texture_file, " for GUI icon ", icon_name + "Failed to load progress bar sprite progress texture ", progress_texture_file, " for GUI icon ", + icon_name ); ret = false; } @@ -237,7 +242,8 @@ static bool generate_icon(generate_gui_args_t&& args) { ); if (progress_texture.is_null()) { UtilityFunctions::push_error( - "Failed to generate progress bar sprite ", progress_colour, " progress texture for GUI icon ", icon_name + "Failed to generate progress bar sprite ", progress_colour, " progress texture for GUI icon ", + icon_name ); ret = false; } @@ -252,7 +258,9 @@ static bool generate_icon(generate_gui_args_t&& args) { } // TODO - work out why progress bar is missing bottom border pixel (e.g. province building expansion bar) - godot_progress_bar->set_custom_minimum_size(Utilities::to_godot_fvec2(static_cast<fvec2_t>(progress_bar->get_size()))); + godot_progress_bar->set_custom_minimum_size( + Utilities::to_godot_fvec2(static_cast<fvec2_t>(progress_bar->get_size())) + ); args.result = godot_progress_bar; } else if (icon.get_sprite()->is_type<GFX::PieChart>()) { @@ -286,7 +294,9 @@ static bool generate_icon(generate_gui_args_t&& args) { const float rotation = icon.get_rotation(); if (rotation != 0.0f) { args.result->set_position( - args.result->get_position() - args.result->get_custom_minimum_size().height * Vector2 { sin(rotation), cos(rotation) - 1.0f } + args.result->get_position() - args.result->get_custom_minimum_size().height * Vector2 { + sin(rotation), cos(rotation) - 1.0f + } ); args.result->set_rotation(-rotation); } |