mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
Fix handling of skinned meshes for nodes
Second try after the revert in 8a28339
due to an unexpected regression.
- Rigidly animated models (e.g. the glTF frog node) were not working correctly,
since cloning the mesh ignored the transformation matrices.
Note that scaling the mesh needs to occur *after* transforming the vertices.
- Visual scale did not apply to skinned models,
as resetting the animation overwrote scaled vertex data with static positions & normals.
For backwards compatibility, we now apply a 10x scale to static, non-glTF models.
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.
See also: #16141
This commit is contained in:
parent
30e33d71cc
commit
7ac5502fdf
12 changed files with 118 additions and 75 deletions
|
@ -10172,9 +10172,12 @@ Used by `core.register_node`.
|
||||||
mesh = "",
|
mesh = "",
|
||||||
-- File name of mesh when using "mesh" drawtype
|
-- File name of mesh when using "mesh" drawtype
|
||||||
-- The center of the node is the model origin.
|
-- The center of the node is the model origin.
|
||||||
-- For legacy reasons, models in OBJ format use a scale of 1 node = 1 unit;
|
-- For legacy reasons, this uses a different scale depending on the mesh:
|
||||||
-- all other model file formats use a scale of 1 node = 10 units,
|
-- 1. For glTF models: 10 units = 1 node (consistent with the scale for entities).
|
||||||
-- consistent with the scale used 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 = {
|
selection_box = {
|
||||||
-- see [Node boxes] for possibilities
|
-- see [Node boxes] for possibilities
|
||||||
|
|
|
@ -26,12 +26,21 @@ class ISceneManager;
|
||||||
class SkinnedMesh : public IAnimatedMesh
|
class SkinnedMesh : public IAnimatedMesh
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
enum class SourceFormat {
|
||||||
|
B3D,
|
||||||
|
X,
|
||||||
|
GLTF,
|
||||||
|
OTHER,
|
||||||
|
};
|
||||||
|
|
||||||
//! constructor
|
//! constructor
|
||||||
SkinnedMesh() :
|
SkinnedMesh(SourceFormat src_format) :
|
||||||
EndFrame(0.f), FramesPerSecond(25.f),
|
EndFrame(0.f), FramesPerSecond(25.f),
|
||||||
LastAnimatedFrame(-1), SkinnedLastFrame(false),
|
LastAnimatedFrame(-1), SkinnedLastFrame(false),
|
||||||
HasAnimation(false), PreparedForSkinning(false),
|
HasAnimation(false), PreparedForSkinning(false),
|
||||||
AnimateNormals(true), HardwareSkinning(false)
|
AnimateNormals(true), HardwareSkinning(false),
|
||||||
|
SrcFormat(src_format)
|
||||||
{
|
{
|
||||||
SkinningBuffers = &LocalBuffers;
|
SkinningBuffers = &LocalBuffers;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +48,10 @@ public:
|
||||||
//! destructor
|
//! destructor
|
||||||
virtual ~SkinnedMesh();
|
virtual ~SkinnedMesh();
|
||||||
|
|
||||||
|
//! The source (file) format the mesh was loaded from.
|
||||||
|
//! Important for legacy reasons pertaining to different mesh loader behavior.
|
||||||
|
SourceFormat getSourceFormat() const { return SrcFormat; }
|
||||||
|
|
||||||
//! If the duration is 0, it is a static (=non animated) mesh.
|
//! If the duration is 0, it is a static (=non animated) mesh.
|
||||||
f32 getMaxFrameNumber() const override;
|
f32 getMaxFrameNumber() const override;
|
||||||
|
|
||||||
|
@ -382,12 +395,14 @@ protected:
|
||||||
bool PreparedForSkinning;
|
bool PreparedForSkinning;
|
||||||
bool AnimateNormals;
|
bool AnimateNormals;
|
||||||
bool HardwareSkinning;
|
bool HardwareSkinning;
|
||||||
|
|
||||||
|
SourceFormat SrcFormat;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Interface for mesh loaders
|
// Interface for mesh loaders
|
||||||
class SkinnedMeshBuilder : public SkinnedMesh {
|
class SkinnedMeshBuilder : public SkinnedMesh {
|
||||||
public:
|
public:
|
||||||
SkinnedMeshBuilder() : SkinnedMesh() {}
|
SkinnedMeshBuilder(SourceFormat src_format) : SkinnedMesh(src_format) {}
|
||||||
|
|
||||||
//! loaders should call this after populating the mesh
|
//! loaders should call this after populating the mesh
|
||||||
// returns *this, so do not try to drop the mesh builder instance
|
// returns *this, so do not try to drop the mesh builder instance
|
||||||
|
|
|
@ -48,7 +48,7 @@ IAnimatedMesh *CB3DMeshFileLoader::createMesh(io::IReadFile *file)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
B3DFile = file;
|
B3DFile = file;
|
||||||
AnimatedMesh = new scene::SkinnedMeshBuilder();
|
AnimatedMesh = new scene::SkinnedMeshBuilder(SkinnedMesh::SourceFormat::B3D);
|
||||||
ShowWarning = true; // If true a warning is issued if too many textures are used
|
ShowWarning = true; // If true a warning is issued if too many textures are used
|
||||||
VerticesStart = 0;
|
VerticesStart = 0;
|
||||||
|
|
||||||
|
|
|
@ -347,7 +347,8 @@ IAnimatedMesh* SelfType::createMesh(io::IReadFile* file)
|
||||||
const char *filename = file->getFileName().c_str();
|
const char *filename = file->getFileName().c_str();
|
||||||
try {
|
try {
|
||||||
tiniergltf::GlTF model = parseGLTF(file);
|
tiniergltf::GlTF model = parseGLTF(file);
|
||||||
irr_ptr<SkinnedMeshBuilder> mesh(new SkinnedMeshBuilder());
|
irr_ptr<SkinnedMeshBuilder> mesh(new SkinnedMeshBuilder(
|
||||||
|
SkinnedMesh::SourceFormat::GLTF));
|
||||||
MeshExtractor extractor(std::move(model), mesh.get());
|
MeshExtractor extractor(std::move(model), mesh.get());
|
||||||
try {
|
try {
|
||||||
extractor.load();
|
extractor.load();
|
||||||
|
|
|
@ -762,7 +762,7 @@ ISceneManager *CSceneManager::createNewSceneManager(bool cloneContent)
|
||||||
//! Get a skinned mesh, which is not available as header-only code
|
//! Get a skinned mesh, which is not available as header-only code
|
||||||
SkinnedMesh *CSceneManager::createSkinnedMesh()
|
SkinnedMesh *CSceneManager::createSkinnedMesh()
|
||||||
{
|
{
|
||||||
return new SkinnedMesh();
|
return new SkinnedMesh(SkinnedMesh::SourceFormat::OTHER);
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates a scenemanager
|
// creates a scenemanager
|
||||||
|
|
|
@ -54,7 +54,7 @@ IAnimatedMesh *CXMeshFileLoader::createMesh(io::IReadFile *file)
|
||||||
u32 time = os::Timer::getRealTime();
|
u32 time = os::Timer::getRealTime();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AnimatedMesh = new SkinnedMeshBuilder();
|
AnimatedMesh = new SkinnedMeshBuilder(SkinnedMesh::SourceFormat::X);
|
||||||
|
|
||||||
SkinnedMesh *res = nullptr;
|
SkinnedMesh *res = nullptr;
|
||||||
if (load(file)) {
|
if (load(file)) {
|
||||||
|
|
|
@ -1676,7 +1676,7 @@ void MapblockMeshGenerator::drawMeshNode()
|
||||||
|
|
||||||
if (cur_node.f->mesh_ptr) {
|
if (cur_node.f->mesh_ptr) {
|
||||||
// clone and rotate mesh
|
// clone and rotate mesh
|
||||||
mesh = cloneMesh(cur_node.f->mesh_ptr);
|
mesh = cloneStaticMesh(cur_node.f->mesh_ptr);
|
||||||
bool modified = true;
|
bool modified = true;
|
||||||
if (facedir)
|
if (facedir)
|
||||||
rotateMeshBy6dFacedir(mesh, facedir);
|
rotateMeshBy6dFacedir(mesh, facedir);
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
// Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
// Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||||
|
|
||||||
#include "mesh.h"
|
#include "mesh.h"
|
||||||
|
#include "IMeshBuffer.h"
|
||||||
|
#include "SSkinMeshBuffer.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -102,6 +104,21 @@ scene::IAnimatedMesh* createCubeMesh(v3f scale)
|
||||||
return anim_mesh;
|
return anim_mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
inline static void transformMeshBuffer(scene::IMeshBuffer *buf,
|
||||||
|
const F &transform_vertex)
|
||||||
|
{
|
||||||
|
const u32 stride = getVertexPitchFromType(buf->getVertexType());
|
||||||
|
u32 vertex_count = buf->getVertexCount();
|
||||||
|
u8 *vertices = (u8 *)buf->getVertices();
|
||||||
|
for (u32 i = 0; i < vertex_count; i++) {
|
||||||
|
auto *vertex = (video::S3DVertex *)(vertices + i * stride);
|
||||||
|
transform_vertex(vertex);
|
||||||
|
}
|
||||||
|
buf->setDirty(scene::EBT_VERTEX);
|
||||||
|
buf->recalculateBoundingBox();
|
||||||
|
}
|
||||||
|
|
||||||
void scaleMesh(scene::IMesh *mesh, v3f scale)
|
void scaleMesh(scene::IMesh *mesh, v3f scale)
|
||||||
{
|
{
|
||||||
if (mesh == NULL)
|
if (mesh == NULL)
|
||||||
|
@ -112,14 +129,9 @@ void scaleMesh(scene::IMesh *mesh, v3f scale)
|
||||||
u32 mc = mesh->getMeshBufferCount();
|
u32 mc = mesh->getMeshBufferCount();
|
||||||
for (u32 j = 0; j < mc; j++) {
|
for (u32 j = 0; j < mc; j++) {
|
||||||
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
|
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
|
||||||
const u32 stride = getVertexPitchFromType(buf->getVertexType());
|
transformMeshBuffer(buf, [scale](video::S3DVertex *vertex) {
|
||||||
u32 vertex_count = buf->getVertexCount();
|
vertex->Pos *= scale;
|
||||||
u8 *vertices = (u8 *)buf->getVertices();
|
});
|
||||||
for (u32 i = 0; i < vertex_count; i++)
|
|
||||||
((video::S3DVertex *)(vertices + i * stride))->Pos *= scale;
|
|
||||||
|
|
||||||
buf->setDirty(scene::EBT_VERTEX);
|
|
||||||
buf->recalculateBoundingBox();
|
|
||||||
|
|
||||||
// calculate total bounding box
|
// calculate total bounding box
|
||||||
if (j == 0)
|
if (j == 0)
|
||||||
|
@ -140,14 +152,9 @@ void translateMesh(scene::IMesh *mesh, v3f vec)
|
||||||
u32 mc = mesh->getMeshBufferCount();
|
u32 mc = mesh->getMeshBufferCount();
|
||||||
for (u32 j = 0; j < mc; j++) {
|
for (u32 j = 0; j < mc; j++) {
|
||||||
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
|
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
|
||||||
const u32 stride = getVertexPitchFromType(buf->getVertexType());
|
transformMeshBuffer(buf, [vec](video::S3DVertex *vertex) {
|
||||||
u32 vertex_count = buf->getVertexCount();
|
vertex->Pos += vec;
|
||||||
u8 *vertices = (u8 *)buf->getVertices();
|
});
|
||||||
for (u32 i = 0; i < vertex_count; i++)
|
|
||||||
((video::S3DVertex *)(vertices + i * stride))->Pos += vec;
|
|
||||||
|
|
||||||
buf->setDirty(scene::EBT_VERTEX);
|
|
||||||
buf->recalculateBoundingBox();
|
|
||||||
|
|
||||||
// calculate total bounding box
|
// calculate total bounding box
|
||||||
if (j == 0)
|
if (j == 0)
|
||||||
|
@ -330,44 +337,40 @@ bool checkMeshNormals(scene::IMesh *mesh)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class VertexType, class SMeshBufferType>
|
||||||
|
static scene::IMeshBuffer *cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer)
|
||||||
|
{
|
||||||
|
auto *v = static_cast<VertexType *>(mesh_buffer->getVertices());
|
||||||
|
u16 *indices = mesh_buffer->getIndices();
|
||||||
|
auto *cloned_buffer = new SMeshBufferType();
|
||||||
|
cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
|
||||||
|
mesh_buffer->getIndexCount());
|
||||||
|
// Rigidly animated meshes may have transformation matrices that need to be applied
|
||||||
|
if (auto *sbuf = dynamic_cast<scene::SSkinMeshBuffer *>(mesh_buffer)) {
|
||||||
|
transformMeshBuffer(cloned_buffer, [sbuf](video::S3DVertex *vertex) {
|
||||||
|
sbuf->Transformation.transformVect(vertex->Pos);
|
||||||
|
vertex->Normal = sbuf->Transformation.rotateAndScaleVect(vertex->Normal);
|
||||||
|
vertex->Normal.normalize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return cloned_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer)
|
scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer)
|
||||||
{
|
{
|
||||||
switch (mesh_buffer->getVertexType()) {
|
switch (mesh_buffer->getVertexType()) {
|
||||||
case video::EVT_STANDARD: {
|
case video::EVT_STANDARD:
|
||||||
video::S3DVertex *v = (video::S3DVertex *) mesh_buffer->getVertices();
|
return cloneMeshBuffer<video::S3DVertex, scene::SMeshBuffer>(mesh_buffer);
|
||||||
u16 *indices = mesh_buffer->getIndices();
|
case video::EVT_2TCOORDS:
|
||||||
scene::SMeshBuffer *cloned_buffer = new scene::SMeshBuffer();
|
return cloneMeshBuffer<video::S3DVertex2TCoords, scene::SMeshBufferLightMap>(mesh_buffer);
|
||||||
cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
|
case video::EVT_TANGENTS:
|
||||||
mesh_buffer->getIndexCount());
|
return cloneMeshBuffer<video::S3DVertexTangents, scene::SMeshBufferTangents>(mesh_buffer);
|
||||||
return cloned_buffer;
|
|
||||||
}
|
}
|
||||||
case video::EVT_2TCOORDS: {
|
|
||||||
video::S3DVertex2TCoords *v =
|
|
||||||
(video::S3DVertex2TCoords *) mesh_buffer->getVertices();
|
|
||||||
u16 *indices = mesh_buffer->getIndices();
|
|
||||||
scene::SMeshBufferLightMap *cloned_buffer =
|
|
||||||
new scene::SMeshBufferLightMap();
|
|
||||||
cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
|
|
||||||
mesh_buffer->getIndexCount());
|
|
||||||
return cloned_buffer;
|
|
||||||
}
|
|
||||||
case video::EVT_TANGENTS: {
|
|
||||||
video::S3DVertexTangents *v =
|
|
||||||
(video::S3DVertexTangents *) mesh_buffer->getVertices();
|
|
||||||
u16 *indices = mesh_buffer->getIndices();
|
|
||||||
scene::SMeshBufferTangents *cloned_buffer =
|
|
||||||
new scene::SMeshBufferTangents();
|
|
||||||
cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
|
|
||||||
mesh_buffer->getIndexCount());
|
|
||||||
return cloned_buffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This should not happen.
|
|
||||||
sanity_check(false);
|
sanity_check(false);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
scene::SMesh* cloneMesh(scene::IMesh *src_mesh)
|
scene::SMesh* cloneStaticMesh(scene::IMesh *src_mesh)
|
||||||
{
|
{
|
||||||
scene::SMesh* dst_mesh = new scene::SMesh();
|
scene::SMesh* dst_mesh = new scene::SMesh();
|
||||||
for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
|
for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
|
||||||
|
|
|
@ -93,10 +93,8 @@ void rotateMeshYZby (scene::IMesh *mesh, f64 degrees);
|
||||||
*/
|
*/
|
||||||
scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer);
|
scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer);
|
||||||
|
|
||||||
/*
|
/// Clone a mesh. For an animated mesh, this will clone the static pose.
|
||||||
Clone the mesh.
|
scene::SMesh* cloneStaticMesh(scene::IMesh *src_mesh);
|
||||||
*/
|
|
||||||
scene::SMesh* cloneMesh(scene::IMesh *src_mesh);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Convert nodeboxes to mesh. Each tile goes into a different buffer.
|
Convert nodeboxes to mesh. Each tile goes into a different buffer.
|
||||||
|
|
|
@ -255,7 +255,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
|
||||||
dim = core::dimension2d<u32>(dim.Width, frame_height);
|
dim = core::dimension2d<u32>(dim.Width, frame_height);
|
||||||
}
|
}
|
||||||
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
|
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
|
||||||
scene::SMesh *mesh = cloneMesh(original);
|
scene::SMesh *mesh = cloneStaticMesh(original);
|
||||||
original->drop();
|
original->drop();
|
||||||
//set texture
|
//set texture
|
||||||
mesh->getMeshBuffer(0)->getMaterial().setTexture(0,
|
mesh->getMeshBuffer(0)->getMaterial().setTexture(0,
|
||||||
|
@ -639,7 +639,7 @@ scene::SMesh *getExtrudedMesh(ITextureSource *tsrc,
|
||||||
// get mesh
|
// get mesh
|
||||||
core::dimension2d<u32> dim = texture->getSize();
|
core::dimension2d<u32> dim = texture->getSize();
|
||||||
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
|
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
|
||||||
scene::SMesh *mesh = cloneMesh(original);
|
scene::SMesh *mesh = cloneStaticMesh(original);
|
||||||
original->drop();
|
original->drop();
|
||||||
|
|
||||||
//set texture
|
//set texture
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "nodedef.h"
|
#include "nodedef.h"
|
||||||
|
|
||||||
|
#include "SAnimatedMesh.h"
|
||||||
#include "itemdef.h"
|
#include "itemdef.h"
|
||||||
#if CHECK_CLIENT_BUILD()
|
#if CHECK_CLIENT_BUILD()
|
||||||
#include "client/mesh.h"
|
#include "client/mesh.h"
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
#include "client/texturesource.h"
|
#include "client/texturesource.h"
|
||||||
#include "client/tile.h"
|
#include "client/tile.h"
|
||||||
#include <IMeshManipulator.h>
|
#include <IMeshManipulator.h>
|
||||||
|
#include <SMesh.h>
|
||||||
#include <SkinnedMesh.h>
|
#include <SkinnedMesh.h>
|
||||||
#endif
|
#endif
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
@ -959,23 +961,44 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
||||||
palette = tsrc->getPalette(palette_name);
|
palette = tsrc->getPalette(palette_name);
|
||||||
|
|
||||||
if (drawtype == NDT_MESH && !mesh.empty()) {
|
if (drawtype == NDT_MESH && !mesh.empty()) {
|
||||||
// Read the mesh and apply scale
|
// Note: By freshly reading, we get an unencumbered mesh.
|
||||||
mesh_ptr = client->getMesh(mesh);
|
if (scene::IMesh *src_mesh = client->getMesh(mesh)) {
|
||||||
if (mesh_ptr) {
|
bool apply_bs = false;
|
||||||
v3f scale = v3f(BS) * visual_scale;
|
// For frame-animated meshes, always get the first frame,
|
||||||
scaleMesh(mesh_ptr, scale);
|
// which holds a model for which we can eventually get the static pose.
|
||||||
|
while (auto *src_meshes = dynamic_cast<scene::SAnimatedMesh *>(src_mesh)) {
|
||||||
|
src_mesh = src_meshes->getMesh(0.0f);
|
||||||
|
src_mesh->grab();
|
||||||
|
src_meshes->drop();
|
||||||
|
}
|
||||||
|
if (auto *skinned_mesh = dynamic_cast<scene::SkinnedMesh *>(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
|
||||||
|
bool is_gltf = skinned_mesh->getSourceFormat() ==
|
||||||
|
scene::SkinnedMesh::SourceFormat::GLTF;
|
||||||
|
apply_bs = skinned_mesh->isStatic() && !is_gltf;
|
||||||
|
// Nodes do not support mesh animation, so we clone the static pose.
|
||||||
|
// This simplifies working with the mesh: We can just scale the vertices
|
||||||
|
// as transformations have already been applied.
|
||||||
|
mesh_ptr = cloneStaticMesh(src_mesh);
|
||||||
|
src_mesh->drop();
|
||||||
|
} else {
|
||||||
|
auto *static_mesh = dynamic_cast<scene::SMesh *>(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((apply_bs ? BS : 1.0f) * visual_scale));
|
||||||
recalculateBoundingBox(mesh_ptr);
|
recalculateBoundingBox(mesh_ptr);
|
||||||
if (!checkMeshNormals(mesh_ptr)) {
|
if (!checkMeshNormals(mesh_ptr)) {
|
||||||
|
// TODO this should be done consistently when the mesh is loaded
|
||||||
infostream << "ContentFeatures: recalculating normals for mesh "
|
infostream << "ContentFeatures: recalculating normals for mesh "
|
||||||
<< mesh << std::endl;
|
<< mesh << std::endl;
|
||||||
meshmanip->recalculateNormals(mesh_ptr, true, false);
|
meshmanip->recalculateNormals(mesh_ptr, true, false);
|
||||||
} else {
|
|
||||||
// Animation is not supported, but we need to reset it to
|
|
||||||
// default state if it is animated.
|
|
||||||
// Note: recalculateNormals() also does this hence the else-block
|
|
||||||
if (mesh_ptr->getMeshType() == scene::EAMT_SKINNED)
|
|
||||||
((scene::SkinnedMesh*) mesh_ptr)->resetAnimation();
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
mesh_ptr = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -342,7 +342,7 @@ struct ContentFeatures
|
||||||
enum NodeDrawType drawtype;
|
enum NodeDrawType drawtype;
|
||||||
std::string mesh;
|
std::string mesh;
|
||||||
#if CHECK_CLIENT_BUILD()
|
#if CHECK_CLIENT_BUILD()
|
||||||
scene::IMesh *mesh_ptr; // mesh in case of mesh node
|
scene::SMesh *mesh_ptr; // mesh in case of mesh node
|
||||||
video::SColor minimap_color;
|
video::SColor minimap_color;
|
||||||
#endif
|
#endif
|
||||||
float visual_scale; // Misc. scale parameter
|
float visual_scale; // Misc. scale parameter
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue