mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
progress
This commit is contained in:
parent
d10abba38c
commit
503d7cf081
13 changed files with 434 additions and 498 deletions
|
@ -16,17 +16,32 @@ namespace scene
|
|||
class IBoneSceneNode : public ISceneNode
|
||||
{
|
||||
public:
|
||||
IBoneSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id = -1) :
|
||||
ISceneNode(parent, mgr, id) {}
|
||||
IBoneSceneNode(ISceneNode *parent, ISceneManager *mgr,
|
||||
s32 id = -1, u32 boneIndex = 0,
|
||||
const std::optional<std::string> &boneName = std::nullopt)
|
||||
:
|
||||
ISceneNode(parent, mgr, id),
|
||||
BoneIndex(boneIndex)
|
||||
{
|
||||
setName(boneName);
|
||||
}
|
||||
|
||||
//! Get the index of the bone
|
||||
virtual u32 getBoneIndex() const = 0;
|
||||
//! Returns the index of the bone
|
||||
u32 getBoneIndex() const
|
||||
{
|
||||
return BoneIndex;
|
||||
}
|
||||
|
||||
//! Get the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &getBoundingBox() const override = 0;
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &getBoundingBox() const override
|
||||
{
|
||||
return Box;
|
||||
}
|
||||
|
||||
//! Returns the relative transformation of the scene node.
|
||||
// virtual core::matrix4 getRelativeTransformation() const = 0;
|
||||
const u32 BoneIndex;
|
||||
|
||||
// Bogus box; bone scene nodes are not rendered anyways.
|
||||
static constexpr core::aabbox3d<f32> Box = {{0, 0, 0}};
|
||||
|
||||
//! The render method.
|
||||
/** Does nothing as bones are not visible. */
|
||||
|
|
|
@ -8,14 +8,17 @@
|
|||
#include "ISceneManager.h"
|
||||
#include "SMeshBuffer.h"
|
||||
#include "SSkinMeshBuffer.h"
|
||||
#include "aabbox3d.h"
|
||||
#include "irrMath.h"
|
||||
#include "matrix4.h"
|
||||
#include "quaternion.h"
|
||||
#include "vector3d.h"
|
||||
#include "Transform.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
@ -40,9 +43,8 @@ public:
|
|||
//! constructor
|
||||
SkinnedMesh(SourceFormat src_format) :
|
||||
EndFrame(0.f), FramesPerSecond(25.f),
|
||||
LastAnimatedFrame(-1), SkinnedLastFrame(false),
|
||||
HasAnimation(false), PreparedForSkinning(false),
|
||||
AnimateNormals(true), HardwareSkinning(false),
|
||||
AnimateNormals(true),
|
||||
SrcFormat(src_format)
|
||||
{
|
||||
SkinningBuffers = &LocalBuffers;
|
||||
|
@ -67,14 +69,16 @@ public:
|
|||
The actual speed is set in the scene node the mesh is instantiated in.*/
|
||||
void setAnimationSpeed(f32 fps) override;
|
||||
|
||||
//! returns the animated mesh for the given frame
|
||||
IMesh *getMesh(f32) override;
|
||||
//! **Must not be called**.
|
||||
//! TODO refactor Irrlicht so that we need not implement this.
|
||||
IMesh *getMesh(f32) override { assert(false); };
|
||||
|
||||
//! Animates joints based on frame input
|
||||
void animateMesh(f32 frame);
|
||||
//! Turns the given array of local matrices into an array of global matrices
|
||||
//! by multiplying with respective parent matrices.
|
||||
void calculateGlobalMatrices(std::vector<core::matrix4> &matrices);
|
||||
|
||||
//! Performs a software skin on this mesh based of joint positions
|
||||
void skinMesh();
|
||||
//! Performs a software skin on this mesh based on the given joint matrices
|
||||
void skinMesh(const std::vector<core::matrix4> &animated_transforms);
|
||||
|
||||
//! returns amount of mesh buffers.
|
||||
u32 getMeshBufferCount() const override;
|
||||
|
@ -143,10 +147,6 @@ public:
|
|||
return !HasAnimation;
|
||||
}
|
||||
|
||||
//! Allows to enable hardware skinning.
|
||||
/* This feature is not implemented in Irrlicht yet */
|
||||
bool setHardwareSkinning(bool on);
|
||||
|
||||
//! Refreshes vertex data cached in joints such as positions and normals
|
||||
void refreshJointCache();
|
||||
|
||||
|
@ -155,16 +155,9 @@ public:
|
|||
|
||||
void updateBoundingBox();
|
||||
|
||||
//! Recovers the joints from the mesh
|
||||
void recoverJointsFromMesh(std::vector<IBoneSceneNode *> &jointChildSceneNodes);
|
||||
|
||||
//! Transfers the joint data to the mesh
|
||||
void transferJointsToMesh(const std::vector<IBoneSceneNode *> &jointChildSceneNodes);
|
||||
|
||||
//! Creates an array of joints from this mesh as children of node
|
||||
void addJoints(std::vector<IBoneSceneNode *> &jointChildSceneNodes,
|
||||
IAnimatedMeshSceneNode *node,
|
||||
ISceneManager *smgr);
|
||||
std::vector<IBoneSceneNode *> addJoints(
|
||||
IAnimatedMeshSceneNode *node, ISceneManager *smgr);
|
||||
|
||||
//! A vertex weight
|
||||
struct SWeight
|
||||
|
@ -291,15 +284,14 @@ public:
|
|||
});
|
||||
}
|
||||
|
||||
void updateTransform(f32 frame,
|
||||
core::vector3df &t, core::quaternion &r, core::vector3df &s) const
|
||||
void updateTransform(f32 frame, core::Transform &transform) const
|
||||
{
|
||||
if (auto pos = position.get(frame))
|
||||
t = *pos;
|
||||
transform.translation = *pos;
|
||||
if (auto rot = rotation.get(frame))
|
||||
r = *rot;
|
||||
transform.rotation = *rot;
|
||||
if (auto scl = scale.get(frame))
|
||||
s = *scl;
|
||||
transform.scale = *scl;
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
|
@ -317,55 +309,24 @@ public:
|
|||
//! The name of this joint
|
||||
std::optional<std::string> Name;
|
||||
|
||||
struct Transform {
|
||||
core::vector3df translation;
|
||||
core::quaternion rotation;
|
||||
core::vector3df scale{1};
|
||||
|
||||
core::matrix4 buildMatrix() const {
|
||||
core::matrix4 T;
|
||||
T.setTranslation(translation);
|
||||
core::matrix4 R;
|
||||
rotation.getMatrix_transposed(R);
|
||||
core::matrix4 S;
|
||||
S.setScale(scale);
|
||||
return T * R * S;
|
||||
}
|
||||
};
|
||||
|
||||
//! Local transformation to be set by loaders. Mutated by animation.
|
||||
//! If a matrix is used, this joint **must not** be animated,
|
||||
//! because then the unique decomposition into translation, rotation and scale need not exist!
|
||||
std::variant<core::matrix4, Transform> transform = Transform{};
|
||||
using VariantTransform = std::variant<core::Transform, core::matrix4>;
|
||||
VariantTransform transform{core::Transform{}};
|
||||
|
||||
Transform &getAnimatableTransform() {
|
||||
if (std::holds_alternative<Transform>(transform))
|
||||
return std::get<Transform>(transform);
|
||||
const auto &mat = std::get<core::matrix4>(transform);
|
||||
Transform trs;
|
||||
trs.translation = mat.getTranslation();
|
||||
trs.scale = mat.getScale();
|
||||
trs.rotation = core::quaternion(
|
||||
mat.getRotationDegrees(trs.scale) * core::DEGTORAD);
|
||||
transform = trs;
|
||||
// TODO raise a warning if the recomposed matrix does not equal the decomposed.
|
||||
return std::get<Transform>(transform);
|
||||
}
|
||||
|
||||
void animate(f32 frame) {
|
||||
VariantTransform animate(f32 frame) const {
|
||||
if (keys.empty())
|
||||
return;
|
||||
auto &transform = getAnimatableTransform();
|
||||
keys.updateTransform(frame,
|
||||
transform.translation,
|
||||
transform.rotation,
|
||||
transform.scale);
|
||||
return transform;
|
||||
|
||||
if (std::holds_alternative<core::matrix4>(transform)) {
|
||||
// TODO raise a warning: Attempt to animate a static joint.
|
||||
return transform;
|
||||
}
|
||||
|
||||
core::matrix4 buildLocalMatrix() const {
|
||||
if (std::holds_alternative<core::matrix4>(transform))
|
||||
return std::get<core::matrix4>(transform);
|
||||
return std::get<Transform>(transform).buildMatrix();
|
||||
auto trs = std::get<core::Transform>(transform);
|
||||
keys.updateTransform(frame, trs);
|
||||
return {trs};
|
||||
}
|
||||
|
||||
//! List of child joints
|
||||
|
@ -382,33 +343,35 @@ public:
|
|||
|
||||
//! Unnecessary for loaders, will be overwritten on finalize
|
||||
core::matrix4 GlobalMatrix; // loaders may still choose to set this (temporarily) to calculate absolute vertex data.
|
||||
core::matrix4 GlobalAnimatedMatrix;
|
||||
core::matrix4 LocalAnimatedMatrix;
|
||||
|
||||
// The .x and .gltf formats pre-calculate this
|
||||
std::optional<core::matrix4> GlobalInversedMatrix;
|
||||
private:
|
||||
//! Internal members used by SkinnedMesh
|
||||
friend class SkinnedMesh;
|
||||
|
||||
// TODO friends?
|
||||
u16 JointID; // TODO refactor away: pointers -> IDs
|
||||
std::optional<u16> ParentJointID;
|
||||
};
|
||||
|
||||
//! Animates joints based on frame input
|
||||
std::vector<SJoint::VariantTransform> animateMesh(f32 frame);
|
||||
|
||||
//! TODO
|
||||
core::aabbox3df calculateBoundingBox(
|
||||
const std::vector<SJoint::VariantTransform> &transforms);
|
||||
|
||||
const std::vector<SJoint *> &getAllJoints() const {
|
||||
return AllJoints;
|
||||
}
|
||||
|
||||
protected:
|
||||
void checkForAnimation();
|
||||
bool checkForAnimation() const;
|
||||
|
||||
void topoSortJoints();
|
||||
|
||||
void prepareForSkinning();
|
||||
|
||||
void normalizeWeights();
|
||||
|
||||
void buildAllLocalAnimatedMatrices();
|
||||
|
||||
void buildAllGlobalAnimatedMatrices(SJoint *Joint = 0, SJoint *ParentJoint = 0);
|
||||
|
||||
void calculateGlobalMatrices(SJoint *Joint, SJoint *ParentJoint);
|
||||
|
||||
void skinJoint(SJoint *Joint, SJoint *ParentJoint);
|
||||
|
||||
void calculateTangents(core::vector3df &normal,
|
||||
core::vector3df &tangent, core::vector3df &binormal,
|
||||
const core::vector3df &vt1, const core::vector3df &vt2, const core::vector3df &vt3,
|
||||
|
@ -420,8 +383,8 @@ protected:
|
|||
//! Mapping from meshbuffer number to bindable texture slot
|
||||
std::vector<u32> TextureSlots;
|
||||
|
||||
//! Joints, topologically sorted (parents come before their children).
|
||||
std::vector<SJoint *> AllJoints;
|
||||
std::vector<SJoint *> RootJoints;
|
||||
|
||||
// bool can't be used here because std::vector<bool>
|
||||
// doesn't allow taking a reference to individual elements.
|
||||
|
@ -432,15 +395,17 @@ protected:
|
|||
f32 EndFrame;
|
||||
f32 FramesPerSecond;
|
||||
|
||||
f32 LastAnimatedFrame;
|
||||
bool SkinnedLastFrame;
|
||||
|
||||
bool HasAnimation;
|
||||
bool PreparedForSkinning;
|
||||
bool AnimateNormals;
|
||||
<<<<<<< HEAD
|
||||
bool HardwareSkinning;
|
||||
|
||||
SourceFormat SrcFormat;
|
||||
||||||| parent of 44f1cd940 (progress)
|
||||
bool HardwareSkinning;
|
||||
=======
|
||||
>>>>>>> 44f1cd940 (progress)
|
||||
};
|
||||
|
||||
// Interface for mesh loaders
|
||||
|
|
44
irr/include/Transform.h
Normal file
44
irr/include/Transform.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include "irrMath.h"
|
||||
#include <matrix4.h>
|
||||
#include <variant>
|
||||
#include <vector3d.h>
|
||||
#include <quaternion.h>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
|
||||
struct Transform {
|
||||
vector3df translation;
|
||||
quaternion rotation;
|
||||
vector3df scale{1};
|
||||
|
||||
// Tries to decompose the matrix, if there is one.
|
||||
static Transform decompose(const core::matrix4 &mat)
|
||||
{
|
||||
auto scale = mat.getScale();
|
||||
return {
|
||||
mat.getTranslation(),
|
||||
quaternion(mat.getRotationDegrees(scale) * DEGTORAD),
|
||||
scale,
|
||||
};
|
||||
}
|
||||
|
||||
matrix4 buildMatrix() const
|
||||
{
|
||||
matrix4 T;
|
||||
T.setTranslation(translation);
|
||||
matrix4 R;
|
||||
// TODO this is sussy. probably shouldn't be doing this.
|
||||
rotation.getMatrix_transposed(R);
|
||||
matrix4 S;
|
||||
S.setScale(scale);
|
||||
return T * R * S;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace core
|
||||
} // end namespace irr
|
|
@ -3,9 +3,13 @@
|
|||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||
|
||||
#include "CAnimatedMeshSceneNode.h"
|
||||
#include "CBoneSceneNode.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "ISceneManager.h"
|
||||
#include "MatrixBoneSceneNode.h"
|
||||
#include "S3DVertex.h"
|
||||
#include "Transform.h"
|
||||
#include "matrix4.h"
|
||||
#include "os.h"
|
||||
#include "SkinnedMesh.h"
|
||||
#include "IDummyTransformationSceneNode.h"
|
||||
|
@ -17,6 +21,7 @@
|
|||
#include "IFileSystem.h"
|
||||
#include "quaternion.h"
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
@ -30,7 +35,7 @@ CAnimatedMeshSceneNode::CAnimatedMeshSceneNode(IAnimatedMesh *mesh,
|
|||
const core::vector3df &rotation,
|
||||
const core::vector3df &scale) :
|
||||
IAnimatedMeshSceneNode(parent, mgr, id, position, rotation, scale),
|
||||
Mesh(0),
|
||||
Mesh(nullptr),
|
||||
StartFrame(0), EndFrame(0), FramesPerSecond(0.025f),
|
||||
CurrentFrameNr(0.f), LastTimeMs(0),
|
||||
TransitionTime(0), Transiting(0.f), TransitingBlend(0.f),
|
||||
|
@ -143,25 +148,27 @@ void CAnimatedMeshSceneNode::OnRegisterSceneNode()
|
|||
|
||||
IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame()
|
||||
{
|
||||
if (Mesh->getMeshType() != EAMT_SKINNED) {
|
||||
if (Mesh->getMeshType() != EAMT_SKINNED)
|
||||
return Mesh->getMesh(getFrameNr());
|
||||
} else {
|
||||
|
||||
// As multiple scene nodes may be sharing the same skinned mesh, we have to
|
||||
// re-animate it every frame to ensure that this node gets the mesh that it needs.
|
||||
|
||||
SkinnedMesh *skinnedMesh = static_cast<SkinnedMesh *>(Mesh);
|
||||
auto *skinnedMesh = static_cast<SkinnedMesh *>(Mesh);
|
||||
|
||||
skinnedMesh->transferJointsToMesh(JointChildSceneNodes);
|
||||
std::vector<core::matrix4> matrices;
|
||||
matrices.reserve(JointChildSceneNodes.size());
|
||||
for (auto *node : JointChildSceneNodes)
|
||||
matrices.push_back(node->getRelativeTransformation());
|
||||
skinnedMesh->calculateGlobalMatrices(matrices);
|
||||
|
||||
// Update the skinned mesh for the current joint transforms.
|
||||
skinnedMesh->skinMesh();
|
||||
skinnedMesh->skinMesh(matrices);
|
||||
|
||||
skinnedMesh->updateBoundingBox();
|
||||
|
||||
Box = skinnedMesh->getBoundingBox();
|
||||
|
||||
return skinnedMesh;
|
||||
}
|
||||
}
|
||||
|
||||
//! OnAnimate() is called just before rendering the whole scene.
|
||||
|
@ -268,13 +275,13 @@ void CAnimatedMeshSceneNode::render()
|
|||
if (Mesh->getMeshType() == EAMT_SKINNED) {
|
||||
// draw skeleton
|
||||
|
||||
for (auto *joint : ((SkinnedMesh *)Mesh)->getAllJoints()) {
|
||||
/*for (auto *joint : ((SkinnedMesh *)Mesh)->getAllJoints()) {
|
||||
for (const auto *childJoint : joint->Children) {
|
||||
driver->draw3DLine(joint->GlobalAnimatedMatrix.getTranslation(),
|
||||
childJoint->GlobalAnimatedMatrix.getTranslation(),
|
||||
video::SColor(255, 51, 66, 255));
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -526,6 +533,42 @@ void CAnimatedMeshSceneNode::setRenderFromIdentity(bool enable)
|
|||
RenderFromIdentity = enable;
|
||||
}
|
||||
|
||||
void CAnimatedMeshSceneNode::addJoints()
|
||||
{
|
||||
const auto &joints = static_cast<SkinnedMesh*>(Mesh)->getAllJoints();
|
||||
JointChildSceneNodes.clear();
|
||||
JointChildSceneNodes.reserve(joints.size());
|
||||
for (size_t i = 0; i < joints.size(); ++i) {
|
||||
const auto *joint = joints[i];
|
||||
ISceneNode *parent = this;
|
||||
if (joint->ParentJointID)
|
||||
parent = JointChildSceneNodes.at(*joint->ParentJointID); // exists because of topo. order
|
||||
assert(parent);
|
||||
if (const auto *matrix = std::get_if<core::matrix4>(&joint->transform)) {
|
||||
JointChildSceneNodes.push_back(new MatrixBoneSceneNode(
|
||||
parent, SceneManager, 0, i, joint->Name, *matrix));
|
||||
} else {
|
||||
JointChildSceneNodes.push_back(new CBoneSceneNode(
|
||||
parent, SceneManager, 0, i, joint->Name,
|
||||
std::get<core::Transform>(joint->transform)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CAnimatedMeshSceneNode::updateJointSceneNodes(
|
||||
const std::vector<SkinnedMesh::SJoint::VariantTransform> &transforms)
|
||||
{
|
||||
for (size_t i = 0; i < transforms.size(); ++i) {
|
||||
const auto &transform = transforms[i];
|
||||
IBoneSceneNode *node = JointChildSceneNodes[i];
|
||||
if (const auto *trs = std::get_if<core::Transform>(&transform)) {
|
||||
dynamic_cast<CBoneSceneNode*>(node)->setTransform(*trs);
|
||||
} else {
|
||||
assert(dynamic_cast<MatrixBoneSceneNode*>(node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! updates the joint positions of this mesh
|
||||
void CAnimatedMeshSceneNode::animateJoints()
|
||||
{
|
||||
|
@ -536,8 +579,7 @@ void CAnimatedMeshSceneNode::animateJoints()
|
|||
|
||||
SkinnedMesh *skinnedMesh = static_cast<SkinnedMesh *>(Mesh);
|
||||
|
||||
skinnedMesh->animateMesh(getFrameNr());
|
||||
skinnedMesh->recoverJointsFromMesh(JointChildSceneNodes);
|
||||
updateJointSceneNodes(skinnedMesh->animateMesh(getFrameNr()));
|
||||
|
||||
//-----------------------------------------
|
||||
// Transition
|
||||
|
@ -593,11 +635,7 @@ void CAnimatedMeshSceneNode::checkJoints()
|
|||
if (!JointsUsed) {
|
||||
for (u32 i = 0; i < JointChildSceneNodes.size(); ++i)
|
||||
removeChild(JointChildSceneNodes[i]);
|
||||
JointChildSceneNodes.clear();
|
||||
|
||||
// Create joints for SkinnedMesh
|
||||
((SkinnedMesh *)Mesh)->addJoints(JointChildSceneNodes, this, SceneManager);
|
||||
((SkinnedMesh *)Mesh)->recoverJointsFromMesh(JointChildSceneNodes);
|
||||
addJoints();
|
||||
|
||||
JointsUsed = true;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "IAnimatedMeshSceneNode.h"
|
||||
#include "IAnimatedMesh.h"
|
||||
|
||||
#include "SkinnedMesh.h"
|
||||
#include "matrix4.h"
|
||||
|
||||
namespace irr
|
||||
|
@ -123,9 +124,13 @@ public:
|
|||
//! you must call animateJoints(), or the mesh will not animate
|
||||
void setTransitionTime(f32 Time) override;
|
||||
|
||||
void updateJointSceneNodes(const std::vector<SkinnedMesh::SJoint::VariantTransform> &transforms);
|
||||
|
||||
//! updates the joint positions of this mesh
|
||||
void animateJoints() override;
|
||||
|
||||
void addJoints();
|
||||
|
||||
//! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected)
|
||||
void setRenderFromIdentity(bool On) override;
|
||||
|
||||
|
|
|
@ -143,7 +143,7 @@ bool CB3DMeshFileLoader::readChunkNODE(SkinnedMesh::SJoint *inJoint)
|
|||
os::Printer::log(logStr.c_str(), joint->Name.value_or("").c_str(), ELL_DEBUG);
|
||||
#endif
|
||||
|
||||
SkinnedMesh::SJoint::Transform transform;
|
||||
core::Transform transform;
|
||||
{
|
||||
f32 t[3], s[3], r[4];
|
||||
|
||||
|
@ -159,9 +159,9 @@ bool CB3DMeshFileLoader::readChunkNODE(SkinnedMesh::SJoint *inJoint)
|
|||
}
|
||||
|
||||
if (inJoint)
|
||||
joint->GlobalMatrix = inJoint->GlobalMatrix * joint->buildLocalMatrix();
|
||||
joint->GlobalMatrix = inJoint->GlobalMatrix * transform.buildMatrix();
|
||||
else
|
||||
joint->GlobalMatrix = joint->buildLocalMatrix();
|
||||
joint->GlobalMatrix = transform.buildMatrix();
|
||||
|
||||
while (B3dStack.getLast().startposition + B3dStack.getLast().length > B3DFile->getPos()) // this chunk repeats
|
||||
{
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
// Used with SkinnedMesh and IAnimatedMeshSceneNode, for boned meshes
|
||||
|
||||
#include "IBoneSceneNode.h"
|
||||
#include "Transform.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
|
@ -21,29 +22,26 @@ public:
|
|||
//! constructor
|
||||
CBoneSceneNode(ISceneNode *parent, ISceneManager *mgr,
|
||||
s32 id = -1, u32 boneIndex = 0,
|
||||
const std::optional<std::string> &boneName = std::nullopt) :
|
||||
IBoneSceneNode(parent, mgr, id),
|
||||
BoneIndex(boneIndex)
|
||||
const std::optional<std::string> &boneName = std::nullopt,
|
||||
const core::Transform &transform = {}) :
|
||||
IBoneSceneNode(parent, mgr, id, boneIndex, boneName)
|
||||
{
|
||||
setName(boneName);
|
||||
setTransform(transform);
|
||||
}
|
||||
|
||||
//! Returns the index of the bone
|
||||
u32 getBoneIndex() const override
|
||||
void setTransform(const core::Transform &transform)
|
||||
{
|
||||
return BoneIndex;
|
||||
}
|
||||
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &getBoundingBox() const override
|
||||
setPosition(transform.translation);
|
||||
{
|
||||
return Box;
|
||||
core::vector3df euler;
|
||||
auto rot = transform.rotation;
|
||||
// Invert to be consistent with setRotationDegrees
|
||||
rot.makeInverse();
|
||||
rot.toEuler(euler);
|
||||
setRotation(euler * core::RADTODEG);
|
||||
}
|
||||
setScale(transform.scale);
|
||||
}
|
||||
|
||||
const u32 BoneIndex;
|
||||
|
||||
// Bogus box; bone scene nodes are not rendered anyways.
|
||||
static constexpr core::aabbox3d<f32> Box = {{0, 0, 0}};
|
||||
};
|
||||
|
||||
} // end namespace scene
|
||||
|
|
|
@ -551,7 +551,7 @@ static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs, SkinnedMesh
|
|||
const auto &t = trs.translation;
|
||||
const auto &r = trs.rotation;
|
||||
const auto &s = trs.scale;
|
||||
SkinnedMesh::SJoint::Transform transform{
|
||||
core::Transform transform{
|
||||
convertHandedness(core::vector3df(t[0], t[1], t[2])),
|
||||
convertHandedness(core::quaternion(r[0], r[1], r[2], r[3])),
|
||||
core::vector3df(s[0], s[1], s[2]),
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "CXMeshFileLoader.h"
|
||||
#include "SkinnedMesh.h"
|
||||
#include "Transform.h"
|
||||
#include "os.h"
|
||||
|
||||
#include "fast_atof.h"
|
||||
|
@ -552,9 +553,16 @@ bool CXMeshFileLoader::parseDataObjectFrame(SkinnedMesh::SJoint *Parent)
|
|||
if (!parseDataObjectFrame(joint))
|
||||
return false;
|
||||
} else if (objectName == "FrameTransformMatrix") {
|
||||
joint->transform = core::matrix4();
|
||||
if (!parseDataObjectTransformationMatrix(std::get<core::matrix4>(joint->transform)))
|
||||
core::matrix4 matrix;
|
||||
if (!parseDataObjectTransformationMatrix(matrix))
|
||||
return false;
|
||||
auto transform = core::Transform::decompose(matrix);
|
||||
// Try to decompose. If the recomposed matrix equals the old one with a liberal tolerance, use that.
|
||||
if (transform.buildMatrix().equals(matrix, 1e-5)) {
|
||||
joint->transform = transform;
|
||||
} else {
|
||||
joint->transform = matrix;
|
||||
}
|
||||
} else if (objectName == "Mesh") {
|
||||
/*
|
||||
frame.Meshes.push_back(SXMesh());
|
||||
|
|
56
irr/src/MatrixBoneSceneNode.h
Normal file
56
irr/src/MatrixBoneSceneNode.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright (C) 2025 Joe Mama
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IBoneSceneNode.h"
|
||||
#include "matrix4.h"
|
||||
#include "vector3d.h"
|
||||
#include <iostream>
|
||||
|
||||
#include <optional>
|
||||
|
||||
// We must represent some transforms differently:
|
||||
// Bones can have static non-TRS matrix transforms,
|
||||
// for example shearing (can not be decomposed at all)
|
||||
// or negatively scaling an axis (can not be decomposed uniquely).
|
||||
// Hence well-defined animation is not possible for such nodes
|
||||
// (and in fact glTF even guarantees that they will never be animated).
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
class MatrixBoneSceneNode : public IBoneSceneNode
|
||||
{
|
||||
public:
|
||||
//! constructor
|
||||
MatrixBoneSceneNode(ISceneNode *parent, ISceneManager *mgr,
|
||||
s32 id = -1, u32 boneIndex = 0,
|
||||
const std::optional<std::string> &boneName = std::nullopt,
|
||||
const core::matrix4 &matrix = core::IdentityMatrix) :
|
||||
IBoneSceneNode(parent, mgr, id, boneIndex, boneName),
|
||||
matrix(matrix)
|
||||
{}
|
||||
|
||||
const core::matrix4 matrix;
|
||||
|
||||
// Matrix nodes should not be fake decomposed.
|
||||
const core::vector3df &getPosition() const override { assert(false); }
|
||||
const core::vector3df &getRotation() const override { assert(false); }
|
||||
const core::vector3df &getScale() const override { assert(false); }
|
||||
|
||||
// This node should be static.
|
||||
void setPosition(const core::vector3df &pos) override { assert(false); }
|
||||
void setRotation(const core::vector3df &euler_deg) override { assert(false); }
|
||||
void setScale(const core::vector3df &scale) override { assert(false); }
|
||||
|
||||
core::matrix4 getRelativeTransformation() const override
|
||||
{
|
||||
return matrix;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace scene
|
||||
} // end namespace irr
|
|
@ -6,11 +6,15 @@
|
|||
#include "IBoneSceneNode.h"
|
||||
#include "CBoneSceneNode.h"
|
||||
#include "IAnimatedMeshSceneNode.h"
|
||||
#include "MatrixBoneSceneNode.h"
|
||||
#include "SSkinMeshBuffer.h"
|
||||
#include "Transform.h"
|
||||
#include "irrMath.h"
|
||||
#include "matrix4.h"
|
||||
#include "os.h"
|
||||
#include "vector3d.h"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
@ -52,96 +56,33 @@ void SkinnedMesh::setAnimationSpeed(f32 fps)
|
|||
FramesPerSecond = fps;
|
||||
}
|
||||
|
||||
//! returns the animated mesh based
|
||||
IMesh *SkinnedMesh::getMesh(f32 frame)
|
||||
{
|
||||
// animate(frame,startFrameLoop, endFrameLoop);
|
||||
if (frame == -1)
|
||||
return this;
|
||||
|
||||
animateMesh(frame);
|
||||
skinMesh();
|
||||
return this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Keyframe Animation
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
//! Animates joints based on frame input
|
||||
void SkinnedMesh::animateMesh(f32 frame)
|
||||
|
||||
using VariantTransform = SkinnedMesh::SJoint::VariantTransform;
|
||||
std::vector<VariantTransform> SkinnedMesh::animateMesh(f32 frame)
|
||||
{
|
||||
if (!HasAnimation || LastAnimatedFrame == frame)
|
||||
return;
|
||||
|
||||
LastAnimatedFrame = frame;
|
||||
SkinnedLastFrame = false;
|
||||
|
||||
assert(HasAnimation);
|
||||
std::vector<VariantTransform> result;
|
||||
result.reserve(AllJoints.size());
|
||||
for (auto *joint : AllJoints)
|
||||
joint->animate(frame);
|
||||
|
||||
// Note:
|
||||
// LocalAnimatedMatrix needs to be built at some point, but this function may be called lots of times for
|
||||
// one render (to play two animations at the same time) LocalAnimatedMatrix only needs to be built once.
|
||||
// a call to buildAllLocalAnimatedMatrices is needed before skinning the mesh, and before the user gets the joints to move
|
||||
|
||||
//----------------
|
||||
// Temp!
|
||||
buildAllLocalAnimatedMatrices();
|
||||
//-----------------
|
||||
|
||||
updateBoundingBox();
|
||||
result.push_back(joint->animate(frame));
|
||||
return result;
|
||||
}
|
||||
|
||||
void SkinnedMesh::buildAllLocalAnimatedMatrices()
|
||||
{
|
||||
for (auto *joint : AllJoints) {
|
||||
// Could be faster:
|
||||
joint->LocalAnimatedMatrix = joint->buildLocalMatrix();
|
||||
}
|
||||
SkinnedLastFrame = false;
|
||||
}
|
||||
|
||||
void SkinnedMesh::buildAllGlobalAnimatedMatrices(SJoint *joint, SJoint *parentJoint)
|
||||
{
|
||||
if (!joint) {
|
||||
for (auto *rootJoint : RootJoints)
|
||||
buildAllGlobalAnimatedMatrices(rootJoint, 0);
|
||||
return;
|
||||
} else {
|
||||
// Find global matrix...
|
||||
if (!parentJoint)
|
||||
joint->GlobalAnimatedMatrix = joint->LocalAnimatedMatrix;
|
||||
else
|
||||
joint->GlobalAnimatedMatrix = parentJoint->GlobalAnimatedMatrix * joint->LocalAnimatedMatrix;
|
||||
}
|
||||
|
||||
for (auto *childJoint : joint->Children)
|
||||
buildAllGlobalAnimatedMatrices(childJoint, joint);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Software Skinning
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
//! Preforms a software skin on this mesh based of joint positions
|
||||
void SkinnedMesh::skinMesh()
|
||||
void SkinnedMesh::skinMesh(const std::vector<core::matrix4> &global_matrices)
|
||||
{
|
||||
if (!HasAnimation || SkinnedLastFrame)
|
||||
if (!HasAnimation)
|
||||
return;
|
||||
|
||||
//----------------
|
||||
// This is marked as "Temp!". A shiny dubloon to whomever can tell me why.
|
||||
buildAllGlobalAnimatedMatrices();
|
||||
//-----------------
|
||||
|
||||
SkinnedLastFrame = true;
|
||||
if (!HardwareSkinning) {
|
||||
// rigid animation
|
||||
for (auto *joint : AllJoints) {
|
||||
for (size_t i = 0; i < AllJoints.size(); ++i) {
|
||||
auto *joint = AllJoints[i];
|
||||
for (u32 attachedMeshIdx : joint->AttachedMeshes) {
|
||||
SSkinMeshBuffer *Buffer = (*SkinningBuffers)[attachedMeshIdx];
|
||||
Buffer->Transformation = joint->GlobalAnimatedMatrix;
|
||||
Buffer->Transformation = global_matrices[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,27 +91,20 @@ void SkinnedMesh::skinMesh()
|
|||
std::fill(buf.begin(), buf.end(), false);
|
||||
|
||||
// skin starting with the root joints
|
||||
for (auto *rootJoint : RootJoints)
|
||||
skinJoint(rootJoint, 0);
|
||||
for (size_t i = 0; i < AllJoints.size(); ++i) {
|
||||
auto *joint = AllJoints[i];
|
||||
if (joint->Weights.empty())
|
||||
continue;
|
||||
|
||||
for (auto *buffer : *SkinningBuffers)
|
||||
buffer->setDirty(EBT_VERTEX);
|
||||
}
|
||||
updateBoundingBox();
|
||||
}
|
||||
|
||||
void SkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint)
|
||||
{
|
||||
if (joint->Weights.size()) {
|
||||
// Find this joints pull on vertices...
|
||||
// Find this joints pull on vertices
|
||||
// Note: It is assumed that the global inversed matrix has been calculated at this point.
|
||||
core::matrix4 jointVertexPull = joint->GlobalAnimatedMatrix * joint->GlobalInversedMatrix.value();
|
||||
core::matrix4 jointVertexPull = global_matrices[i] * joint->GlobalInversedMatrix.value();
|
||||
|
||||
core::vector3df thisVertexMove, thisNormalMove;
|
||||
|
||||
auto &buffersUsed = *SkinningBuffers;
|
||||
|
||||
// Skin Vertices Positions and Normals...
|
||||
// Skin Vertices, Positions and Normals
|
||||
for (const auto &weight : joint->Weights) {
|
||||
// Pull this vertex...
|
||||
jointVertexPull.transformVect(thisVertexMove, weight.StaticPos);
|
||||
|
@ -202,9 +136,8 @@ void SkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint)
|
|||
}
|
||||
}
|
||||
|
||||
// Skin all children
|
||||
for (auto *childJoint : joint->Children)
|
||||
skinJoint(childJoint, joint);
|
||||
for (auto *buffer : *SkinningBuffers)
|
||||
buffer->setDirty(EBT_VERTEX);
|
||||
}
|
||||
|
||||
//! Gets joint count.
|
||||
|
@ -256,7 +189,7 @@ IMeshBuffer *SkinnedMesh::getMeshBuffer(const video::SMaterial &material) const
|
|||
if (LocalBuffers[i]->getMaterial() == material)
|
||||
return LocalBuffers[i];
|
||||
}
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 SkinnedMesh::getTextureSlot(u32 meshbufNr) const
|
||||
|
@ -283,29 +216,6 @@ void SkinnedMesh::setDirty(E_BUFFER_TYPE buffer)
|
|||
LocalBuffers[i]->setDirty(buffer);
|
||||
}
|
||||
|
||||
//! (This feature is not implemented in irrlicht yet)
|
||||
bool SkinnedMesh::setHardwareSkinning(bool on)
|
||||
{
|
||||
if (HardwareSkinning != on) {
|
||||
if (on) {
|
||||
|
||||
// set mesh to static pose...
|
||||
for (auto *joint : AllJoints) {
|
||||
for (const auto &weight : joint->Weights) {
|
||||
const u16 buffer_id = weight.buffer_id;
|
||||
const u32 vertex_id = weight.vertex_id;
|
||||
LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos = weight.StaticPos;
|
||||
LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = weight.StaticNormal;
|
||||
LocalBuffers[buffer_id]->boundingBoxNeedsRecalculated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HardwareSkinning = on;
|
||||
}
|
||||
return HardwareSkinning;
|
||||
}
|
||||
|
||||
void SkinnedMesh::refreshJointCache()
|
||||
{
|
||||
// copy cache from the mesh...
|
||||
|
@ -330,73 +240,51 @@ void SkinnedMesh::resetAnimation()
|
|||
LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = weight.StaticNormal;
|
||||
}
|
||||
}
|
||||
SkinnedLastFrame = false;
|
||||
LastAnimatedFrame = -1;
|
||||
}
|
||||
|
||||
void SkinnedMesh::calculateGlobalMatrices(SJoint *joint, SJoint *parentJoint)
|
||||
//! Turns the given array of local matrices into an array of global matrices
|
||||
//! by multiplying with respective parent matrices.
|
||||
void SkinnedMesh::calculateGlobalMatrices(std::vector<core::matrix4> &matrices)
|
||||
{
|
||||
if (!joint && parentJoint) // bit of protection from endless loops
|
||||
return;
|
||||
|
||||
// Go through the root bones
|
||||
if (!joint) {
|
||||
for (auto *rootJoint : RootJoints)
|
||||
calculateGlobalMatrices(rootJoint, nullptr);
|
||||
return;
|
||||
// Note that the joints are topologically sorted.
|
||||
for (u16 i = 0; i < AllJoints.size(); ++i) {
|
||||
if (auto parent_id = AllJoints[i]->ParentJointID) {
|
||||
matrices[i] = matrices[*parent_id] * matrices[i];
|
||||
}
|
||||
|
||||
const auto local_matrix = joint->buildLocalMatrix();
|
||||
if (!parentJoint)
|
||||
joint->GlobalMatrix = local_matrix;
|
||||
else
|
||||
joint->GlobalMatrix = parentJoint->GlobalMatrix * local_matrix;
|
||||
|
||||
joint->LocalAnimatedMatrix = local_matrix;
|
||||
joint->GlobalAnimatedMatrix = joint->GlobalMatrix;
|
||||
|
||||
if (!joint->GlobalInversedMatrix.has_value()) { // might be pre calculated
|
||||
joint->GlobalInversedMatrix = joint->GlobalMatrix;
|
||||
joint->GlobalInversedMatrix->makeInverse(); // slow
|
||||
}
|
||||
|
||||
for (auto *childJoint : joint->Children)
|
||||
calculateGlobalMatrices(childJoint, joint);
|
||||
SkinnedLastFrame = false;
|
||||
}
|
||||
|
||||
void SkinnedMesh::checkForAnimation()
|
||||
bool SkinnedMesh::checkForAnimation() const
|
||||
{
|
||||
// Check for animation...
|
||||
HasAnimation = false;
|
||||
for (auto *joint : AllJoints) {
|
||||
if (!joint->keys.empty()) {
|
||||
HasAnimation = true;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// meshes with weights, are still counted as animated for ragdolls, etc
|
||||
if (!HasAnimation) {
|
||||
// meshes with weights are animatable
|
||||
for (auto *joint : AllJoints) {
|
||||
if (joint->Weights.size()) {
|
||||
HasAnimation = true;
|
||||
break;
|
||||
}
|
||||
if (!joint->Weights.empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (HasAnimation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void SkinnedMesh::prepareForSkinning()
|
||||
{
|
||||
HasAnimation = checkForAnimation();
|
||||
if (!HasAnimation || PreparedForSkinning)
|
||||
return;
|
||||
|
||||
PreparedForSkinning = true;
|
||||
|
||||
EndFrame = 0.0f;
|
||||
for (const auto *joint : AllJoints) {
|
||||
EndFrame = std::max(EndFrame, joint->keys.getEndFrame());
|
||||
}
|
||||
}
|
||||
|
||||
if (HasAnimation && !PreparedForSkinning) {
|
||||
PreparedForSkinning = true;
|
||||
|
||||
// check for bugs:
|
||||
for (auto *joint : AllJoints) {
|
||||
for (auto &weight : joint->Weights) {
|
||||
const u16 buffer_id = weight.buffer_id;
|
||||
|
@ -413,14 +301,11 @@ void SkinnedMesh::checkForAnimation()
|
|||
}
|
||||
}
|
||||
|
||||
// An array used in skinning
|
||||
|
||||
for (u32 i = 0; i < Vertices_Moved.size(); ++i)
|
||||
for (u32 j = 0; j < Vertices_Moved[i].size(); ++j)
|
||||
Vertices_Moved[i][j] = false;
|
||||
|
||||
// For skinning: cache weight values for speed
|
||||
|
||||
for (auto *joint : AllJoints) {
|
||||
for (auto &weight : joint->Weights) {
|
||||
const u16 buffer_id = weight.buffer_id;
|
||||
|
@ -434,10 +319,46 @@ void SkinnedMesh::checkForAnimation()
|
|||
}
|
||||
}
|
||||
|
||||
// normalize weights
|
||||
normalizeWeights();
|
||||
|
||||
for (auto *joint : AllJoints) {
|
||||
joint->keys.cleanup();
|
||||
}
|
||||
SkinnedLastFrame = false;
|
||||
}
|
||||
|
||||
void SkinnedMesh::topoSortJoints()
|
||||
{
|
||||
size_t n = AllJoints.size();
|
||||
|
||||
std::vector<u16> permutation; // new id -> old id
|
||||
|
||||
std::vector<std::vector<u16>> children(AllJoints.size());
|
||||
for (u16 i = 0; i < n; ++i) {
|
||||
if (auto parentId = AllJoints[i]->ParentJointID)
|
||||
children[*parentId].push_back(i);
|
||||
else
|
||||
permutation.push_back(i);
|
||||
}
|
||||
|
||||
// Levelorder
|
||||
for (u16 i = 0; i < n; ++i) {
|
||||
permutation.insert(permutation.end(),
|
||||
children[i].begin(), children[i].end());
|
||||
}
|
||||
|
||||
// old id -> new id
|
||||
std::vector<u16> inverse_permutation(n);
|
||||
for (u16 i = 0; i < n; ++i)
|
||||
inverse_permutation[permutation[i]] = i;
|
||||
|
||||
std::vector<SJoint *> joints(n);
|
||||
for (u16 i = 0; i < n; ++i) {
|
||||
joints[i] = AllJoints[permutation[i]];
|
||||
joints[i]->JointID = i;
|
||||
if (auto parentId = joints[i]->ParentJointID)
|
||||
joints[i]->ParentJointID = inverse_permutation[*parentId];
|
||||
}
|
||||
AllJoints = joints;
|
||||
}
|
||||
|
||||
//! called by loader after populating with mesh and bone data
|
||||
|
@ -445,60 +366,40 @@ SkinnedMesh *SkinnedMeshBuilder::finalize()
|
|||
{
|
||||
os::Printer::log("Skinned Mesh - finalize", ELL_DEBUG);
|
||||
|
||||
// Make sure we recalc the next frame
|
||||
LastAnimatedFrame = -1;
|
||||
SkinnedLastFrame = false;
|
||||
topoSortJoints();
|
||||
|
||||
// calculate bounding box
|
||||
// Calculate bounding box
|
||||
for (auto *buffer : LocalBuffers) {
|
||||
buffer->recalculateBoundingBox();
|
||||
}
|
||||
|
||||
if (AllJoints.size() || RootJoints.size()) {
|
||||
// populate AllJoints or RootJoints, depending on which is empty
|
||||
if (RootJoints.empty()) {
|
||||
|
||||
for (auto *joint : AllJoints) {
|
||||
|
||||
bool foundParent = false;
|
||||
for (const auto *parentJoint : AllJoints) {
|
||||
for (const auto *childJoint : parentJoint->Children) {
|
||||
if (childJoint == joint)
|
||||
foundParent = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundParent)
|
||||
RootJoints.push_back(joint);
|
||||
}
|
||||
} else {
|
||||
AllJoints = RootJoints;
|
||||
}
|
||||
}
|
||||
|
||||
// Set array sizes...
|
||||
|
||||
// Set array sizes
|
||||
for (u32 i = 0; i < LocalBuffers.size(); ++i) {
|
||||
Vertices_Moved.emplace_back(LocalBuffers[i]->getVertexCount());
|
||||
}
|
||||
|
||||
checkForAnimation();
|
||||
prepareForSkinning();
|
||||
|
||||
if (HasAnimation) {
|
||||
std::vector<core::matrix4> matrices;
|
||||
matrices.reserve(AllJoints.size());
|
||||
// TODO populate with local matrices
|
||||
for (auto *joint : AllJoints) {
|
||||
joint->keys.cleanup();
|
||||
}
|
||||
if (const auto *matrix = std::get_if<core::matrix4>(&joint->transform))
|
||||
matrices.push_back(*matrix);
|
||||
else
|
||||
matrices.push_back(std::get<core::Transform>(joint->transform).buildMatrix());
|
||||
}
|
||||
calculateGlobalMatrices(matrices);
|
||||
|
||||
// Needed for animation and skinning...
|
||||
|
||||
calculateGlobalMatrices(0, 0);
|
||||
|
||||
for (size_t i = 0; i < AllJoints.size(); ++i) {
|
||||
auto *joint = AllJoints[i];
|
||||
joint->GlobalInversedMatrix = matrices[i];
|
||||
joint->GlobalInversedMatrix->makeInverse();
|
||||
// rigid animation for non animated meshes
|
||||
for (auto *joint : AllJoints) {
|
||||
for (u32 attachedMeshIdx : joint->AttachedMeshes) {
|
||||
SSkinMeshBuffer *Buffer = (*SkinningBuffers)[attachedMeshIdx];
|
||||
Buffer->Transformation = joint->GlobalAnimatedMatrix;
|
||||
Buffer->Transformation = matrices[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -554,14 +455,11 @@ void SkinnedMeshBuilder::addMeshBuffer(SSkinMeshBuffer *meshbuf)
|
|||
SkinnedMesh::SJoint *SkinnedMeshBuilder::addJoint(SJoint *parent)
|
||||
{
|
||||
SJoint *joint = new SJoint;
|
||||
if (parent)
|
||||
joint->ParentJointID = parent->JointID;
|
||||
|
||||
joint->JointID = AllJoints.size();
|
||||
AllJoints.push_back(joint);
|
||||
if (!parent) {
|
||||
// Add root joints to array in finalize()
|
||||
} else {
|
||||
// Set parent (Be careful of the mesh loader also setting the parent)
|
||||
parent->Children.push_back(joint);
|
||||
}
|
||||
|
||||
return joint;
|
||||
}
|
||||
|
@ -631,88 +529,6 @@ void SkinnedMesh::normalizeWeights()
|
|||
}
|
||||
}
|
||||
|
||||
void SkinnedMesh::recoverJointsFromMesh(std::vector<IBoneSceneNode *> &jointChildSceneNodes)
|
||||
{
|
||||
for (u32 i = 0; i < AllJoints.size(); ++i) {
|
||||
IBoneSceneNode *node = jointChildSceneNodes[i];
|
||||
SJoint *joint = AllJoints[i];
|
||||
if (std::holds_alternative<SJoint::Transform>(joint->transform)) {
|
||||
auto transform = std::get<SJoint::Transform>(joint->transform);
|
||||
node->setPosition(transform.translation);
|
||||
{
|
||||
core::vector3df euler;
|
||||
auto rot = transform.rotation;
|
||||
// Invert to be consistent with setRotationDegrees
|
||||
rot.makeInverse();
|
||||
rot.toEuler(euler);
|
||||
node->setRotation(euler * core::RADTODEG);
|
||||
}
|
||||
node->setScale(transform.scale);
|
||||
} else {
|
||||
node->setPosition(joint->LocalAnimatedMatrix.getTranslation());
|
||||
node->setRotation(joint->LocalAnimatedMatrix.getRotationDegrees());
|
||||
node->setScale(joint->LocalAnimatedMatrix.getScale());
|
||||
}
|
||||
|
||||
// node->updateAbsolutePosition(); // WTF
|
||||
}
|
||||
}
|
||||
|
||||
void SkinnedMesh::transferJointsToMesh(const std::vector<IBoneSceneNode *> &jointChildSceneNodes)
|
||||
{
|
||||
for (u32 i = 0; i < AllJoints.size(); ++i) {
|
||||
const IBoneSceneNode *const node = jointChildSceneNodes[i];
|
||||
SJoint *joint = AllJoints[i];
|
||||
|
||||
if (std::holds_alternative<SJoint::Transform>(joint->transform)) {
|
||||
joint->LocalAnimatedMatrix = core::matrix4();
|
||||
joint->LocalAnimatedMatrix.setRotationDegrees(node->getRotation());
|
||||
joint->LocalAnimatedMatrix.setTranslation(node->getPosition());
|
||||
joint->LocalAnimatedMatrix *= core::matrix4().setScale(node->getScale());
|
||||
}
|
||||
}
|
||||
// Make sure we recalc the next frame
|
||||
LastAnimatedFrame = -1;
|
||||
SkinnedLastFrame = false;
|
||||
}
|
||||
|
||||
void SkinnedMesh::addJoints(std::vector<IBoneSceneNode *> &jointChildSceneNodes,
|
||||
IAnimatedMeshSceneNode *node, ISceneManager *smgr)
|
||||
{
|
||||
// Create new joints
|
||||
for (u32 i = 0; i < AllJoints.size(); ++i) {
|
||||
jointChildSceneNodes.push_back(new CBoneSceneNode(0, smgr, 0, i, AllJoints[i]->Name));
|
||||
}
|
||||
|
||||
// Match up parents
|
||||
for (u32 i = 0; i < jointChildSceneNodes.size(); ++i) {
|
||||
const SJoint *const joint = AllJoints[i]; // should be fine
|
||||
|
||||
s32 parentID = -1;
|
||||
|
||||
for (u32 j = 0; (parentID == -1) && (j < AllJoints.size()); ++j) {
|
||||
if (i != j) {
|
||||
const SJoint *const parentTest = AllJoints[j];
|
||||
for (u32 n = 0; n < parentTest->Children.size(); ++n) {
|
||||
if (parentTest->Children[n] == joint) {
|
||||
parentID = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IBoneSceneNode *bone = jointChildSceneNodes[i];
|
||||
if (parentID != -1)
|
||||
bone->setParent(jointChildSceneNodes[parentID]);
|
||||
else
|
||||
bone->setParent(node);
|
||||
|
||||
bone->drop();
|
||||
}
|
||||
SkinnedLastFrame = false;
|
||||
}
|
||||
|
||||
void SkinnedMesh::convertMeshToTangents()
|
||||
{
|
||||
// now calculate tangents
|
||||
|
|
|
@ -178,15 +178,6 @@ static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill,
|
|||
matrix.setTextureScale(txs, tys);
|
||||
}
|
||||
|
||||
// Evaluate transform chain recursively; irrlicht does not do this for us
|
||||
static void updatePositionRecursive(scene::ISceneNode *node)
|
||||
{
|
||||
scene::ISceneNode *parent = node->getParent();
|
||||
if (parent)
|
||||
updatePositionRecursive(parent);
|
||||
node->updateAbsolutePosition();
|
||||
}
|
||||
|
||||
static bool logOnce(const std::ostringstream &from, std::ostream &log_to)
|
||||
{
|
||||
thread_local std::vector<u64> logged;
|
||||
|
|
|
@ -414,14 +414,14 @@ SECTION("simple skin")
|
|||
SECTION("transformations are correct")
|
||||
{
|
||||
{
|
||||
const auto &transform = std::get<SkinnedMesh::SJoint::Transform>(parent->transform);
|
||||
const auto &transform = std::get<core::Transform>(parent->transform);
|
||||
CHECK(transform.translation == v3f(0, 0, 0));
|
||||
CHECK(transform.rotation == irr::core::quaternion());
|
||||
CHECK(transform.scale == v3f(1, 1, 1));
|
||||
CHECK(parent->GlobalInversedMatrix == irr::core::matrix4());
|
||||
}
|
||||
{
|
||||
const auto &transform = std::get<SkinnedMesh::SJoint::Transform>(child->transform);
|
||||
const auto &transform = std::get<core::Transform>(child->transform);
|
||||
const v3f translation(0, 1, 0);
|
||||
CHECK(transform.translation == translation);
|
||||
CHECK(transform.rotation == irr::core::quaternion());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue