1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00
This commit is contained in:
Lars Mueller 2025-01-16 17:13:59 +01:00
parent 769c472ceb
commit abc2e54c07
16 changed files with 137 additions and 224 deletions

View file

@ -135,7 +135,7 @@ public:
//! animates the joints in the mesh based on the current frame. //! animates the joints in the mesh based on the current frame.
/** Also takes in to account transitions. */ /** Also takes in to account transitions. */
virtual void animateJoints(bool CalculateAbsolutePositions = true) = 0; virtual void animateJoints() = 0;
//! render mesh ignoring its transformation. //! render mesh ignoring its transformation.
/** Culling is unaffected. */ /** Culling is unaffected. */

View file

@ -28,15 +28,9 @@ public:
//! Returns the relative transformation of the scene node. //! Returns the relative transformation of the scene node.
// virtual core::matrix4 getRelativeTransformation() const = 0; // virtual core::matrix4 getRelativeTransformation() const = 0;
//! The animation method.
void OnAnimate(u32 timeMs) override = 0;
//! The render method. //! The render method.
/** Does nothing as bones are not visible. */ /** Does nothing as bones are not visible. */
void render() override {} void render() override {}
//! Updates the absolute position based on the relative and the parents position
virtual void updateAbsolutePositionOfAllChildren() = 0;
}; };
} // end namespace scene } // end namespace scene

View file

@ -94,16 +94,12 @@ public:
\param timeMs Current time in milliseconds. */ \param timeMs Current time in milliseconds. */
virtual void OnAnimate(u32 timeMs) virtual void OnAnimate(u32 timeMs)
{ {
if (IsVisible) { if (!IsVisible && Children.empty())
// update absolute position return;
updateAbsolutePosition();
// perform the post render process on all children updateAbsolutePosition();
for (auto *child : Children)
ISceneNodeList::iterator it = Children.begin(); child->OnAnimate(timeMs);
for (; it != Children.end(); ++it)
(*it)->OnAnimate(timeMs);
}
} }
//! Renders the node. //! Renders the node.

View file

