From 3145c30b89a84ddddad9302edfc7267fecc219f0 Mon Sep 17 00:00:00 2001 From: Cory Petkovsek <632766+TokisanGames@users.noreply.github.com> Date: Tue, 7 Jan 2025 19:58:23 +0700 Subject: [PATCH] Add config warnings for texture size/format/mipmap mismatch --- src/constants.h | 6 +++ src/terrain_3d.cpp | 21 ++++++++- src/terrain_3d.h | 5 ++- src/terrain_3d_assets.cpp | 77 ++++++++++++++++++++------------ src/terrain_3d_texture_asset.cpp | 32 +++++++++++-- 5 files changed, 107 insertions(+), 34 deletions(-) diff --git a/src/constants.h b/src/constants.h index d3b069fa..f59db6d6 100644 --- a/src/constants.h +++ b/src/constants.h @@ -32,6 +32,12 @@ using namespace godot; #define V3_ZERO Vector3(0.f, 0.f, 0.f) #define V3_MAX Vector3(FLT_MAX, FLT_MAX, FLT_MAX) +// Terrain3D::_warnings is uint8_t +#define WARN_MISMATCHED_SIZE 0x01 +#define WARN_MISMATCHED_FORMAT 0x02 +#define WARN_MISMATCHED_MIPMAPS 0x04 +#define WARN_ALL 0xFF + // Set class name for logger.h #define CLASS_NAME() const String __class__ = get_class_static() + \ diff --git a/src/terrain_3d.cpp b/src/terrain_3d.cpp index f5ce118d..7d301b09 100644 --- a/src/terrain_3d.cpp +++ b/src/terrain_3d.cpp @@ -762,7 +762,9 @@ void Terrain3D::set_data_directory(String p_dir) { _data_directory = p_dir; _initialize(); } + update_configuration_warnings(); } + void Terrain3D::set_material(const Ref &p_material) { if (_material != p_material) { _clear_meshes(); @@ -1291,13 +1293,28 @@ PackedVector3Array Terrain3D::generate_nav_mesh_source_geometry(const AABB &p_gl return faces; } +void Terrain3D::set_warning(const uint8_t p_warning, const bool p_enabled) { + if (p_enabled) { + _warnings |= p_warning; + } else { + _warnings &= ~p_warning; + } + update_configuration_warnings(); +} + PackedStringArray Terrain3D::_get_configuration_warnings() const { PackedStringArray psa; if (_data_directory.is_empty()) { psa.push_back("No data directory specified. Select a directory then save the scene to write data."); } - if (!psa.is_empty()) { - psa.push_back("To update this message, deselect and reselect Terrain3D in the Scene panel."); + if (_warnings & WARN_MISMATCHED_SIZE) { + psa.push_back("Texture dimensions don't match. Double-click a texture in the FileSystem panel to see its size. Read Texture Prep in docs."); + } + if (_warnings & WARN_MISMATCHED_FORMAT) { + psa.push_back("Texture formats don't match. Double-click a texture in the FileSystem panel to see its format. Check Import panel. Read Texture Prep in docs."); + } + if (_warnings & WARN_MISMATCHED_MIPMAPS) { + psa.push_back("Texture mipmap settings don't match. Change on the Import panel."); } return psa; } diff --git a/src/terrain_3d.h b/src/terrain_3d.h index 9a7c711d..4a187199 100644 --- a/src/terrain_3d.h +++ b/src/terrain_3d.h @@ -47,6 +47,7 @@ class Terrain3D : public Node3D { String _data_directory; bool _is_inside_world = false; bool _initialized = false; + uint8_t _warnings = 0; // Object references Terrain3DData *_data = nullptr; @@ -253,7 +254,9 @@ class Terrain3D : public Node3D { Ref bake_mesh(const int p_lod, const Terrain3DData::HeightFilter p_filter = Terrain3DData::HEIGHT_FILTER_NEAREST) const; PackedVector3Array generate_nav_mesh_source_geometry(const AABB &p_global_aabb, const bool p_require_nav = true) const; - // Godot Callbacks + // Warnings + void set_warning(const uint8_t p_warning, const bool p_enabled); + uint8_t get_warnings() const { return _warnings; } PackedStringArray _get_configuration_warnings() const override; protected: diff --git a/src/terrain_3d_assets.cpp b/src/terrain_3d_assets.cpp index 32ed8180..44cce541 100644 --- a/src/terrain_3d_assets.cpp +++ b/src/terrain_3d_assets.cpp @@ -165,15 +165,14 @@ void Terrain3DAssets::_update_texture_files() { } // Detect image sizes and formats - - LOG(INFO, "Validating texture sizes"); + LOG(DEBUG, "Validating texture sizes"); Vector2i albedo_size = V2I_ZERO; Vector2i normal_size = V2I_ZERO; - Image::Format albedo_format = Image::FORMAT_MAX; Image::Format normal_format = Image::FORMAT_MAX; bool albedo_mipmaps = true; bool normal_mipmaps = true; + _terrain->set_warning(WARN_ALL, false); //DEPRECATED 1.0 - remove in Godot 4.4 bool warn_compatibility_decompress = false; @@ -182,58 +181,80 @@ void Terrain3DAssets::_update_texture_files() { if (texture_set.is_null()) { continue; } - Ref albedo_tex = texture_set->_albedo_texture; - Ref normal_tex = texture_set->_normal_texture; - // If this is the first texture, set expected size and format for the arrays + Ref albedo_tex = texture_set->_albedo_texture; if (albedo_tex.is_valid()) { Vector2i tex_size = albedo_tex->get_size(); - if (albedo_size.length() == 0.0) { - albedo_size = tex_size; - } else if (tex_size != albedo_size) { - LOG(ERROR, "Texture ID ", i, " albedo size: ", tex_size, " doesn't match size of first texture: ", albedo_size, ". They must be identical. Read Texture Prep in docs."); - return; - } Ref img = albedo_tex->get_image(); + Image::Format format = img->get_format(); + bool mipmaps = img->has_mipmaps(); + //DEPRECATED 1.0 - remove in Godot 4.4 if (_terrain->is_compatibility_mode() && img->is_compressed()) { warn_compatibility_decompress = true; img->decompress(); } - Image::Format format = img->get_format(); + + // If this is the first valid texture, set expected size and format for the arrays if (albedo_format == Image::FORMAT_MAX) { + albedo_size = tex_size; albedo_format = format; - albedo_mipmaps = img->has_mipmaps(); - } else if (format != albedo_format) { - LOG(ERROR, "Texture ID ", i, " albedo format: ", format, " doesn't match format of first texture: ", albedo_format, ". They must be identical. Read Texture Prep in docs."); - return; + albedo_mipmaps = mipmaps; + } else { // else validate against first texture + if (tex_size != albedo_size) { + _terrain->set_warning(WARN_MISMATCHED_SIZE, true); + LOG(ERROR, "Texture ID ", i, " albedo size: ", tex_size, " doesn't match size of first texture: ", albedo_size, ". They must be identical. Read Texture Prep in docs."); + } + if (format != albedo_format) { + _terrain->set_warning(WARN_MISMATCHED_FORMAT, true); + LOG(ERROR, "Texture ID ", i, " albedo format: ", format, " doesn't match format of first texture: ", albedo_format, ". They must be identical. Read Texture Prep in docs."); + } + if (mipmaps != albedo_mipmaps) { + _terrain->set_warning(WARN_MISMATCHED_MIPMAPS, true); + LOG(ERROR, "Texture ID ", i, " albedo mipmap setting (", mipmaps, ") doesn't match first texture (", albedo_mipmaps, "). They must be identical. Read Texture Prep in docs."); + } } } + + Ref normal_tex = texture_set->_normal_texture; if (normal_tex.is_valid()) { Vector2i tex_size = normal_tex->get_size(); - if (normal_size.length() == 0.0) { - normal_size = tex_size; - } else if (tex_size != normal_size) { - LOG(ERROR, "Texture ID ", i, " normal size: ", tex_size, " doesn't match size of first texture: ", normal_size, ". They must be identical. Read Texture Prep in docs."); - return; - } Ref img = normal_tex->get_image(); + Image::Format format = img->get_format(); + bool mipmaps = img->has_mipmaps(); + //DEPRECATED 1.0 - remove in Godot 4.4 if (_terrain->is_compatibility_mode() && img->is_compressed()) { warn_compatibility_decompress = true; img->decompress(); } - Image::Format format = img->get_format(); + + // If this is the first valid texture, set expected size and format for the arrays if (normal_format == Image::FORMAT_MAX) { + normal_size = tex_size; normal_format = format; - normal_mipmaps = img->has_mipmaps(); - } else if (format != normal_format) { - LOG(ERROR, "Texture ID ", i, " normal format: ", format, " doesn't match format of first texture: ", normal_format, ". They must be identical. Read Texture Prep in docs."); - return; + normal_mipmaps = mipmaps; + } else { // else validate against first texture + if (tex_size != normal_size) { + _terrain->set_warning(WARN_MISMATCHED_SIZE, true); + LOG(ERROR, "Texture ID ", i, " normal size: ", tex_size, " doesn't match size of first texture: ", normal_size, ". They must be identical. Read Texture Prep in docs."); + } + if (format != normal_format) { + _terrain->set_warning(WARN_MISMATCHED_FORMAT, true); + LOG(ERROR, "Texture ID ", i, " normal format: ", format, " doesn't match format of first texture: ", normal_format, ". They must be identical. Read Texture Prep in docs."); + } + if (mipmaps != normal_mipmaps) { + _terrain->set_warning(WARN_MISMATCHED_MIPMAPS, true); + LOG(ERROR, "Texture ID ", i, " normal mipmap setting (", mipmaps, ") doesn't match first texture (", albedo_mipmaps, "). They must be identical. Read Texture Prep in docs."); + } } } } + if (_terrain->get_warnings()) { + return; + } + // Setup defaults for generated texture if (normal_size == V2I_ZERO) { normal_size = albedo_size; } else if (albedo_size == V2I_ZERO) { diff --git a/src/terrain_3d_texture_asset.cpp b/src/terrain_3d_texture_asset.cpp index 5d82edc8..7f8271aa 100644 --- a/src/terrain_3d_texture_asset.cpp +++ b/src/terrain_3d_texture_asset.cpp @@ -66,9 +66,22 @@ void Terrain3DTextureAsset::set_albedo_texture(const Ref &p_texture) LOG(INFO, "Setting albedo texture: ", p_texture); if (_is_valid_format(p_texture)) { _albedo_texture = p_texture; - if (p_texture.is_valid() && _name == "New Texture") { - _name = p_texture->get_path().get_file().get_basename(); - LOG(INFO, "Naming texture based on filename: ", _name); + if (p_texture.is_valid()) { + String filename = p_texture->get_path().get_file().get_basename(); + if (_name == "New Texture") { + _name = filename; + LOG(INFO, "Naming texture based on filename: ", _name); + } + Ref img = p_texture->get_image(); + if (!img->has_mipmaps()) { + LOG(WARN, "Texture '", filename, "' has no mipmaps. Change on the Import panel if desired."); + } + if (img->get_width() != img->get_height()) { + LOG(WARN, "Texture '", filename, "' is not square. Mipmaps might have artifacts."); + } + if (!is_power_of_2(img->get_width()) || !is_power_of_2(img->get_height())) { + LOG(WARN, "Texture '", filename, "' size is not power of 2. This is sub-optimal."); + } } emit_signal("file_changed"); } @@ -78,6 +91,19 @@ void Terrain3DTextureAsset::set_normal_texture(const Ref &p_texture) LOG(INFO, "Setting normal texture: ", p_texture); if (_is_valid_format(p_texture)) { _normal_texture = p_texture; + if (p_texture.is_valid()) { + String filename = p_texture->get_path().get_file().get_basename(); + Ref img = p_texture->get_image(); + if (!img->has_mipmaps()) { + LOG(WARN, "Texture '", filename, "' has no mipmaps. Change on the Import panel if desired."); + } + if (img->get_width() != img->get_height()) { + LOG(WARN, "Texture '", filename, "' is not square. Not recommended. Mipmaps might have artifacts."); + } + if (!is_power_of_2(img->get_width()) || !is_power_of_2(img->get_height())) { + LOG(WARN, "Texture '", filename, "' dimensions are not power of 2. This is sub-optimal."); + } + } emit_signal("file_changed"); } }