Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions irr/include/SSkinMeshBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
#include "IMeshBuffer.h"
#include "CVertexBuffer.h"
#include "CIndexBuffer.h"
#include "WeightBuffer.h"
#include "IVertexBuffer.h"
#include "S3DVertex.h"
#include "vector3d.h"
#include <cassert>

namespace scene
Expand Down Expand Up @@ -222,6 +225,8 @@ struct SSkinMeshBuffer final : public IMeshBuffer
SVertexBuffer *Vertices_Standard;
SIndexBuffer *Indices;

std::optional<WeightBuffer> Weights;

core::matrix4 Transformation;

video::SMaterial Material;
Expand Down
65 changes: 20 additions & 45 deletions irr/include/SkinnedMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ class SkinnedMesh : public IAnimatedMesh
SkinnedMesh(SourceFormat src_format) :
EndFrame(0.f), FramesPerSecond(25.f),
HasAnimation(false), PreparedForSkinning(false),
AnimateNormals(true),
SrcFormat(src_format)
{
SkinningBuffers = &LocalBuffers;
Expand Down Expand Up @@ -126,14 +125,6 @@ class SkinnedMesh : public IAnimatedMesh
\return Number of the joint or std::nullopt if not found. */
std::optional<u32> getJointNumber(const std::string &name) const;

//! Update Normals when Animating
/** \param on If false don't animate, which is faster.
Else update normals, which allows for proper lighting of
animated meshes (default). */
void updateNormalsWhenAnimating(bool on) {
AnimateNormals = on;
}

//! converts the vertex type of all meshbuffers to tangents.
/** E.g. used for bump mapping. */
void convertMeshToTangents();
Expand All @@ -143,8 +134,8 @@ class SkinnedMesh : public IAnimatedMesh
return !HasAnimation;
}

//! Refreshes vertex data cached in joints such as positions and normals
void refreshJointCache();
//! Back up static pose after local buffers have been modified directly
void updateStaticPose();

//! Moves the mesh into static position.
void resetAnimation();
Expand All @@ -153,26 +144,6 @@ class SkinnedMesh : public IAnimatedMesh
std::vector<IBoneSceneNode *> addJoints(
AnimatedMeshSceneNode *node, ISceneManager *smgr);

//! A vertex weight
struct SWeight
{
//! Index of the mesh buffer
u16 buffer_id; // I doubt 32bits is needed

//! Index of the vertex
u32 vertex_id; // Store global ID here

//! Weight Strength/Percentage (0-1)
f32 strength;

private:
//! Internal members used by SkinnedMesh
friend class SkinnedMesh;
char *Moved;
core::vector3df StaticPos;
core::vector3df StaticNormal;
};

