From 740223d30cf1c0402c51162e08c256749a324790 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Thu, 15 May 2025 02:53:33 +0200 Subject: [PATCH] Refine node mesh scaling logic We now do scale static meshes, as the bug that caused meshes not to be scaled was limited to skeletally animated meshes, hence we ought not to reproduce it for skinned meshes that do not take advantage of skeletal animations (e.g. current MTG doors). However, gltf models (e.g. Wuzzy's eyeballs) up until recently were always affected due to technical reasons (using skeletal animation for rigid animation). Thus, to preserve behavior, we: 1. Do not apply 10x scale to glTF models. 2. Apply 10x scale to obj models. 3. Apply 10x scale to static x or b3d models, but not to animated ones. --- doc/lua_api.md | 9 ++++++--- irr/include/SkinnedMesh.h | 8 +++++++- irr/src/CGLTFMeshFileLoader.cpp | 2 +- src/nodedef.cpp | 26 +++++++++++++++++++------- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index f028a14d8..b604b317c 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -10172,9 +10172,12 @@ Used by `core.register_node`. mesh = "", -- File name of mesh when using "mesh" drawtype -- The center of the node is the model origin. - -- For legacy reasons, models in OBJ format use a scale of 1 node = 1 unit; - -- all other model file formats use a scale of 1 node = 10 units, - -- consistent with the scale used for entities. + -- For legacy reasons, this uses a different scale depending on the mesh: + -- 1. For glTF models: 10 units = 1 node (consistent with the scale for entities). + -- 2. For obj models: 1 unit = 1 node. + -- 3. For b3d and x models: 1 unit = 1 node if static, otherwise 10 units = 1 node. + -- Using static glTF or obj models is recommended. + -- You can use the `visual_scale` multiplier to achieve the expected scale. selection_box = { -- see [Node boxes] for possibilities diff --git a/irr/include/SkinnedMesh.h b/irr/include/SkinnedMesh.h index c9ea99365..7d93173a2 100644 --- a/irr/include/SkinnedMesh.h +++ b/irr/include/SkinnedMesh.h @@ -339,6 +339,10 @@ public: return AllJoints; } + //! Whether the mesh originated from a glTF file. + //! This is important for legacy reasons. + bool isGltf() const { return IsGltf; } + protected: void checkForAnimation(); @@ -382,12 +386,14 @@ protected: bool PreparedForSkinning; bool AnimateNormals; bool HardwareSkinning; + + bool IsGltf = false; }; // Interface for mesh loaders class SkinnedMeshBuilder : public SkinnedMesh { public: - SkinnedMeshBuilder() : SkinnedMesh() {} + SkinnedMeshBuilder(bool is_gltf = false) : SkinnedMesh() { IsGltf = is_gltf; } //! loaders should call this after populating the mesh // returns *this, so do not try to drop the mesh builder instance diff --git a/irr/src/CGLTFMeshFileLoader.cpp b/irr/src/CGLTFMeshFileLoader.cpp index f70f6692b..29aaf6341 100644 --- a/irr/src/CGLTFMeshFileLoader.cpp +++ b/irr/src/CGLTFMeshFileLoader.cpp @@ -347,7 +347,7 @@ IAnimatedMesh* SelfType::createMesh(io::IReadFile* file) const char *filename = file->getFileName().c_str(); try { tiniergltf::GlTF model = parseGLTF(file); - irr_ptr mesh(new SkinnedMeshBuilder()); + irr_ptr mesh(new SkinnedMeshBuilder(true)); MeshExtractor extractor(std::move(model), mesh.get()); try { extractor.load(); diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 2339423e9..7e2281e48 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -4,6 +4,7 @@ #include "nodedef.h" +#include "SAnimatedMesh.h" #include "itemdef.h" #if CHECK_CLIENT_BUILD() #include "client/mesh.h" @@ -962,17 +963,28 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc if (drawtype == NDT_MESH && !mesh.empty()) { // Note: By freshly reading, we get an unencumbered mesh. if (scene::IMesh *src_mesh = client->getMesh(mesh)) { - f32 mesh_scale = 1.0f; - if (auto *static_mesh = dynamic_cast(src_mesh)) { - mesh_ptr = static_mesh; - // Compatibility: Only apply BS scaling to static meshes (.obj). See #15811. - mesh_scale = 10.0f; - } else { + bool apply_bs = false; + // Unpack the possible matrjoshka of frame-animated meshes + while (auto *src_meshes = dynamic_cast(src_mesh)) { + src_mesh = src_meshes->getMesh(0); + src_mesh->grab(); + src_meshes->drop(); + } + if (auto *skinned_mesh = dynamic_cast(src_mesh)) { + // Compatibility: Animated meshes, as well as static gltf meshes, are not scaled by BS. + // See https://github.com/luanti-org/luanti/pull/16112#issuecomment-2881860329 + apply_bs = skinned_mesh->isStatic() && !skinned_mesh->isGltf(); // We only want to consider static meshes from here on. mesh_ptr = cloneStaticMesh(src_mesh); src_mesh->drop(); + } else { + auto *static_mesh = dynamic_cast(src_mesh); + assert(static_mesh); + mesh_ptr = static_mesh; + // Compatibility: Apply BS scaling to static meshes (.obj). See #15811. + apply_bs = true; } - scaleMesh(mesh_ptr, v3f(mesh_scale * visual_scale)); + scaleMesh(mesh_ptr, v3f((apply_bs ? BS : 1.0f) * visual_scale)); recalculateBoundingBox(mesh_ptr); if (!checkMeshNormals(mesh_ptr)) { // TODO this should be done consistently when the mesh is loaded