@ -8,11 +8,14 @@
#include "ISceneManager.h" #include "ISceneManager.h"
#include "SMeshBuffer.h" #include "SMeshBuffer.h"
#include "SSkinMeshBuffer.h" #include "SSkinMeshBuffer.h"
#include "irrMath.h"
#include "matrix4.h"
#include "quaternion.h" #include "quaternion.h"
#include "vector3d.h" #include "vector3d.h"
#include <optional> #include <optional>
#include <string> #include <string>
#include <variant>
namespace irr namespace irr
{ {
@ -314,8 +317,56 @@ public:
//! The name of this joint //! The name of this joint
std::optional<std::string> Name; std::optional<std::string> Name;
//! Local matrix of this joint struct Transform {
core::matrix4 LocalMatrix; 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{};
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) {
if (keys.empty())
return;
auto &transform = getAnimatableTransform();
keys.updateTransform(frame,
transform.translation,
transform.rotation,
transform.scale);
}
core::matrix4 buildLocalMatrix() const {
if (std::holds_alternative<core::matrix4>(transform))
return std::get<core::matrix4>(transform);
return std::get<Transform>(transform).buildMatrix();
}
//! List of child joints //! List of child joints
std::vector<SJoint *> Children; std::vector<SJoint *> Children;
@ -334,11 +385,6 @@ public:
core::matrix4 GlobalAnimatedMatrix; core::matrix4 GlobalAnimatedMatrix;
core::matrix4 LocalAnimatedMatrix; core::matrix4 LocalAnimatedMatrix;
//! These should be set by loaders.
core::vector3df Animatedposition;
core::vector3df Animatedscale;
core::quaternion Animatedrotation;
// The .x and .gltf formats pre-calculate this // The .x and .gltf formats pre-calculate this
std::optional<core::matrix4> GlobalInversedMatrix; std::optional<core::matrix4> GlobalInversedMatrix;
private: private:

View file

@ -170,7 +170,11 @@ IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame()
// Update the skinned mesh for the current joint transforms. // Update the skinned mesh for the current joint transforms.
skinnedMesh->skinMesh(); skinnedMesh->skinMesh();
skinnedMesh->updateBoundingBox(); skinnedMesh->updateBoundingBox();
Box = skinnedMesh->getBoundingBox();
setAutomaticCulling(EAC_OFF);
return skinnedMesh; return skinnedMesh;
} }
@ -187,6 +191,10 @@ void CAnimatedMeshSceneNode::OnAnimate(u32 timeMs)
buildFrameNr(timeMs - LastTimeMs); buildFrameNr(timeMs - LastTimeMs);
LastTimeMs = timeMs; LastTimeMs = timeMs;
// This needs to be done on animate, which is called recursively *before*
// anything is rendered so that the transformations of children are up to date
animateJoints();
IAnimatedMeshSceneNode::OnAnimate(timeMs); IAnimatedMeshSceneNode::OnAnimate(timeMs);
} }
@ -204,15 +212,8 @@ void CAnimatedMeshSceneNode::render()
++PassCount; ++PassCount;
scene::IMesh *m = getMeshForCurrentFrame(); scene::IMesh *m = getMeshForCurrentFrame();
_IRR_DEBUG_BREAK_IF(!m);
if (m) { Box = m->getBoundingBox(); // HACK
Box = m->getBoundingBox();
} else {
#ifdef _DEBUG
os::Printer::log("Animated Mesh returned no mesh to render.", ELL_WARNING);
#endif
return;
}
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
@ -237,6 +238,7 @@ void CAnimatedMeshSceneNode::render()
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
// for debug purposes only: // for debug purposes only:
// DebugDataVisible = ~0;
if (DebugDataVisible && PassCount == 1) { if (DebugDataVisible && PassCount == 1) {
video::SMaterial debug_mat; video::SMaterial debug_mat;
debug_mat.AntiAliasing = video::EAAM_OFF; debug_mat.AntiAliasing = video::EAAM_OFF;
@ -559,7 +561,7 @@ void CAnimatedMeshSceneNode::setRenderFromIdentity(bool enable)
} }
//! updates the joint positions of this mesh //! updates the joint positions of this mesh
void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions) void CAnimatedMeshSceneNode::animateJoints()
{ {
if (Mesh && Mesh->getMeshType() == EAMT_SKINNED) { if (Mesh && Mesh->getMeshType() == EAMT_SKINNED) {
checkJoints(); checkJoints();
@ -614,15 +616,6 @@ void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions)
// TransitingBlend)); // TransitingBlend));
} }
} }
if (CalculateAbsolutePositions) {
//---slow---
for (u32 n = 0; n < JointChildSceneNodes.size(); ++n) {
if (JointChildSceneNodes[n]->getParent() == this) {
JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); // temp, should be an option
}
}
}
} }
} }

View file

@ -117,12 +117,12 @@ public:
//! updates the absolute position based on the relative and the parents position //! updates the absolute position based on the relative and the parents position
void updateAbsolutePosition() override; void updateAbsolutePosition() override;
//! Sets the transition time in seconds (note: This needs to enable joints, and setJointmode maybe set to 2) //! Sets the transition time in seconds (note: This needs to enable joints)
//! you must call animateJoints(), or the mesh will not animate //! you must call animateJoints(), or the mesh will not animate
void setTransitionTime(f32 Time) override; void setTransitionTime(f32 Time) override;
//! updates the joint positions of this mesh //! updates the joint positions of this mesh
void animateJoints(bool CalculateAbsolutePositions = true) override; void animateJoints() override;
//! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected) //! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected)
void setRenderFromIdentity(bool On) override; void setRenderFromIdentity(bool On) override;

View file

@ -143,31 +143,25 @@ bool CB3DMeshFileLoader::readChunkNODE(SkinnedMesh::SJoint *inJoint)
os::Printer::log(logStr.c_str(), joint->Name.value_or("").c_str(), ELL_DEBUG); os::Printer::log(logStr.c_str(), joint->Name.value_or("").c_str(), ELL_DEBUG);
#endif #endif
f32 position[3], scale[3], rotation[4]; SkinnedMesh::SJoint::Transform transform;
{
f32 t[3], s[3], r[4];
readFloats(position, 3); readFloats(t, 3);
readFloats(scale, 3); readFloats(s, 3);
readFloats(rotation, 4); readFloats(r, 4);
joint->Animatedposition = core::vector3df(position[0], position[1], position[2]); joint->transform = transform = {
joint->Animatedscale = core::vector3df(scale[0], scale[1], scale[2]); {t[0], t[1], t[2]},
joint->Animatedrotation = core::quaternion(rotation[1], rotation[2], rotation[3], rotation[0]); {r[1], r[2], r[3], r[0]},
{s[0], s[1], s[2]},
// Build LocalMatrix: };
}
core::matrix4 positionMatrix;
positionMatrix.setTranslation(joint->Animatedposition);
core::matrix4 scaleMatrix;
scaleMatrix.setScale(joint->Animatedscale);
core::matrix4 rotationMatrix;
joint->Animatedrotation.getMatrix_transposed(rotationMatrix);
joint->LocalMatrix = positionMatrix * rotationMatrix * scaleMatrix;
if (inJoint) if (inJoint)
joint->GlobalMatrix = inJoint->GlobalMatrix * joint->LocalMatrix; joint->GlobalMatrix = inJoint->GlobalMatrix * joint->buildLocalMatrix();
else else
joint->GlobalMatrix = joint->LocalMatrix; joint->GlobalMatrix = joint->buildLocalMatrix();
while (B3dStack.getLast().startposition + B3dStack.getLast().length > B3DFile->getPos()) // this chunk repeats while (B3dStack.getLast().startposition + B3dStack.getLast().length > B3DFile->getPos()) // this chunk repeats
{ {

View file

@ -1,43 +0,0 @@
// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "CBoneSceneNode.h"
#include <optional>
namespace irr
{
namespace scene
{
void CBoneSceneNode::OnAnimate(u32 timeMs)
{
if (IsVisible) {
// update absolute position
// updateAbsolutePosition();
// perform the post render process on all children
ISceneNodeList::iterator it = Children.begin();
for (; it != Children.end(); ++it)
(*it)->OnAnimate(timeMs);
}
}
void CBoneSceneNode::helper_updateAbsolutePositionOfAllChildren(ISceneNode *Node)
{
Node->updateAbsolutePosition();
ISceneNodeList::const_iterator it = Node->getChildren().begin();
for (; it != Node->getChildren().end(); ++it) {
helper_updateAbsolutePositionOfAllChildren((*it));
}
}
void CBoneSceneNode::updateAbsolutePositionOfAllChildren()
{
helper_updateAbsolutePositionOfAllChildren(this);
}
} // namespace scene
} // namespace irr

View file

@ -40,13 +40,6 @@ public:
return Box; return Box;
} }
void OnAnimate(u32 timeMs) override;
void updateAbsolutePositionOfAllChildren() override;
private:
void helper_updateAbsolutePositionOfAllChildren(ISceneNode *Node);
const u32 BoneIndex; const u32 BoneIndex;
// Bogus box; bone scene nodes are not rendered anyways. // Bogus box; bone scene nodes are not rendered anyways.

View file

@ -539,34 +539,25 @@ static core::matrix4 loadTransform(const tiniergltf::Node::Matrix &m, SkinnedMes
mat[i] = static_cast<f32>(m[i]); mat[i] = static_cast<f32>(m[i]);
mat = convertHandedness(mat); mat = convertHandedness(mat);
// Decompose the matrix into translation, scale, and rotation. // Note: "When a node is targeted for animation [...],
joint->Animatedposition = mat.getTranslation(); // only TRS properties MAY be present; matrix MUST NOT be present."
// Thus we MUST NOT do any decomposition, which in general need not exist.
auto scale = mat.getScale(); joint->transform = mat;
joint->Animatedscale = scale;
joint->Animatedrotation = mat.getRotationRadians(scale);
// Invert the rotation because it is applied using `getMatrix_transposed`,
// which again inverts.
joint->Animatedrotation.makeInverse();
return mat; return mat;
} }
static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs, SkinnedMesh::SJoint *joint) static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs, SkinnedMesh::SJoint *joint)
{ {
const auto &trans = trs.translation; const auto &t = trs.translation;
const auto &rot = trs.rotation; const auto &r = trs.rotation;
const auto &scale = trs.scale; const auto &s = trs.scale;
core::matrix4 transMat; SkinnedMesh::SJoint::Transform transform{
joint->Animatedposition = convertHandedness(core::vector3df(trans[0], trans[1], trans[2])); convertHandedness(core::vector3df(t[0], t[1], t[2])),
transMat.setTranslation(joint->Animatedposition); convertHandedness(core::quaternion(r[0], r[1], r[2], r[3])),
core::matrix4 rotMat; core::vector3df(s[0], s[1], s[2]),
joint->Animatedrotation = convertHandedness(core::quaternion(rot[0], rot[1], rot[2], rot[3])); };
core::quaternion(joint->Animatedrotation).getMatrix_transposed(rotMat); joint->transform = transform;
joint->Animatedscale = core::vector3df(scale[0], scale[1], scale[2]); return transform.buildMatrix();
core::matrix4 scaleMat;
scaleMat.setScale(joint->Animatedscale);
return transMat * rotMat * scaleMat;
} }
static core::matrix4 loadTransform(std::optional<std::variant<tiniergltf::Node::Matrix, tiniergltf::Node::TRS>> transform, static core::matrix4 loadTransform(std::optional<std::variant<tiniergltf::Node::Matrix, tiniergltf::Node::TRS>> transform,
@ -584,8 +575,7 @@ void SelfType::MeshExtractor::loadNode(
const auto &node = m_gltf_model.nodes->at(nodeIdx); const auto &node = m_gltf_model.nodes->at(nodeIdx);
auto *joint = m_irr_model->addJoint(parent); auto *joint = m_irr_model->addJoint(parent);
const core::matrix4 transform = loadTransform(node.transform, joint); const core::matrix4 transform = loadTransform(node.transform, joint);
joint->LocalMatrix = transform; joint->GlobalMatrix = parent ? parent->GlobalMatrix * transform : transform;
joint->GlobalMatrix = parent ? parent->GlobalMatrix * joint->LocalMatrix : joint->LocalMatrix;
if (node.name.has_value()) { if (node.name.has_value()) {
joint->Name = node.name->c_str(); joint->Name = node.name->c_str();
} }

View file

@ -320,7 +320,6 @@ set(IRRMESHLOADER
add_library(IRRMESHOBJ OBJECT add_library(IRRMESHOBJ OBJECT
SkinnedMesh.cpp SkinnedMesh.cpp
CBoneSceneNode.cpp
CMeshSceneNode.cpp CMeshSceneNode.cpp
CAnimatedMeshSceneNode.cpp CAnimatedMeshSceneNode.cpp
${IRRMESHLOADER} ${IRRMESHLOADER}

View file

@ -552,12 +552,9 @@ bool CXMeshFileLoader::parseDataObjectFrame(SkinnedMesh::SJoint *Parent)
if (!parseDataObjectFrame(joint)) if (!parseDataObjectFrame(joint))
return false; return false;
} else if (objectName == "FrameTransformMatrix") { } else if (objectName == "FrameTransformMatrix") {
if (!parseDataObjectTransformationMatrix(joint->LocalMatrix)) joint->transform = core::matrix4();
if (!parseDataObjectTransformationMatrix(std::get<core::matrix4>(joint->transform)))
return false; return false;
// joint->LocalAnimatedMatrix
// joint->LocalAnimatedMatrix.makeInverse();
// joint->LocalMatrix=tmp*joint->LocalAnimatedMatrix;
} else if (objectName == "Mesh") { } else if (objectName == "Mesh") {
/* /*
frame.Meshes.push_back(SXMesh()); frame.Meshes.push_back(SXMesh());

View file

@ -7,7 +7,11 @@
#include "CBoneSceneNode.h" #include "CBoneSceneNode.h"
#include "IAnimatedMeshSceneNode.h" #include "IAnimatedMeshSceneNode.h"
#include "SSkinMeshBuffer.h" #include "SSkinMeshBuffer.h"
#include "irrMath.h"
#include "matrix4.h"
#include "os.h" #include "os.h"
#include "vector3d.h"
#include <variant>
#include <vector> #include <vector>
#include <cassert> #include <cassert>
@ -73,13 +77,8 @@ void SkinnedMesh::animateMesh(f32 frame)
LastAnimatedFrame = frame; LastAnimatedFrame = frame;
SkinnedLastFrame = false; SkinnedLastFrame = false;
for (auto *joint : AllJoints) { for (auto *joint : AllJoints)
// The joints can be animated here with no input from their parents joint->animate(frame);
joint->keys.updateTransform(frame,
joint->Animatedposition,
joint->Animatedrotation,
joint->Animatedscale);
}
// Note: // Note:
// LocalAnimatedMatrix needs to be built at some point, but this function may be called lots of times for // LocalAnimatedMatrix needs to be built at some point, but this function may be called lots of times for
@ -98,56 +97,7 @@ void SkinnedMesh::buildAllLocalAnimatedMatrices()
{ {
for (auto *joint : AllJoints) { for (auto *joint : AllJoints) {
// Could be faster: // Could be faster:
joint->LocalAnimatedMatrix = joint->buildLocalMatrix();
if (!joint->keys.empty()) {
// IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched to getMatrix_transposed instead of getMatrix for downward compatibility.
// Not tested so far if this was correct or wrong before quaternion fix!
// Note that using getMatrix_transposed inverts the rotation.
joint->Animatedrotation.getMatrix_transposed(joint->LocalAnimatedMatrix);
// --- joint->LocalAnimatedMatrix *= joint->Animatedrotation.getMatrix() ---
f32 *m1 = joint->LocalAnimatedMatrix.pointer();
core::vector3df &Pos = joint->Animatedposition;
m1[0] += Pos.X * m1[3];
m1[1] += Pos.Y * m1[3];
m1[2] += Pos.Z * m1[3];
m1[4] += Pos.X * m1[7];
m1[5] += Pos.Y * m1[7];
m1[6] += Pos.Z * m1[7];
m1[8] += Pos.X * m1[11];
m1[9] += Pos.Y * m1[11];
m1[10] += Pos.Z * m1[11];
m1[12] += Pos.X * m1[15];
m1[13] += Pos.Y * m1[15];
m1[14] += Pos.Z * m1[15];
// -----------------------------------
if (!joint->keys.scale.empty()) {
/*
core::matrix4 scaleMatrix;
scaleMatrix.setScale(joint->Animatedscale);
joint->LocalAnimatedMatrix *= scaleMatrix;
*/
// -------- joint->LocalAnimatedMatrix *= scaleMatrix -----------------
core::matrix4 &mat = joint->LocalAnimatedMatrix;
mat[0] *= joint->Animatedscale.X;
mat[1] *= joint->Animatedscale.X;
mat[2] *= joint->Animatedscale.X;
mat[3] *= joint->Animatedscale.X;
mat[4] *= joint->Animatedscale.Y;
mat[5] *= joint->Animatedscale.Y;
mat[6] *= joint->Animatedscale.Y;
mat[7] *= joint->Animatedscale.Y;
mat[8] *= joint->Animatedscale.Z;
mat[9] *= joint->Animatedscale.Z;
mat[10] *= joint->Animatedscale.Z;
mat[11] *= joint->Animatedscale.Z;
// -----------------------------------
}
} else {
joint->LocalAnimatedMatrix = joint->LocalMatrix;
}
} }
SkinnedLastFrame = false; SkinnedLastFrame = false;
} }
@ -396,12 +346,13 @@ void SkinnedMesh::calculateGlobalMatrices(SJoint *joint, SJoint *parentJoint)
return; return;
} }
const auto local_matrix = joint->buildLocalMatrix();
if (!parentJoint) if (!parentJoint)
joint->GlobalMatrix = joint->LocalMatrix; joint->GlobalMatrix = local_matrix;
else else
joint->GlobalMatrix = parentJoint->GlobalMatrix * joint->LocalMatrix; joint->GlobalMatrix = parentJoint->GlobalMatrix * local_matrix;
joint->LocalAnimatedMatrix = joint->LocalMatrix; joint->LocalAnimatedMatrix = local_matrix;
joint->GlobalAnimatedMatrix = joint->GlobalMatrix; joint->GlobalAnimatedMatrix = joint->GlobalMatrix;
if (!joint->GlobalInversedMatrix.has_value()) { // might be pre calculated if (!joint->GlobalInversedMatrix.has_value()) { // might be pre calculated
@ -689,7 +640,7 @@ void SkinnedMesh::recoverJointsFromMesh(std::vector<IBoneSceneNode *> &jointChil
node->setRotation(joint->LocalAnimatedMatrix.getRotationDegrees()); node->setRotation(joint->LocalAnimatedMatrix.getRotationDegrees());
node->setScale(joint->LocalAnimatedMatrix.getScale()); node->setScale(joint->LocalAnimatedMatrix.getScale());
node->updateAbsolutePosition(); node->updateAbsolutePosition(); // WTF
} }
} }

View file

@ -916,12 +916,7 @@ struct Node {
std::optional<std::size_t> skin; std::optional<std::size_t> skin;
std::optional<std::vector<double>> weights; std::optional<std::vector<double>> weights;
Node(const Json::Value &o) Node(const Json::Value &o)
: transform(Matrix { : transform(TRS{})
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
})
{ {
check(o.isObject()); check(o.isObject());
if (o.isMember("camera")) { if (o.isMember("camera")) {

View file

@ -1401,6 +1401,8 @@ void GenericCAO::updateBones(f32 dtime)
if (m_bone_override.empty()) if (m_bone_override.empty())
return; return;
// FIXME these need to be applied at a different point in time
// in order to be relative to the animated bone positions of the current frame
for (auto &it : m_bone_override) { for (auto &it : m_bone_override) {
std::string bone_name = it.first; std::string bone_name = it.first;
scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str()); scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());

View file

@ -413,17 +413,23 @@ SECTION("simple skin")
SECTION("transformations are correct") SECTION("transformations are correct")
{ {
CHECK(parent->Animatedposition == v3f(0, 0, 0)); {
CHECK(parent->Animatedrotation == irr::core::quaternion()); const auto &transform = std::get<SkinnedMesh::SJoint::Transform>(parent->transform);
CHECK(parent->Animatedscale == v3f(1, 1, 1)); CHECK(transform.translation == v3f(0, 0, 0));
CHECK(parent->GlobalInversedMatrix == irr::core::matrix4()); CHECK(transform.rotation == irr::core::quaternion());
const v3f childTranslation(0, 1, 0); CHECK(transform.scale == v3f(1, 1, 1));
CHECK(child->Animatedposition == childTranslation); CHECK(parent->GlobalInversedMatrix == irr::core::matrix4());
CHECK(child->Animatedrotation == irr::core::quaternion()); }
CHECK(child->Animatedscale == v3f(1, 1, 1)); {
irr::core::matrix4 inverseBindMatrix; const auto &transform = std::get<SkinnedMesh::SJoint::Transform>(child->transform);
inverseBindMatrix.setTranslation(-childTranslation); const v3f translation(0, 1, 0);
CHECK(child->GlobalInversedMatrix == inverseBindMatrix); CHECK(transform.translation == translation);
CHECK(transform.rotation == irr::core::quaternion());
CHECK(transform.scale == v3f(1, 1, 1));
irr::core::matrix4 inverseBindMatrix;
inverseBindMatrix.setTranslation(-translation);
CHECK(child->GlobalInversedMatrix == inverseBindMatrix);
}
} }
SECTION("weights are correct") SECTION("weights are correct")