From ef719d2ab77df9be1f58d4f2abc4fedd67cdaccb Mon Sep 17 00:00:00 2001 From: Hat Kid <6624576+Hat-Kid@users.noreply.github.com> Date: Wed, 27 Nov 2024 19:14:23 +0100 Subject: [PATCH] custom models: better error for invalid envmap material (#3784) When a material in Blender has its IOR level changed to anything other than the default value of 0.5, the `KHR_materials_specular` extension is applied during the glTF export, which is what we use to check for envmaps in custom models. If an envmap is undesired, but the IOR value was accidentally changed, the program would assert during model processing if there is no metallic roughness texture attached to the material. Since this is an easy mistake to make and is hard to spot, this adds a better error message for such cases. --- common/util/gltf_util.cpp | 22 +++++++++++++++++++ common/util/gltf_util.h | 2 ++ .../level_extractor/merc_replacement.cpp | 12 ++++------ goalc/build_actor/common/MercExtract.cpp | 14 +++++------- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/common/util/gltf_util.cpp b/common/util/gltf_util.cpp index 22af8f2664a..f9e549d0c53 100644 --- a/common/util/gltf_util.cpp +++ b/common/util/gltf_util.cpp @@ -680,6 +680,28 @@ EnvmapSettings envmap_settings_from_gltf(const tinygltf::Material& mat) { return settings; } +bool material_has_envmap(const tinygltf::Material& mat) { + return mat.extensions.contains("KHR_materials_specular"); +} + +bool envmap_is_valid(const tinygltf::Material& mat, bool die) { + if (material_has_envmap(mat) && mat.pbrMetallicRoughness.metallicRoughnessTexture.index < 0) { + std::string error = fmt::format( + "Material \"{}\" has specular property set, but is missing a metallic roughness texture! " + "Check " + "that the Specular IOR level for the material is at the default of 0.5 if this is " + "unintended.", + mat.name); + if (die) { + lg::die(error); + } else { + lg::error(error); + } + return false; + } + return true; +} + std::optional find_single_skin(const tinygltf::Model& model, const std::vector& all_nodes) { std::optional skin_index; diff --git a/common/util/gltf_util.h b/common/util/gltf_util.h index 627110cb751..01cfbce1245 100644 --- a/common/util/gltf_util.h +++ b/common/util/gltf_util.h @@ -125,6 +125,8 @@ struct EnvmapSettings { }; EnvmapSettings envmap_settings_from_gltf(const tinygltf::Material& mat); +bool material_has_envmap(const tinygltf::Material& mat); +bool envmap_is_valid(const tinygltf::Material& mat, bool die); /*! * Find the index of the skin for this model. Returns nullopt if there is no skin, the index of the diff --git a/decompiler/level_extractor/merc_replacement.cpp b/decompiler/level_extractor/merc_replacement.cpp index 2afed37bd1a..c35647e2e31 100644 --- a/decompiler/level_extractor/merc_replacement.cpp +++ b/decompiler/level_extractor/merc_replacement.cpp @@ -4,10 +4,6 @@ using namespace gltf_util; namespace decompiler { -bool material_has_envmap(const tinygltf::Material& mat) { - return mat.extensions.contains("KHR_materials_specular"); -} - void extract(const std::string& name, MercExtractData& out, const tinygltf::Model& model, @@ -25,7 +21,7 @@ void extract(const std::string& name, int joints = 3; auto skin_idx = find_single_skin(model, all_nodes); if (skin_idx) { - joints = get_joint_count(model, *skin_idx); + joints += get_joint_count(model, *skin_idx); } for (const auto& n : all_nodes) { @@ -134,14 +130,13 @@ void extract(const std::string& name, return; } - int roughness_tex_idx = mat.pbrMetallicRoughness.metallicRoughnessTexture.index; - ASSERT(roughness_tex_idx >= 0); const auto& base_tex = model.textures[base_tex_idx]; ASSERT(base_tex.sampler >= 0); ASSERT(base_tex.source >= 0); gltf_util::setup_draw_mode_from_sampler(model.samplers.at(base_tex.sampler), &draw.mode); gltf_util::setup_alpha_from_material(mat, &draw.mode); - const auto& roughness_tex = model.textures.at(roughness_tex_idx); + const auto& roughness_tex = + model.textures.at(mat.pbrMetallicRoughness.metallicRoughnessTexture.index); ASSERT(roughness_tex.sampler >= 0); ASSERT(roughness_tex.source >= 0); @@ -169,6 +164,7 @@ void extract(const std::string& name, if (!material_has_envmap(mat)) { process_normal_draw(e, mat_idx, d_); } else { + envmap_is_valid(mat, true); has_envmaps = true; envmap_eff.has_envmap = true; process_envmap_draw(envmap_eff, mat_idx, d_); diff --git a/goalc/build_actor/common/MercExtract.cpp b/goalc/build_actor/common/MercExtract.cpp index fb857b8dc23..db20ea69516 100644 --- a/goalc/build_actor/common/MercExtract.cpp +++ b/goalc/build_actor/common/MercExtract.cpp @@ -5,10 +5,6 @@ #include "goalc/build_level/common/gltf_mesh_extract.h" -bool material_has_envmap(const tinygltf::Material& mat) { - return mat.extensions.contains("KHR_materials_specular"); -} - void extract(const std::string& name, MercExtractData& out, const tinygltf::Model& model, @@ -25,7 +21,7 @@ void extract(const std::string& name, int joints = 3; auto skin_idx = find_single_skin(model, all_nodes); if (skin_idx) { - joints = gltf_util::get_joint_count(model, *skin_idx); + joints += gltf_util::get_joint_count(model, *skin_idx); } for (const auto& n : all_nodes) { @@ -146,14 +142,13 @@ void extract(const std::string& name, return; } - int roughness_tex_idx = mat.pbrMetallicRoughness.metallicRoughnessTexture.index; - ASSERT(roughness_tex_idx >= 0); const auto& base_tex = model.textures[base_tex_idx]; ASSERT(base_tex.sampler >= 0); ASSERT(base_tex.source >= 0); gltf_util::setup_draw_mode_from_sampler(model.samplers.at(base_tex.sampler), &draw.mode); gltf_util::setup_alpha_from_material(mat, &draw.mode); - const auto& roughness_tex = model.textures.at(roughness_tex_idx); + const auto& roughness_tex = + model.textures.at(mat.pbrMetallicRoughness.metallicRoughnessTexture.index); ASSERT(roughness_tex.sampler >= 0); ASSERT(roughness_tex.source >= 0); @@ -178,9 +173,10 @@ void extract(const std::string& name, for (const auto& [mat_idx, d_] : draw_by_material) { const auto& mat = model.materials[mat_idx]; - if (!material_has_envmap(mat)) { + if (!gltf_util::material_has_envmap(mat)) { process_normal_draw(e, mat_idx, d_); } else { + gltf_util::envmap_is_valid(mat, false); has_envmaps = true; envmap_eff.has_envmap = true; process_envmap_draw(envmap_eff, mat_idx, d_);