template <class T>
struct Channel {
struct Frame {
Expand Down Expand Up @@ -329,13 +300,11 @@ class SkinnedMesh : public IAnimatedMesh

//! List of attached meshes
std::vector<u32> AttachedMeshes;
// TODO ^ should turn this into optional meshbuffer parent field?

// Animation keyframes for translation, rotation, scale
Keys keys;

//! Skin weights
std::vector<SWeight> Weights;

//! Bounding box of all affected vertices, in local space
core::aabbox3df LocalBoundingBox{{0, 0, 0}};

Expand Down Expand Up @@ -369,16 +338,12 @@ class SkinnedMesh : public IAnimatedMesh
protected:
bool checkForAnimation() const;

void topoSortJoints();

void prepareForSkinning();

void calculateStaticBoundingBox();
void calculateJointBoundingBoxes();
void calculateBufferBoundingBoxes();

void normalizeWeights();

void calculateTangents(core::vector3df &normal,
core::vector3df &tangent, core::vector3df &binormal,
const core::vector3df &vt1, const core::vector3df &vt2, const core::vector3df &vt3,
Expand All @@ -387,16 +352,13 @@ class SkinnedMesh : public IAnimatedMesh
std::vector<SSkinMeshBuffer *> *SkinningBuffers; // Meshbuffer to skin, default is to skin localBuffers

std::vector<SSkinMeshBuffer *> LocalBuffers;

//! Mapping from meshbuffer number to bindable texture slot
std::vector<u32> TextureSlots;

//! Joints, topologically sorted (parents come before their children).
std::vector<SJoint *> AllJoints;

// bool can't be used here because std::vector<bool>
// doesn't allow taking a reference to individual elements.
std::vector<std::vector<char>> Vertices_Moved;

//! Bounding box of just the static parts of the mesh
core::aabbox3df StaticPartsBox{{0, 0, 0}};

Expand All @@ -408,7 +370,6 @@ class SkinnedMesh : public IAnimatedMesh

bool HasAnimation;
bool PreparedForSkinning;
bool AnimateNormals;

SourceFormat SrcFormat;
};
Expand Down Expand Up @@ -440,8 +401,22 @@ class SkinnedMeshBuilder : public SkinnedMesh {
void addRotationKey(SJoint *joint, f32 frame, core::quaternion rotation);
void addScaleKey(SJoint *joint, f32 frame, core::vector3df scale);

//! Adds a new weight to the mesh, access it as last one
SWeight *addWeight(SJoint *joint);
//! Adds a new weight to the mesh
void addWeight(SJoint *joint, u16 buf, u32 vert_id, f32 strength);

private:

void topoSortJoints();

struct Weight {
u16 joint_id;
u16 buffer_id;
u32 vertex_id;
f32 strength;
};

std::vector<Weight> Weights;

};

} // end namespace scene
62 changes: 62 additions & 0 deletions irr/include/WeightBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 Lars Müller

#pragma once

#include "vector3d.h"
#include "matrix4.h"
#include "IVertexBuffer.h"

#include <cassert>
#include <memory>
#include <optional>

namespace scene
{

struct WeightBuffer
{
constexpr static u16 MAX_WEIGHTS_PER_VERTEX = 4;
size_t n_verts;
std::unique_ptr<u16[]> joint_idxs;
std::unique_ptr<f32[]> weights;

std::optional<std::vector<u32>> animated_vertices;

// A bit of a hack for now: Store static positions here so we can use them for skinning.
// Ideally we might want a design where we do not mutate the original vertex buffer at all.
std::unique_ptr<core::vector3df[]> static_positions;
std::unique_ptr<core::vector3df[]> static_normals;

WeightBuffer(size_t n_verts);

u16 *getJointIndices(u32 vertex_id)
{ return &joint_idxs[vertex_id * MAX_WEIGHTS_PER_VERTEX]; }
const u16 *getJointIndices(u32 vertex_id) const
{ return &joint_idxs[vertex_id * MAX_WEIGHTS_PER_VERTEX]; }

f32 *getWeights(u32 vertex_id)
{ return &weights[vertex_id * MAX_WEIGHTS_PER_VERTEX]; }
const f32 *getWeights(u32 vertex_id) const
{ return &weights[vertex_id * MAX_WEIGHTS_PER_VERTEX]; }

void addWeight(u32 vertex_id, u16 joint_id, f32 weight);

void skinVertex(u32 vertex_id, core::vector3df &pos, core::vector3df &normal,
const std::vector<core::matrix4> &joint_transforms) const;

/// @note src and dst can be the same buffer
void skin(IVertexBuffer *dst,
const std::vector<core::matrix4> &joint_transforms) const;

/// Normalizes weights so that they sum to 1.0 per vertex,
/// stores which vertices are animated.
void finalize();

void updateStaticPose(const IVertexBuffer *vbuf);

void resetToStatic(IVertexBuffer *vbuf) const;
};

} // end namespace scene
9 changes: 4 additions & 5 deletions irr/src/CB3DMeshFileLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -541,11 +541,10 @@ bool CB3DMeshFileLoader::readChunkBONE(SkinnedMesh::SJoint *inJoint)
if (AnimatedVertices_VertexID[globalVertexID] == -1) {
os::Printer::log("B3dMeshLoader: Weight has bad vertex id (no link to meshbuffer index found)");
} else if (strength > 0) {
SkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(inJoint);
weight->strength = strength;
// Find the meshbuffer and Vertex index from the Global Vertex ID:
weight->vertex_id = AnimatedVertices_VertexID[globalVertexID];
weight->buffer_id = AnimatedVertices_BufferID[globalVertexID];
AnimatedMesh->addWeight(inJoint,
AnimatedVertices_BufferID[globalVertexID],
AnimatedVertices_VertexID[globalVertexID],
strength);
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions irr/src/CGLTFMeshFileLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,10 +501,8 @@ void SelfType::MeshExtractor::addPrimitive(
if (strength <= 0)
continue; // note: also ignores negative weights

SkinnedMesh::SWeight *weight = m_irr_model->addWeight(m_loaded_nodes.at(skin.joints.at(jointIdx)));
weight->buffer_id = meshbufNr;
weight->vertex_id = v;
weight->strength = strength;
m_irr_model->addWeight(m_loaded_nodes.at(skin.joints.at(jointIdx)),
meshbufNr, v, strength);
}
}
if (negative_weights)
Expand Down
1 change: 1 addition & 0 deletions irr/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ set(IRRMESHLOADER
add_library(IRRMESHOBJ OBJECT
CMeshSceneNode.h

WeightBuffer.cpp
SkinnedMesh.cpp
CMeshSceneNode.cpp
AnimatedMeshSceneNode.cpp
Expand Down
2 changes: 1 addition & 1 deletion irr/src/CMeshManipulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ void CMeshManipulator::recalculateNormals(scene::IMesh *mesh, bool smooth, bool

if (mesh->getMeshType() == EAMT_SKINNED) {
auto *smesh = (SkinnedMesh *)mesh;
smesh->refreshJointCache();
smesh->updateStaticPose();
}
}

Expand Down
Loading
Loading