mirror of
https://github.com/luanti-org/luanti.git
synced 2025-07-27 17:28:41 +00:00
Fix handling of skinned meshes for nodes
- 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 only apply a 10x scale to static (.obj) models.
This commit is contained in:
parent
d19640d57f
commit
511c637c2a
6 changed files with 72 additions and 65 deletions
|
@ -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
|
||||||
|
|
|
@ -13,6 +13,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 +960,28 @@ 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) {
|
f32 mesh_scale = 1.0f;
|
||||||
v3f scale = v3f(BS) * visual_scale;
|
if (auto *static_mesh = dynamic_cast<scene::SMesh *>(src_mesh)) {
|
||||||
scaleMesh(mesh_ptr, scale);
|
mesh_ptr = static_mesh;
|
||||||
|
// Compatibility: Only apply BS scaling to static meshes (.obj). See #15811.
|
||||||
|
mesh_scale = 10.0f;
|
||||||
|
} else {
|
||||||
|
// We only want to consider static meshes from here on.
|
||||||
|
mesh_ptr = cloneStaticMesh(src_mesh);
|
||||||
|
src_mesh->drop();
|
||||||
|
}
|
||||||
|
scaleMesh(mesh_ptr, v3f(mesh_scale * 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