diff options
Diffstat (limited to 'extension')
-rw-r--r-- | extension/deps/SCsub | 9 | ||||
m--------- | extension/deps/gli | 0 | ||||
m--------- | extension/deps/openvic-simulation | 0 | ||||
-rw-r--r-- | extension/src/openvic-extension/GameSingleton.cpp | 173 | ||||
-rw-r--r-- | extension/src/openvic-extension/GameSingleton.hpp | 25 | ||||
-rw-r--r-- | extension/src/openvic-extension/LoadGameCompatibility.cpp | 115 | ||||
-rw-r--r-- | extension/src/openvic-extension/LoadGameOpenVic.cpp | 132 | ||||
-rw-r--r-- | extension/src/openvic-extension/Utilities.cpp | 38 |
8 files changed, 209 insertions, 283 deletions
diff --git a/extension/deps/SCsub b/extension/deps/SCsub index 25a3286..6a7a48d 100644 --- a/extension/deps/SCsub +++ b/extension/deps/SCsub @@ -9,4 +9,11 @@ def build_openvic_simulation(env): env.Append(CPPPATH=ovsim_env.openvic_simulation["INCPATH"]) env.openvic_simulation = ovsim_env.openvic_simulation -build_openvic_simulation(env)
\ No newline at end of file +def build_gli(env): + gli_includes = ["gli", "gli/external"] + env.gli_loader = {} + env.gli_loader["INCPATH"] = [env.Dir(p) for p in gli_includes] + env.Append(CPPPATH=env.gli_loader["INCPATH"]) + +build_openvic_simulation(env) +build_gli(env) diff --git a/extension/deps/gli b/extension/deps/gli new file mode 160000 +Subproject 779b99ac6656e4d30c3b24e96e0136a59649a86 diff --git a/extension/deps/openvic-simulation b/extension/deps/openvic-simulation -Subproject 05b6db7305398e12363f727a50315972cc9a5a5 +Subproject 04795924456062db1631686a90f13d963791ad3 diff --git a/extension/src/openvic-extension/GameSingleton.cpp b/extension/src/openvic-extension/GameSingleton.cpp index 2d6f784..ade6ec9 100644 --- a/extension/src/openvic-extension/GameSingleton.cpp +++ b/extension/src/openvic-extension/GameSingleton.cpp @@ -4,20 +4,12 @@ #include <openvic-simulation/utility/Logger.hpp> +#include "openvic-extension/LoadLocalisation.hpp" #include "openvic-extension/Utilities.hpp" using namespace godot; using namespace OpenVic; -TerrainVariant::TerrainVariant(const std::string_view new_identfier, - colour_t new_colour, Ref<Image> const& new_image) - : HasIdentifierAndColour { new_identfier, new_colour, true, false }, - image { new_image } {} - -Ref<Image> TerrainVariant::get_image() const { - return image; -} - GameSingleton* GameSingleton::singleton = nullptr; void GameSingleton::_bind_methods() { @@ -60,6 +52,7 @@ void GameSingleton::_bind_methods() { ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_province_key"), &GameSingleton::get_province_info_province_key); ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_region_key"), &GameSingleton::get_province_info_region_key); ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_life_rating_key"), &GameSingleton::get_province_info_life_rating_key); + ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_terrain_type_key"), &GameSingleton::get_province_info_terrain_type_key); ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_total_population_key"), &GameSingleton::get_province_info_total_population_key); ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_pop_types_key"), &GameSingleton::get_province_info_pop_types_key); ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_pop_ideologies_key"), &GameSingleton::get_province_info_pop_ideologies_key); @@ -81,6 +74,7 @@ void GameSingleton::_bind_methods() { "shadow_displacement", "shadow_tightness", "shadow_radius", "shadow_thickness", "trim_colour", "trim_size", "gradient_falloff", "gradient_base", "donut", "donut_inner_trim", "donut_inner_radius"), &GameSingleton::draw_pie_chart); + ClassDB::bind_static_method("GameSingleton", D_METHOD("load_image", "path"), &GameSingleton::load_image); } void GameSingleton::draw_pie_chart(Ref<Image> image, @@ -94,6 +88,10 @@ void GameSingleton::draw_pie_chart(Ref<Image> image, donut, donut_inner_trim, donut_inner_radius); } +Ref<Image> GameSingleton::load_image(String const& path) { + return load_godot_image(path); +} + GameSingleton* GameSingleton::get_singleton() { return singleton; } @@ -106,8 +104,7 @@ void GameSingleton::_on_state_updated() { /* REQUIREMENTS: * MAP-21, MAP-23, MAP-25, MAP-32, MAP-33, MAP-34 */ -GameSingleton::GameSingleton() : game_manager { [this]() { _on_state_updated(); } }, - terrain_variants { "terrain variants" } { +GameSingleton::GameSingleton() : game_manager { [this]() { _on_state_updated(); } } { ERR_FAIL_COND(singleton != nullptr); singleton = this; } @@ -147,6 +144,10 @@ StringName const& GameSingleton::get_province_info_life_rating_key() { static const StringName key = "life_rating"; return key; } +StringName const& GameSingleton::get_province_info_terrain_type_key() { + static const StringName key = "terrain_type"; + return key; +} StringName const& GameSingleton::get_province_info_total_population_key() { static const StringName key = "total_population"; return key; @@ -231,6 +232,10 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const { if (rgo != nullptr) ret[get_province_info_rgo_key()] = std_to_godot_string(rgo->get_identifier()); ret[get_province_info_life_rating_key()] = province->get_life_rating(); + + TerrainType const* terrain_type = province->get_terrain_type(); + if (terrain_type != nullptr) ret[get_province_info_terrain_type_key()] = std_to_godot_string(terrain_type->get_identifier()); + ret[get_province_info_total_population_key()] = province->get_total_population(); distribution_t const& pop_types = province->get_pop_type_distribution(); if (!pop_types.empty()) ret[get_province_info_pop_types_key()] = _distribution_to_dictionary(pop_types); @@ -239,16 +244,16 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const { distribution_t const& cultures = province->get_culture_distribution(); if (!cultures.empty()) ret[get_province_info_pop_cultures_key()] = _distribution_to_dictionary(cultures); - std::vector<Building> const& buildings = province->get_buildings(); + std::vector<BuildingInstance> const& buildings = province->get_buildings(); if (!buildings.empty()) { Array buildings_array; buildings_array.resize(buildings.size()); for (size_t idx = 0; idx < buildings.size(); ++idx) { - Building const& building = buildings[idx]; + BuildingInstance const& building = buildings[idx]; Dictionary building_dict; building_dict[get_building_info_building_key()] = std_to_godot_string(building.get_identifier()); - building_dict[get_building_info_level_key()] = static_cast<int32_t>(building.get_level()); + building_dict[get_building_info_level_key()] = static_cast<int32_t>(building.get_current_level()); building_dict[get_building_info_expansion_state_key()] = static_cast<int32_t>(building.get_expansion_state()); building_dict[get_building_info_start_date_key()] = std_to_godot_string(building.get_start_date().to_string()); building_dict[get_building_info_end_date_key()] = std_to_godot_string(building.get_end_date().to_string()); @@ -388,3 +393,143 @@ String GameSingleton::get_longform_date() const { void GameSingleton::try_tick() { game_manager.get_clock().conditionallyAdvanceGame(); } + +Error GameSingleton::_load_map_images(bool flip_vertical) { + if (province_shape_texture.is_valid()) { + UtilityFunctions::push_error("Map images have already been loaded!"); + return FAILED; + } + + Error err = OK; + + const Vector2i province_dims { + static_cast<int32_t>(game_manager.get_map().get_width()), + static_cast<int32_t>(game_manager.get_map().get_height()) }; + + static constexpr int32_t GPU_DIM_LIMIT = 0x3FFF; + // For each dimension of the image, this finds the small number of equal subdivisions required get the individual texture dims under GPU_DIM_LIMIT + for (int i = 0; i < 2; ++i) + for (image_subdivisions[i] = 1; province_dims[i] / image_subdivisions[i] > GPU_DIM_LIMIT || + province_dims[i] % image_subdivisions[i] != 0; ++image_subdivisions[i]); + + Map::shape_pixel_t const* province_shape_data = game_manager.get_map().get_province_shape_image().data(); + const Vector2i divided_dims = province_dims / image_subdivisions; + Array province_shape_images; + province_shape_images.resize(image_subdivisions.x * image_subdivisions.y); + for (int32_t v = 0; v < image_subdivisions.y; ++v) { + for (int32_t u = 0; u < image_subdivisions.x; ++u) { + PackedByteArray index_data_array; + index_data_array.resize(divided_dims.x * divided_dims.y * sizeof(Map::shape_pixel_t)); + + for (int32_t y = 0; y < divided_dims.y; ++y) + memcpy(index_data_array.ptrw() + y * divided_dims.x * sizeof(Map::shape_pixel_t), + province_shape_data + (v * divided_dims.y + y) * province_dims.x + u * divided_dims.x, + divided_dims.x * sizeof(Map::shape_pixel_t)); + + const Ref<Image> province_shape_subimage = Image::create_from_data(divided_dims.x, divided_dims.y, false, Image::FORMAT_RGB8, index_data_array); + if (province_shape_subimage.is_null()) { + UtilityFunctions::push_error("Failed to create province shape image (", u, ", ", v, ")"); + err = FAILED; + } + province_shape_images[u + v * image_subdivisions.x] = province_shape_subimage; + } + } + + province_shape_texture.instantiate(); + if (province_shape_texture->create_from_images(province_shape_images) != OK) { + UtilityFunctions::push_error("Failed to create terrain texture array!"); + err = FAILED; + } + + if (_update_colour_image() != OK) err = FAILED; + + return err; +} + +Error GameSingleton::_load_terrain_variants_compatibility_mode(String const& terrain_texturesheet_path) { + static constexpr int32_t SHEET_DIMS = 8, SHEET_SIZE = SHEET_DIMS * SHEET_DIMS; + + // Load the terrain texture sheet and prepare to slice it up + Ref<Image> terrain_sheet = load_godot_image(terrain_texturesheet_path); + if (terrain_sheet.is_null()) { + UtilityFunctions::push_error("Failed to load terrain texture sheet: ", terrain_texturesheet_path); + return FAILED; + } + terrain_sheet->flip_y(); + const int32_t sheet_width = terrain_sheet->get_width(), sheet_height = terrain_sheet->get_height(); + if (sheet_width < 1 || sheet_width % SHEET_DIMS != 0 || sheet_width != sheet_height) { + UtilityFunctions::push_error("Invalid terrain texture sheet dims: ", sheet_width, "x", sheet_height, " (must be square with dims positive multiples of ", SHEET_DIMS, ")"); + return FAILED; + } + const int32_t slice_size = sheet_width / SHEET_DIMS; + + Array terrain_images; + { + static constexpr colour_t TERRAIN_WATER_INDEX_COLOUR = 0xFFFFFF; + Ref<Image> water_image = Image::create(slice_size, slice_size, false, terrain_sheet->get_format()); + ERR_FAIL_NULL_V_EDMSG(water_image, FAILED, "Failed to create water terrain image"); + water_image->fill({ 0.1f, 0.1f, 0.5f }); + terrain_images.append(water_image); + } + Error err = OK; + for (int32_t idx = 0; idx < SHEET_SIZE; ++idx) { + const Rect2i slice { (idx % SHEET_DIMS) * slice_size, (7 - (idx / SHEET_DIMS)) * slice_size, slice_size, slice_size }; + const Ref<Image> terrain_image = terrain_sheet->get_region(slice); + if (terrain_image.is_null() || terrain_image->is_empty()) { + UtilityFunctions::push_error("Failed to extract terrain texture slice ", slice, " from ", terrain_texturesheet_path); + err = FAILED; + } + terrain_images.append(terrain_image); + } + + terrain_texture.instantiate(); + if (terrain_texture->create_from_images(terrain_images) != OK) { + UtilityFunctions::push_error("Failed to create terrain texture array!"); + return FAILED; + } + return err; +} + +Error GameSingleton::load_defines_compatibility_mode(PackedStringArray const& file_paths) { + static const fs::path terrain_texture_file = "map/terrain/texturesheet.tga"; + + Dataloader::path_vector_t roots; + for (String const& path : file_paths) { + roots.push_back(godot_to_std_string(path)); + } + + Error err = OK; + + if (!dataloader.set_roots(roots)) { + Logger::error("Failed to set dataloader roots!"); + err = FAILED; + } + if (!dataloader.load_defines(game_manager)) { + UtilityFunctions::push_error("Failed to load defines!"); + err = FAILED; + } + if (_load_terrain_variants_compatibility_mode( + std_to_godot_string(dataloader.lookup_file(terrain_texture_file).string()) + ) != OK) { + UtilityFunctions::push_error("Failed to load terrain variants!"); + err = FAILED; + } + if (_load_map_images(true) != OK) { + UtilityFunctions::push_error("Failed to load map images!"); + err = FAILED; + } + if (!game_manager.load_hardcoded_defines()) { + UtilityFunctions::push_error("Failed to hardcoded defines!"); + err = FAILED; + } + if (!dataloader.load_localisation_files(LoadLocalisation::add_message)) { + UtilityFunctions::push_error("Failed to load localisation!"); + err = FAILED; + } + + return err; +} + +String GameSingleton::lookup_file(String const& path) const { + return std_to_godot_string(dataloader.lookup_file(godot_to_std_string(path)).string()); +} diff --git a/extension/src/openvic-extension/GameSingleton.hpp b/extension/src/openvic-extension/GameSingleton.hpp index 4d9e912..56c9e88 100644 --- a/extension/src/openvic-extension/GameSingleton.hpp +++ b/extension/src/openvic-extension/GameSingleton.hpp @@ -8,23 +8,6 @@ namespace OpenVic { - struct TerrainVariant : HasIdentifierAndColour { - friend class GameSingleton; - - private: - const godot::Ref<godot::Image> image; - - TerrainVariant(const std::string_view new_identfier, colour_t new_colour, - godot::Ref<godot::Image> const& new_image); - - public: - static constexpr size_t MAX_TERRIN_VARIANT_COUNT = 1 << (8 * sizeof(Map::terrain_t)); - - TerrainVariant(TerrainVariant&&) = default; - - godot::Ref<godot::Image> get_image() const; - }; - class GameSingleton : public godot::Object { GDCLASS(GameSingleton, godot::Object) @@ -38,14 +21,12 @@ namespace OpenVic { godot::Ref<godot::Image> province_colour_image; godot::Ref<godot::ImageTexture> province_colour_texture; Mapmode::index_t mapmode_index = 0; - IdentifierRegistry<TerrainVariant> terrain_variants; - Map::terrain_variant_map_t terrain_variant_map; godot::Ref<godot::Texture2DArray> terrain_texture; godot::Error _generate_terrain_texture_array(); - godot::Error _load_map_images(godot::String const& province_image_path, godot::String const& terrain_image_path, bool flip_vertical = false); + godot::Error _load_map_images(bool flip_vertical = false); - godot::Error _load_terrain_variants_compatibility_mode(godot::String const& terrain_image_path, godot::String const& terrain_texturesheet_path); + godot::Error _load_terrain_variants_compatibility_mode(godot::String const& terrain_texturesheet_path); /* Generate the province_colour_texture from the current mapmode. */ @@ -63,6 +44,7 @@ namespace OpenVic { godot::Vector2 shadow_displacement, float shadow_tightness, float shadow_radius, float shadow_thickness, godot::Color trim_colour, float trim_size, float gradient_falloff, float gradient_base, bool donut, bool donut_inner_trim, float donut_inner_radius); + static godot::Ref<godot::Image> load_image(godot::String const& path); static GameSingleton* get_singleton(); @@ -88,6 +70,7 @@ namespace OpenVic { static godot::StringName const& get_province_info_province_key(); static godot::StringName const& get_province_info_region_key(); static godot::StringName const& get_province_info_life_rating_key(); + static godot::StringName const& get_province_info_terrain_type_key(); static godot::StringName const& get_province_info_total_population_key(); static godot::StringName const& get_province_info_pop_types_key(); static godot::StringName const& get_province_info_pop_ideologies_key(); diff --git a/extension/src/openvic-extension/LoadGameCompatibility.cpp b/extension/src/openvic-extension/LoadGameCompatibility.cpp deleted file mode 100644 index b696315..0000000 --- a/extension/src/openvic-extension/LoadGameCompatibility.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "GameSingleton.hpp" - -#include <godot_cpp/classes/file_access.hpp> -#include <godot_cpp/variant/utility_functions.hpp> - -#include <openvic-simulation/utility/BMP.hpp> - -#include "openvic-extension/LoadLocalisation.hpp" -#include "openvic-extension/Utilities.hpp" - -using namespace godot; -using namespace OpenVic; - -Error GameSingleton::_load_terrain_variants_compatibility_mode(String const& terrain_image_path, String const& terrain_texturesheet_path) { - // Read BMP's palette to determine terrain variant colours which texture they're associated with - BMP bmp; - if (!(bmp.open(godot_to_std_string(terrain_image_path).c_str()) && bmp.read_header() && bmp.read_palette())) { - UtilityFunctions::push_error("Failed to read BMP palette from compatibility mode terrain image: ", terrain_image_path); - return FAILED; - } - std::vector<colour_t> const& palette = bmp.get_palette(); - static constexpr int32_t SHEET_DIMS = 8, PALETTE_SIZE = SHEET_DIMS * SHEET_DIMS; - if (palette.size() == 0 || palette.size() < PALETTE_SIZE) { - UtilityFunctions::push_error("Invalid BMP palette size for terrain image: ", static_cast<uint64_t>(palette.size()), " (expected ", PALETTE_SIZE, ")"); - return FAILED; - } - - // Load the terrain texture sheet and prepare to slice it up - Ref<Image> terrain_sheet = load_godot_image(terrain_texturesheet_path); - if (terrain_sheet.is_null()) { - UtilityFunctions::push_error("Failed to load terrain texture sheet: ", terrain_texturesheet_path); - return FAILED; - } - terrain_sheet->flip_y(); - const int32_t sheet_width = terrain_sheet->get_width(), sheet_height = terrain_sheet->get_height(); - if (sheet_width < 1 || sheet_width % SHEET_DIMS != 0 || sheet_width != sheet_height) { - UtilityFunctions::push_error("Invalid terrain texture sheet dims: ", sheet_width, "x", sheet_height, " (must be square with dims positive multiples of ", SHEET_DIMS, ")"); - return FAILED; - } - const int32_t slice_size = sheet_width / SHEET_DIMS; - - { - static constexpr colour_t TERRAIN_WATER_INDEX_COLOUR = 0xFFFFFF; - Ref<Image> water_image = Image::create(slice_size, slice_size, false, terrain_sheet->get_format()); - ERR_FAIL_NULL_V_EDMSG(water_image, FAILED, "Failed to create water terrain image"); - water_image->fill({ 0.1f, 0.1f, 0.5f }); - terrain_variants.add_item({ "terrain_water", TERRAIN_WATER_INDEX_COLOUR, water_image }); - } - Error err = OK; - for (int32_t idx = 0; idx < PALETTE_SIZE; ++idx) { - const Rect2i slice { (idx % SHEET_DIMS) * slice_size, (7 - (idx / SHEET_DIMS)) * slice_size, slice_size, slice_size }; - const Ref<Image> terrain_image = terrain_sheet->get_region(slice); - if (terrain_image.is_null() || terrain_image->is_empty()) { - UtilityFunctions::push_error("Failed to extract terrain texture slice ", slice, " from ", terrain_texturesheet_path); - err = FAILED; - continue; - } - if (!terrain_variants.add_item({ "terrain_" + std::to_string(idx), palette[idx], terrain_image })) err = FAILED; - } - terrain_variants.lock(); - if (_generate_terrain_texture_array() != OK) return FAILED; - return err; -} - -Error GameSingleton::load_defines_compatibility_mode(PackedStringArray const& file_paths) { - static const fs::path province_image_file = "map/provinces.bmp"; - static const fs::path terrain_image_file = "map/terrain.bmp"; - static const fs::path terrain_texture_file = "map/terrain/texturesheet.tga"; - - Dataloader::path_vector_t roots; - for (String const& path : file_paths) { - roots.push_back(godot_to_std_string(path)); - } - - Error err = OK; - - if (!dataloader.set_roots(roots)) { - Logger::error("Failed to set dataloader roots!"); - err = FAILED; - } - - if (!dataloader.load_defines(game_manager)) { - UtilityFunctions::push_error("Failed to load defines!"); - err = FAILED; - } - - if (_load_terrain_variants_compatibility_mode( - std_to_godot_string(dataloader.lookup_file(terrain_image_file).string()), - std_to_godot_string(dataloader.lookup_file(terrain_texture_file).string()) - ) != OK) { - UtilityFunctions::push_error("Failed to load terrain variants!"); - err = FAILED; - } - if (_load_map_images( - std_to_godot_string(dataloader.lookup_file(province_image_file).string()), - std_to_godot_string(dataloader.lookup_file(terrain_image_file).string()), - true) != OK) { - UtilityFunctions::push_error("Failed to load map images!"); - err = FAILED; - } - if (!game_manager.load_hardcoded_defines()) { - UtilityFunctions::push_error("Failed to hardcoded defines!"); - err = FAILED; - } - if (!dataloader.load_localisation_files(LoadLocalisation::add_message)) { - UtilityFunctions::push_error("Failed to load localisation!"); - err = FAILED; - } - - return err; -} - -String GameSingleton::lookup_file(String const& path) const { - return std_to_godot_string(dataloader.lookup_file(godot_to_std_string(path)).string()); -} diff --git a/extension/src/openvic-extension/LoadGameOpenVic.cpp b/extension/src/openvic-extension/LoadGameOpenVic.cpp deleted file mode 100644 index c34411c..0000000 --- a/extension/src/openvic-extension/LoadGameOpenVic.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "GameSingleton.hpp" - -#include <godot_cpp/classes/file_access.hpp> -#include <godot_cpp/classes/json.hpp> -#include <godot_cpp/variant/utility_functions.hpp> - -#include "openvic-extension/Utilities.hpp" - -using namespace godot; -using namespace OpenVic; - -Error GameSingleton::_generate_terrain_texture_array() { - Error err = OK; - if (terrain_variants.size() == 0) { - UtilityFunctions::push_error("Failed to load terrain textures!"); - return FAILED; - } - // TerrainVariant count is limited by the data type representing it in the map image - if (terrain_variants.size() > TerrainVariant::MAX_TERRIN_VARIANT_COUNT) { - UtilityFunctions::push_error("Too many terrain textures - all after the first ", - static_cast<uint64_t>(TerrainVariant::MAX_TERRIN_VARIANT_COUNT), " will be ignored"); - err = FAILED; - } - - Array terrain_images; - for (size_t i = 0; i < terrain_variants.size() && i < TerrainVariant::MAX_TERRIN_VARIANT_COUNT; ++i) { - TerrainVariant const& var = *terrain_variants.get_item_by_index(i); - terrain_variant_map[var.get_colour()] = i; - terrain_images.append(var.get_image()); - } - - terrain_texture.instantiate(); - if (terrain_texture->create_from_images(terrain_images) != OK) { - UtilityFunctions::push_error("Failed to create terrain texture array!"); - return FAILED; - } - return err; -} - -Error GameSingleton::_load_map_images(String const& province_image_path, String const& terrain_image_path, bool flip_vertical) { - if (province_shape_texture.is_valid()) { - UtilityFunctions::push_error("Map images have already been loaded, cannot load: ", province_image_path, " and ", terrain_image_path); - return FAILED; - } - - // Load images - Ref<Image> province_image = load_godot_image(province_image_path); - if (province_image.is_null()) { - UtilityFunctions::push_error("Failed to load province image: ", province_image_path); - return FAILED; - } - Ref<Image> terrain_image = load_godot_image(terrain_image_path); - if (terrain_image.is_null()) { - UtilityFunctions::push_error("Failed to load terrain image: ", terrain_image_path); - return FAILED; - } - - if (flip_vertical) { - province_image->flip_y(); - terrain_image->flip_y(); - } - - // Validate dimensions and format - Error err = OK; - const Vector2i province_dims = province_image->get_size(), terrain_dims = terrain_image->get_size(); - if (province_dims.x < 1 || province_dims.y < 1) { - UtilityFunctions::push_error("Invalid dimensions (", province_dims.x, "x", province_dims.y, ") for province image: ", province_image_path); - err = FAILED; - } - if (province_dims != terrain_dims) { - UtilityFunctions::push_error("Invalid dimensions (", terrain_dims.x, "x", terrain_dims.y, ") for terrain image: ", - terrain_image_path, " (must match province image: (", province_dims.x, "x", province_dims.x, "))"); - err = FAILED; - } - static constexpr Image::Format expected_format = Image::FORMAT_RGB8; - if (province_image->get_format() == Image::FORMAT_RGBA8) province_image->convert(expected_format); - if (terrain_image->get_format() == Image::FORMAT_RGBA8) terrain_image->convert(expected_format); - if (province_image->get_format() != expected_format) { - UtilityFunctions::push_error("Invalid format (", province_image->get_format(), ", should be ", expected_format, ") for province image: ", province_image_path); - err = FAILED; - } - if (terrain_image->get_format() != expected_format) { - UtilityFunctions::push_error("Invalid format (", terrain_image->get_format(), ", should be ", expected_format, ") for terrain image: ", terrain_image_path); - err = FAILED; - } - if (err != OK) return err; - - // Generate interleaved province and terrain ID image - if (!game_manager.get_map().generate_province_shape_image(province_dims.x, province_dims.y, - province_image->get_data().ptr(), terrain_image->get_data().ptr(), terrain_variant_map, - false /* <-- whether to print detailed map errors or not (specific missing/unrecognised colours) */ - )) err = FAILED; - - static constexpr int32_t GPU_DIM_LIMIT = 0x3FFF; - // For each dimension of the image, this finds the small number of equal subdivisions required get the individual texture dims under GPU_DIM_LIMIT - for (int i = 0; i < 2; ++i) - for (image_subdivisions[i] = 1; province_dims[i] / image_subdivisions[i] > GPU_DIM_LIMIT || - province_dims[i] % image_subdivisions[i] != 0; ++image_subdivisions[i]); - - Map::shape_pixel_t const* province_shape_data = game_manager.get_map().get_province_shape_image().data(); - const Vector2i divided_dims = province_dims / image_subdivisions; - Array province_shape_images; - province_shape_images.resize(image_subdivisions.x * image_subdivisions.y); - for (int32_t v = 0; v < image_subdivisions.y; ++v) { - for (int32_t u = 0; u < image_subdivisions.x; ++u) { - PackedByteArray index_data_array; - index_data_array.resize(divided_dims.x * divided_dims.y * sizeof(Map::shape_pixel_t)); - - for (int32_t y = 0; y < divided_dims.y; ++y) - memcpy(index_data_array.ptrw() + y * divided_dims.x * sizeof(Map::shape_pixel_t), - province_shape_data + (v * divided_dims.y + y) * province_dims.x + u * divided_dims.x, - divided_dims.x * sizeof(Map::shape_pixel_t)); - - const Ref<Image> province_shape_subimage = Image::create_from_data(divided_dims.x, divided_dims.y, false, Image::FORMAT_RGB8, index_data_array); - if (province_shape_subimage.is_null()) { - UtilityFunctions::push_error("Failed to create province shape image (", u, ", ", v, ")"); - err = FAILED; - } - province_shape_images[u + v * image_subdivisions.x] = province_shape_subimage; - } - } - - province_shape_texture.instantiate(); - if (province_shape_texture->create_from_images(province_shape_images) != OK) { - UtilityFunctions::push_error("Failed to create terrain texture array!"); - err = FAILED; - } - - if (_update_colour_image() != OK) err = FAILED; - - return err; -} diff --git a/extension/src/openvic-extension/Utilities.cpp b/extension/src/openvic-extension/Utilities.cpp index 4ca6855..649550f 100644 --- a/extension/src/openvic-extension/Utilities.cpp +++ b/extension/src/openvic-extension/Utilities.cpp @@ -2,17 +2,55 @@ #include <numbers> +#include <godot_cpp/classes/file_access.hpp> #include <godot_cpp/classes/resource_loader.hpp> #include <godot_cpp/variant/utility_functions.hpp> +#include <gli/convert.hpp> +#include <gli/load_dds.hpp> + using namespace godot; using namespace OpenVic; +static Ref<Image> load_dds_image(String const& path) { + gli::texture2d texture { gli::load_dds(godot_to_std_string(path)) }; + if (texture.empty()) { + UtilityFunctions::push_error("Failed to load DDS file: ", path); + return {}; + } + + static constexpr gli::format expected_format = gli::FORMAT_BGRA8_UNORM_PACK8; + const bool needs_bgr_to_rgb = texture.format() == expected_format; + if (!needs_bgr_to_rgb) { + texture = gli::convert(texture, expected_format); + if (texture.empty()) { + UtilityFunctions::push_error("Failed to convert DDS file: ", path); + return {}; + } + } + + PackedByteArray pixels; + pixels.resize(texture.size()); + memcpy(pixels.ptrw(), texture.data(), pixels.size()); + UtilityFunctions::print("needs_bgr_to_rgb = ", needs_bgr_to_rgb); + if (needs_bgr_to_rgb) { + for (size_t i = 0; i < pixels.size(); i += 4) { + std::swap(pixels[i], pixels[i+2]); + } + } + + const gli::texture2d::extent_type extent { texture.extent() }; + return Image::create_from_data(extent.x, extent.y, false, Image::FORMAT_RGBA8, pixels); +} + Ref<Image> OpenVic::load_godot_image(String const& path) { if (path.begins_with("res://")) { ResourceLoader* loader = ResourceLoader::get_singleton(); return loader ? loader->load(path) : nullptr; } else { + if (path.ends_with(".dds")) { + return load_dds_image(path); + } return Image::load_from_file(path); } } |