mirror of
https://github.com/luanti-org/luanti.git
synced 2025-09-15 18:57:08 +00:00
Fix and clean up skeletal animation (#15722)
* Fix attachments lagging behind their parents (#14818) * Fix animation blending (#14817) * Bring back cool guy as another .x smoke test * Add .x mesh loader unittest * Do bounding box & matrix calculation at proper point in time * Remove obsolete `SAnimatedMesh`
This commit is contained in:
parent
0bb87eb1ff
commit
fde6384a09
40 changed files with 856 additions and 1388 deletions
|
@ -13,8 +13,8 @@ namespace scene
|
|||
//! Interface for an animated mesh.
|
||||
/** There are already simple implementations of this interface available so
|
||||
you don't have to implement this interface on your own if you need to:
|
||||
You might want to use irr::scene::SAnimatedMesh, irr::scene::SMesh,
|
||||
irr::scene::SMeshBuffer etc. */
|
||||
You might want to use irr::scene::SMesh, irr::scene::SMeshBuffer etc.
|
||||
*/
|
||||
class IAnimatedMesh : public IMesh
|
||||
{
|
||||
public:
|
||||
|
@ -34,22 +34,8 @@ public:
|
|||
scene node the mesh is instantiated in.*/
|
||||
virtual void setAnimationSpeed(f32 fps) = 0;
|
||||
|
||||
//! Returns the IMesh interface for a frame.
|
||||
/** \param frame: Frame number, >= 0, <= getMaxFrameNumber()
|
||||
Linear interpolation is used if this is between two frames.
|
||||
\return Returns the animated mesh for the given frame */
|
||||
virtual IMesh *getMesh(f32 frame) = 0;
|
||||
|
||||
//! Returns the type of the animated mesh.
|
||||
/** In most cases it is not necessary to use this method.
|
||||
This is useful for making a safe downcast. For example,
|
||||
if getMeshType() returns EAMT_MD2 it's safe to cast the
|
||||
IAnimatedMesh to IAnimatedMeshMD2.
|
||||
\returns Type of the mesh. */
|
||||
E_ANIMATED_MESH_TYPE getMeshType() const override
|
||||
{
|
||||
return EAMT_UNKNOWN;
|
||||
}
|
||||
//! Returns the type of the animated mesh. Useful for safe downcasts.
|
||||
E_ANIMATED_MESH_TYPE getMeshType() const = 0;
|
||||
};
|
||||
|
||||
} // end namespace scene
|
||||
|
|
|
@ -12,35 +12,8 @@ namespace irr
|
|||
{
|
||||
namespace scene
|
||||
{
|
||||
enum E_JOINT_UPDATE_ON_RENDER
|
||||
{
|
||||
//! do nothing
|
||||
EJUOR_NONE = 0,
|
||||
|
||||
//! get joints positions from the mesh (for attached nodes, etc)
|
||||
EJUOR_READ,
|
||||
|
||||
//! control joint positions in the mesh (eg. ragdolls, or set the animation from animateJoints() )
|
||||
EJUOR_CONTROL
|
||||
};
|
||||
|
||||
class IAnimatedMeshSceneNode;
|
||||
|
||||
//! Callback interface for catching events of ended animations.
|
||||
/** Implement this interface and use
|
||||
IAnimatedMeshSceneNode::setAnimationEndCallback to be able to
|
||||
be notified if an animation playback has ended.
|
||||
**/
|
||||
class IAnimationEndCallBack : public virtual IReferenceCounted
|
||||
{
|
||||
public:
|
||||
//! Will be called when the animation playback has ended.
|
||||
/** See IAnimatedMeshSceneNode::setAnimationEndCallback for
|
||||
more information.
|
||||
\param node: Node of which the animation has ended. */
|
||||
virtual void OnAnimationEnd(IAnimatedMeshSceneNode *node) = 0;
|
||||
};
|
||||
|
||||
//! Scene node capable of displaying an animated mesh.
|
||||
class IAnimatedMeshSceneNode : public ISceneNode
|
||||
{
|
||||
|
@ -120,11 +93,10 @@ public:
|
|||
/** When true the animations are played looped */
|
||||
virtual bool getLoopMode() const = 0;
|
||||
|
||||
//! Sets a callback interface which will be called if an animation playback has ended.
|
||||
/** Set this to 0 to disable the callback again.
|
||||
Please note that this will only be called when in non looped
|
||||
mode, see IAnimatedMeshSceneNode::setLoopMode(). */
|
||||
virtual void setAnimationEndCallback(IAnimationEndCallBack *callback = 0) = 0;
|
||||
//! Will be called right after the joints have been animated,
|
||||
//! but before the transforms have been propagated recursively to children.
|
||||
virtual void setOnAnimateCallback(
|
||||
const std::function<void(f32 dtime)> &cb) = 0;
|
||||
|
||||
//! Sets if the scene node should not copy the materials of the mesh but use them in a read only style.
|
||||
/** In this way it is possible to change the materials a mesh
|
||||
|
@ -139,20 +111,15 @@ public:
|
|||
virtual void setMesh(IAnimatedMesh *mesh) = 0;
|
||||
|
||||
//! Returns the current mesh
|
||||
virtual IAnimatedMesh *getMesh(void) = 0;
|
||||
|
||||
//! Set how the joints should be updated on render
|
||||
virtual void setJointMode(E_JOINT_UPDATE_ON_RENDER mode) = 0;
|
||||
virtual IAnimatedMesh *getMesh() = 0;
|
||||
|
||||
//! Sets the transition time in seconds
|
||||
/** Note: This needs to enable joints, and setJointmode set to
|
||||
EJUOR_CONTROL. You must call animateJoints(), or the mesh will
|
||||
not animate. */
|
||||
/** Note: You must call animateJoints(), or the mesh will not animate. */
|
||||
virtual void setTransitionTime(f32 Time) = 0;
|
||||
|
||||
//! animates the joints in the mesh based on the current frame.
|
||||
/** Also takes in to account transitions. */
|
||||
virtual void animateJoints(bool CalculateAbsolutePositions = true) = 0;
|
||||
virtual void animateJoints() = 0;
|
||||
|
||||
//! render mesh ignoring its transformation.
|
||||
/** Culling is unaffected. */
|
||||
|
|
|
@ -11,85 +11,41 @@ namespace irr
|
|||
namespace scene
|
||||
{
|
||||
|
||||
//! Enumeration for different bone animation modes
|
||||
enum E_BONE_ANIMATION_MODE
|
||||
{
|
||||
//! The bone is usually animated, unless it's parent is not animated
|
||||
EBAM_AUTOMATIC = 0,
|
||||
|
||||
//! The bone is animated by the skin, if it's parent is not animated then animation will resume from this bone onward
|
||||
EBAM_ANIMATED,
|
||||
|
||||
//! The bone is not animated by the skin
|
||||
EBAM_UNANIMATED,
|
||||
|
||||
//! Not an animation mode, just here to count the available modes
|
||||
EBAM_COUNT
|
||||
|
||||
};
|
||||
|
||||
enum E_BONE_SKINNING_SPACE
|
||||
{
|
||||
//! local skinning, standard
|
||||
EBSS_LOCAL = 0,
|
||||
|
||||
//! global skinning
|
||||
EBSS_GLOBAL,
|
||||
|
||||
EBSS_COUNT
|
||||
};
|
||||
|
||||
//! Names for bone animation modes
|
||||
const c8 *const BoneAnimationModeNames[] = {
|
||||
"automatic",
|
||||
"animated",
|
||||
"unanimated",
|
||||
0,
|
||||
};
|
||||
|
||||
//! Interface for bones used for skeletal animation.
|
||||
/** Used with SkinnedMesh and IAnimatedMeshSceneNode. */
|
||||
class IBoneSceneNode : public ISceneNode
|
||||
{
|
||||
public:
|
||||
IBoneSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id = -1) :
|
||||
ISceneNode(parent, mgr, id), positionHint(-1), scaleHint(-1), rotationHint(-1) {}
|
||||
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;
|
||||
}
|
||||
|
||||
//! Sets the animation mode of the bone.
|
||||
/** \return True if successful. (Unused) */
|
||||
virtual bool setAnimationMode(E_BONE_ANIMATION_MODE mode) = 0;
|
||||
//! returns the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &getBoundingBox() const override
|
||||
{
|
||||
return Box;
|
||||
}
|
||||
|
||||
//! Gets the current animation mode of the bone
|
||||
virtual E_BONE_ANIMATION_MODE getAnimationMode() const = 0;
|
||||
const u32 BoneIndex;
|
||||
|
||||
//! Get the axis aligned bounding box of this node
|
||||
const core::aabbox3d<f32> &getBoundingBox() const override = 0;
|
||||
|
||||
//! Returns the relative transformation of the scene node.
|
||||
// virtual core::matrix4 getRelativeTransformation() const = 0;
|
||||
|
||||
//! The animation method.
|
||||
void OnAnimate(u32 timeMs) override = 0;
|
||||
// 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. */
|
||||
void render() override {}
|
||||
|
||||
//! How the relative transformation of the bone is used
|
||||
virtual void setSkinningSpace(E_BONE_SKINNING_SPACE space) = 0;
|
||||
|
||||
//! How the relative transformation of the bone is used
|
||||
virtual E_BONE_SKINNING_SPACE getSkinningSpace() const = 0;
|
||||
|
||||
//! Updates the absolute position based on the relative and the parents position
|
||||
virtual void updateAbsolutePositionOfAllChildren() = 0;
|
||||
|
||||
s32 positionHint;
|
||||
s32 scaleHint;
|
||||
s32 rotationHint;
|
||||
};
|
||||
|
||||
} // end namespace scene
|
||||
|
|
|
@ -20,38 +20,6 @@ enum E_ANIMATED_MESH_TYPE
|
|||
//! Unknown animated mesh type.
|
||||
EAMT_UNKNOWN = 0,
|
||||
|
||||
//! Quake 2 MD2 model file
|
||||
EAMT_MD2,
|
||||
|
||||
//! Quake 3 MD3 model file
|
||||
EAMT_MD3,
|
||||
|
||||
//! Maya .obj static model
|
||||
EAMT_OBJ,
|
||||
|
||||
//! Quake 3 .bsp static Map
|
||||
EAMT_BSP,
|
||||
|
||||
//! 3D Studio .3ds file
|
||||
EAMT_3DS,
|
||||
|
||||
//! My3D Mesh, the file format by Zhuck Dimitry
|
||||
EAMT_MY3D,
|
||||
|
||||
//! Pulsar LMTools .lmts file. This Irrlicht loader was written by Jonas Petersen
|
||||
EAMT_LMTS,
|
||||
|
||||
//! Cartography Shop .csm file. This loader was created by Saurav Mohapatra.
|
||||
EAMT_CSM,
|
||||
|
||||
//! .oct file for Paul Nette's FSRad or from Murphy McCauley's Blender .oct exporter.
|
||||
/** The oct file format contains 3D geometry and lightmaps and
|
||||
can be loaded directly by Irrlicht */
|
||||
EAMT_OCT,
|
||||
|
||||
//! Halflife MDL model file
|
||||
EAMT_MDL_HALFLIFE,
|
||||
|
||||
//! generic skinned mesh
|
||||
EAMT_SKINNED,
|
||||
|
||||
|
@ -119,9 +87,7 @@ public:
|
|||
virtual void setDirty(E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) = 0;
|
||||
|
||||
//! Returns the type of the meshes.
|
||||
/** This is useful for making a safe downcast. For example,
|
||||
if getMeshType() returns EAMT_MD2 it's safe to cast the
|
||||
IMesh to IAnimatedMeshMD2.
|
||||
/** This is useful for making a safe downcast.
|
||||
Note: It's no longer just about animated meshes, that name has just historical reasons.
|
||||
\returns Type of the mesh */
|
||||
virtual E_ANIMATED_MESH_TYPE getMeshType() const
|
||||
|
|
|
@ -66,26 +66,6 @@ public:
|
|||
IReferenceCounted::drop() for more information. */
|
||||
virtual SMesh *createMeshCopy(IMesh *mesh) const = 0;
|
||||
|
||||
//! Get amount of polygons in mesh.
|
||||
/** \param mesh Input mesh
|
||||
\return Number of polygons in mesh. */
|
||||
virtual s32 getPolyCount(IMesh *mesh) const = 0;
|
||||
|
||||
//! Get amount of polygons in mesh.
|
||||
/** \param mesh Input mesh
|
||||
\return Number of polygons in mesh. */
|
||||
virtual s32 getPolyCount(IAnimatedMesh *mesh) const = 0;
|
||||
|
||||
//! Create a new AnimatedMesh and adds the mesh to it
|
||||
/** \param mesh Input mesh
|
||||
\param type The type of the animated mesh to create.
|
||||
\return Newly created animated mesh with mesh as its only
|
||||
content. When you don't need the animated mesh anymore, you
|
||||
should call IAnimatedMesh::drop(). See
|
||||
IReferenceCounted::drop() for more information. */
|
||||
virtual IAnimatedMesh *createAnimatedMesh(IMesh *mesh,
|
||||
scene::E_ANIMATED_MESH_TYPE type = scene::EAMT_UNKNOWN) const = 0;
|
||||
|
||||
//! Apply a manipulator on the Meshbuffer
|
||||
/** \param func A functor defining the mesh manipulation.
|
||||
\param buffer The Meshbuffer to apply the manipulator to.
|
||||
|
|
|
@ -32,7 +32,7 @@ public:
|
|||
|
||||
//! Get the currently defined mesh for display.
|
||||
/** \return Pointer to mesh which is displayed by this node. */
|
||||
virtual IMesh *getMesh(void) = 0;
|
||||
virtual IMesh *getMesh() = 0;
|
||||
|
||||
//! Sets if the scene node should not copy the materials of the mesh but use them directly.
|
||||
/** In this way it is possible to change the materials of a mesh
|
||||
|
|
|
@ -94,16 +94,12 @@ public:
|
|||
\param timeMs Current time in milliseconds. */
|
||||
virtual void OnAnimate(u32 timeMs)
|
||||
{
|
||||
if (IsVisible) {
|
||||
// update absolute position
|
||||
updateAbsolutePosition();
|
||||
if (!IsVisible && Children.empty())
|
||||
return;
|
||||
|
||||
// perform the post render process on all children
|
||||
|
||||
ISceneNodeList::iterator it = Children.begin();
|
||||
for (; it != Children.end(); ++it)
|
||||
(*it)->OnAnimate(timeMs);
|
||||
}
|
||||
updateAbsolutePosition();
|
||||
for (auto *child : Children)
|
||||
child->OnAnimate(timeMs);
|
||||
}
|
||||
|
||||
//! Renders the node.
|
||||
|
|
|
@ -1,167 +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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "IAnimatedMesh.h"
|
||||
#include "IMesh.h"
|
||||
#include "aabbox3d.h"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene
|
||||
{
|
||||
|
||||
//! Simple implementation of the IAnimatedMesh interface.
|
||||
struct SAnimatedMesh final : public IAnimatedMesh
|
||||
{
|
||||
//! constructor
|
||||
SAnimatedMesh(scene::IMesh *mesh = 0, scene::E_ANIMATED_MESH_TYPE type = scene::EAMT_UNKNOWN) :
|
||||
IAnimatedMesh(), FramesPerSecond(25.f), Type(type)
|
||||
{
|
||||
addMesh(mesh);
|
||||
recalculateBoundingBox();
|
||||
}
|
||||
|
||||
//! destructor
|
||||
virtual ~SAnimatedMesh()
|
||||
{
|
||||
// drop meshes
|
||||
for (auto *mesh : Meshes)
|
||||
mesh->drop();
|
||||
}
|
||||
|
||||
f32 getMaxFrameNumber() const override
|
||||
{
|
||||
return static_cast<f32>(Meshes.size() - 1);
|
||||
}
|
||||
|
||||
//! Gets the default animation speed of the animated mesh.
|
||||
/** \return Amount of frames per second. If the amount is 0, it is a static, non animated mesh. */
|
||||
f32 getAnimationSpeed() const override
|
||||
{
|
||||
return FramesPerSecond;
|
||||
}
|
||||
|
||||
//! Gets the frame count of the animated mesh.
|
||||
/** \param fps Frames per second to play the animation with. If the amount is 0, it is not animated.
|
||||
The actual speed is set in the scene node the mesh is instantiated in.*/
|
||||
void setAnimationSpeed(f32 fps) override
|
||||
{
|
||||
FramesPerSecond = fps;
|
||||
}
|
||||
|
||||
//! Returns the IMesh interface for a frame.
|
||||
/** \param frame: Frame number as zero based index.
|
||||
\return The animated mesh based for the given frame */
|
||||
IMesh *getMesh(f32 frame) override
|
||||
{
|
||||
if (Meshes.empty())
|
||||
return nullptr;
|
||||
|
||||
return Meshes[static_cast<s32>(frame)];
|
||||
}
|
||||
|
||||
//! adds a Mesh
|
||||
void addMesh(IMesh *mesh)
|
||||
{
|
||||
if (mesh) {
|
||||
mesh->grab();
|
||||
Meshes.push_back(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
//! Returns an axis aligned bounding box of the mesh.
|
||||
/** \return A bounding box of this mesh is returned. */
|
||||
const core::aabbox3d<f32> &getBoundingBox() const override
|
||||
{
|
||||
return Box;
|
||||
}
|
||||
|
||||
//! set user axis aligned bounding box
|
||||
void setBoundingBox(const core::aabbox3df &box) override
|
||||
{
|
||||
Box = box;
|
||||
}
|
||||
|
||||
//! Recalculates the bounding box.
|
||||
void recalculateBoundingBox()
|
||||
{
|
||||
Box.reset(0, 0, 0);
|
||||
|
||||
if (Meshes.empty())
|
||||
return;
|
||||
|
||||
Box = Meshes[0]->getBoundingBox();
|
||||
|
||||
for (u32 i = 1; i < Meshes.size(); ++i)
|
||||
Box.addInternalBox(Meshes[i]->getBoundingBox());
|
||||
}
|
||||
|
||||
//! Returns the type of the animated mesh.
|
||||
E_ANIMATED_MESH_TYPE getMeshType() const override
|
||||
{
|
||||
return Type;
|
||||
}
|
||||
|
||||
//! returns amount of mesh buffers.
|
||||
u32 getMeshBufferCount() const override
|
||||
{
|
||||
if (Meshes.empty())
|
||||
return 0;
|
||||
|
||||
return Meshes[0]->getMeshBufferCount();
|
||||
}
|
||||
|
||||
//! returns pointer to a mesh buffer
|
||||
IMeshBuffer *getMeshBuffer(u32 nr) const override
|
||||
{
|
||||
if (Meshes.empty())
|
||||
return 0;
|
||||
|
||||
return Meshes[0]->getMeshBuffer(nr);
|
||||
}
|
||||
|
||||
//! Returns pointer to a mesh buffer which fits a material
|
||||
/** \param material: material to search for
|
||||
\return Returns the pointer to the mesh buffer or
|
||||
NULL if there is no such mesh buffer. */
|
||||
IMeshBuffer *getMeshBuffer(const video::SMaterial &material) const override
|
||||
{
|
||||
if (Meshes.empty())
|
||||
return 0;
|
||||
|
||||
return Meshes[0]->getMeshBuffer(material);
|
||||
}
|
||||
|
||||
//! set the hardware mapping hint, for driver
|
||||
void setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) override
|
||||
{
|
||||
for (u32 i = 0; i < Meshes.size(); ++i)
|
||||
Meshes[i]->setHardwareMappingHint(newMappingHint, buffer);
|
||||
}
|
||||
|
||||
//! flags the meshbuffer as changed, reloads hardware buffers
|
||||
void setDirty(E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) override
|
||||
{
|
||||
for (u32 i = 0; i < Meshes.size(); ++i)
|
||||
Meshes[i]->setDirty(buffer);
|
||||
}
|
||||
|
||||
//! All meshes defining the animated mesh
|
||||
std::vector<IMesh *> Meshes;
|
||||
|
||||
//! The bounding box of this mesh
|
||||
core::aabbox3d<f32> Box{{0.0f, 0.0f, 0.0f}};
|
||||
|
||||
//! Default animation speed of this mesh.
|
||||
f32 FramesPerSecond;
|
||||
|
||||
//! The type of the mesh.
|
||||
E_ANIMATED_MESH_TYPE Type;
|
||||
};
|
||||
|
||||
} // end namespace scene
|
||||
} // end namespace irr
|
|
@ -5,7 +5,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "IMesh.h"
|
||||
#include "IAnimatedMesh.h"
|
||||
#include "IMeshBuffer.h"
|
||||
#include "aabbox3d.h"
|
||||
|
||||
|
@ -14,7 +14,7 @@ namespace irr
|
|||
namespace scene
|
||||
{
|
||||
//! Simple implementation of the IMesh interface.
|
||||
struct SMesh final : public IMesh
|
||||
struct SMesh final : public IAnimatedMesh
|
||||
{
|
||||
//! constructor
|
||||
SMesh() {}
|
||||
|
@ -134,6 +134,15 @@ struct SMesh final : public IMesh
|
|||
|
||||
//! The bounding box of this mesh
|
||||
core::aabbox3d<f32> BoundingBox{{0, 0, 0}};
|
||||
|
||||
// Implement animated mesh interface as a static mesh.
|
||||
// Slightly hacky: Eventually should be consolidated with SSkinnedMesh,
|
||||
// with all the animation-related parts behind an optional.
|
||||
|
||||
virtual f32 getMaxFrameNumber() const override { return 0.0f; }
|
||||
virtual f32 getAnimationSpeed() const override { return 0.0f; }
|
||||
virtual void setAnimationSpeed(f32 fps) override {}
|
||||
E_ANIMATED_MESH_TYPE getMeshType() const override { return EAMT_STATIC; }
|
||||
};
|
||||
|
||||
} // end namespace scene
|
||||
|
|
|
@ -8,11 +8,18 @@
|
|||
#include "ISceneManager.h"
|
||||
#include "SMeshBuffer.h"
|
||||
#include "SSkinMeshBuffer.h"
|
||||
#include "aabbox3d.h"
|
||||
#include "irrMath.h"
|
||||
#include "irrTypes.h"
|
||||
#include "matrix4.h"
|
||||
#include "quaternion.h"
|
||||
#include "vector3d.h"
|
||||
#include "Transform.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
@ -37,9 +44,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;
|
||||
|
@ -64,14 +70,12 @@ 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;
|
||||
//! 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) const;
|
||||
|
||||
//! Animates joints based on frame input
|
||||
void animateMesh(f32 frame);
|
||||
|
||||
//! 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;
|
||||
|
@ -89,14 +93,15 @@ public:
|
|||
|
||||
void setTextureSlot(u32 meshbufNr, u32 textureSlot);
|
||||
|
||||
//! returns an axis aligned bounding box
|
||||
//! Returns bounding box of the mesh *in static pose*.
|
||||
const core::aabbox3d<f32> &getBoundingBox() const override {
|
||||
return BoundingBox;
|
||||
// TODO ideally we shouldn't be forced to implement this
|
||||
return StaticPoseBox;
|
||||
}
|
||||
|
||||
//! set user axis aligned bounding box
|
||||
//! Set bounding box of the mesh *in static pose*.
|
||||
void setBoundingBox(const core::aabbox3df &box) override {
|
||||
BoundingBox = box;
|
||||
StaticPoseBox = box;
|
||||
}
|
||||
|
||||
//! set the hardware mapping hint, for driver
|
||||
|
@ -140,28 +145,15 @@ 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();
|
||||
|
||||
//! Moves the mesh into static position.
|
||||
void resetAnimation();
|
||||
|
||||
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
|
||||
|
@ -236,7 +228,7 @@ public:
|
|||
|
||||
static core::quaternion interpolateValue(core::quaternion from, core::quaternion to, f32 time) {
|
||||
core::quaternion result;
|
||||
result.slerp(from, to, time, 0.001f);
|
||||
result.slerp(from, to, time);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -288,15 +280,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() {
|
||||
|
@ -309,16 +300,34 @@ public:
|
|||
//! Joints
|
||||
struct SJoint
|
||||
{
|
||||
SJoint() : GlobalSkinningSpace(false) {}
|
||||
SJoint() {}
|
||||
|
||||
//! The name of this joint
|
||||
std::optional<std::string> Name;
|
||||
|
||||
//! Local matrix of this joint
|
||||
core::matrix4 LocalMatrix;
|
||||
//! Local transformation to be set by loaders. Mutated by animation.
|
||||
using VariantTransform = std::variant<core::Transform, core::matrix4>;
|
||||
VariantTransform transform{core::Transform{}};
|
||||
|
||||
VariantTransform animate(f32 frame) const {
|
||||
if (keys.empty())
|
||||
return transform;
|
||||
|
||||
if (std::holds_alternative<core::matrix4>(transform)) {
|
||||
// .x lets animations override matrix transforms entirely,
|
||||
// which is what we implement here.
|
||||
// .gltf does not allow animation of nodes using matrix transforms.
|
||||
// Note that a decomposition into a TRS transform need not exist!
|
||||
core::Transform trs;
|
||||
keys.updateTransform(frame, trs);
|
||||
return {trs};
|
||||
}
|
||||
|
||||
auto trs = std::get<core::Transform>(transform);
|
||||
keys.updateTransform(frame, trs);
|
||||
return {trs};
|
||||
}
|
||||
|
||||
//! List of child joints
|
||||
std::vector<SJoint *> Children;
|
||||
|
||||
//! List of attached meshes
|
||||
std::vector<u32> AttachedMeshes;
|
||||
|
@ -329,42 +338,49 @@ public:
|
|||
//! Skin weights
|
||||
std::vector<SWeight> Weights;
|
||||
|
||||
//! Bounding box of all affected vertices, in local space
|
||||
core::aabbox3df LocalBoundingBox{{0, 0, 0}};
|
||||
|
||||
//! 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;
|
||||
|
||||
//! These should be set by loaders.
|
||||
core::vector3df Animatedposition;
|
||||
core::vector3df Animatedscale;
|
||||
core::quaternion Animatedrotation;
|
||||
|
||||
// The .x and .gltf formats pre-calculate this
|
||||
std::optional<core::matrix4> GlobalInversedMatrix;
|
||||
private:
|
||||
//! Internal members used by SkinnedMesh
|
||||
friend class SkinnedMesh;
|
||||
|
||||
bool GlobalSkinningSpace;
|
||||
void setParent(SJoint *parent) {
|
||||
ParentJointID = parent ? parent->JointID : std::optional<u16>{};
|
||||
}
|
||||
|
||||
u16 JointID; // TODO refactor away: pointers -> IDs (problem: .x loader abuses SJoint)
|
||||
std::optional<u16> ParentJointID;
|
||||
};
|
||||
|
||||
//! Animates joints based on frame input
|
||||
std::vector<SJoint::VariantTransform> animateMesh(f32 frame);
|
||||
|
||||
//! Calculates a bounding box given an animation in the form of global joint transforms.
|
||||
core::aabbox3df calculateBoundingBox(
|
||||
const std::vector<core::matrix4> &global_transforms);
|
||||
|
||||
void recalculateBaseBoundingBoxes();
|
||||
|
||||
const std::vector<SJoint *> &getAllJoints() const {
|
||||
return AllJoints;
|
||||
}
|
||||
|
||||
protected:
|
||||
void checkForAnimation();
|
||||
bool checkForAnimation() const;
|
||||
|
||||
void topoSortJoints();
|
||||
|
||||
void prepareForSkinning();
|
||||
|
||||
void calculateStaticBoundingBox();
|
||||
void calculateJointBoundingBoxes();
|
||||
void calculateBufferBoundingBoxes();
|
||||
|
||||
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,
|
||||
|
@ -376,25 +392,25 @@ 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.
|
||||
std::vector<std::vector<char>> Vertices_Moved;
|
||||
|
||||
core::aabbox3d<f32> BoundingBox{{0, 0, 0}};
|
||||
//! Bounding box of just the static parts of the mesh
|
||||
core::aabbox3df StaticPartsBox{{0, 0, 0}};
|
||||
|
||||
//! Bounding box of the mesh in static pose
|
||||
core::aabbox3df StaticPoseBox{{0, 0, 0}};
|
||||
|
||||
f32 EndFrame;
|
||||
f32 FramesPerSecond;
|
||||
|
||||
f32 LastAnimatedFrame;
|
||||
bool SkinnedLastFrame;
|
||||
|
||||
bool HasAnimation;
|
||||
bool PreparedForSkinning;
|
||||
bool AnimateNormals;
|
||||
bool HardwareSkinning;
|
||||
|
||||
SourceFormat SrcFormat;
|
||||
};
|
||||
|
|
42
irr/include/Transform.h
Normal file
42
irr/include/Transform.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include "irrMath.h"
|
||||
#include <matrix4.h>
|
||||
#include <vector3d.h>
|
||||
#include <quaternion.h>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
|
||||
struct Transform {
|
||||
vector3df translation;
|
||||
quaternion rotation;
|
||||
vector3df scale{1};
|
||||
|
||||
Transform interpolate(Transform to, f32 time) const
|
||||
{
|
||||
core::quaternion interpolated_rotation;
|
||||
interpolated_rotation.slerp(rotation, to.rotation, time);
|
||||
return {
|
||||
to.translation.getInterpolated(translation, time),
|
||||
interpolated_rotation,
|
||||
to.scale.getInterpolated(scale, time),
|
||||
};
|
||||
}
|
||||
|
||||
matrix4 buildMatrix() const
|
||||
{
|
||||
matrix4 T;
|
||||
T.setTranslation(translation);
|
||||
matrix4 R;
|
||||
rotation.getMatrix_transposed(R);
|
||||
matrix4 S;
|
||||
S.setScale(scale);
|
||||
return T * R * S;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace core
|
||||
} // end namespace irr
|
|
@ -180,7 +180,7 @@ public:
|
|||
linear interpolation.
|
||||
*/
|
||||
quaternion &slerp(quaternion q1, quaternion q2,
|
||||
f32 time, f32 threshold = .05f);
|
||||
f32 time, f32 threshold = .001f);
|
||||
|
||||
//! Set this quaternion to represent a rotation from angle and axis.
|
||||
/** Axis must be unit length.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue