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