mirror of
https://github.com/luanti-org/luanti.git
synced 2025-09-15 18:57:08 +00:00
Support floating-point animation frame numbers
This commit is contained in:
parent
323fc0a798
commit
06907aa99b
22 changed files with 111 additions and 105 deletions
|
@ -19,11 +19,8 @@ irr::scene::SMeshBuffer etc. */
|
|||
class IAnimatedMesh : public IMesh
|
||||
{
|
||||
public:
|
||||
//! Gets the frame count of the animated mesh.
|
||||
/** Note that the play-time is usually getFrameCount()-1 as it stops as soon as the last frame-key is reached.
|
||||
\return The amount of frames. If the amount is 1,
|
||||
it is a static, non animated mesh. */
|
||||
virtual u32 getFrameCount() const = 0;
|
||||
//! Gets the maximum frame number, 0 if the mesh is static.
|
||||
virtual f32 getMaxFrameNumber() const = 0;
|
||||
|
||||
//! Gets the animation speed of the animated mesh.
|
||||
/** \return The number of frames per second to play the
|
||||
|
@ -39,19 +36,10 @@ public:
|
|||
virtual void setAnimationSpeed(f32 fps) = 0;
|
||||
|
||||
//! Returns the IMesh interface for a frame.
|
||||
/** \param frame: Frame number as zero based index. The maximum
|
||||
frame number is getFrameCount() - 1;
|
||||
\param detailLevel: Level of detail. 0 is the lowest, 255 the
|
||||
highest level of detail. Most meshes will ignore the detail level.
|
||||
\param startFrameLoop: Because some animated meshes (.MD2) are
|
||||
blended between 2 static frames, and maybe animated in a loop,
|
||||
the startFrameLoop and the endFrameLoop have to be defined, to
|
||||
prevent the animation to be blended between frames which are
|
||||
outside of this loop.
|
||||
If startFrameLoop and endFrameLoop are both -1, they are ignored.
|
||||
\param endFrameLoop: see startFrameLoop.
|
||||
\return Returns the animated mesh based on a detail level. */
|
||||
virtual IMesh *getMesh(s32 frame, s32 detailLevel = 255, s32 startFrameLoop = -1, s32 endFrameLoop = -1) = 0;
|
||||
/** \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.
|
||||
|
|
|
@ -63,7 +63,7 @@ public:
|
|||
virtual void setCurrentFrame(f32 frame) = 0;
|
||||
|
||||
//! Sets the frame numbers between the animation is looped.
|
||||
/** The default is 0 to getFrameCount()-1 of the mesh.
|
||||
/** The default is 0 to getMaxFrameNumber() of the mesh.
|
||||
Number of played frames is end-start.
|
||||
It interpolates toward the last frame but stops when it is reached.
|
||||
It does not interpolate back to start even when looping.
|
||||
|
@ -71,7 +71,7 @@ public:
|
|||
\param begin: Start frame number of the loop.
|
||||
\param end: End frame number of the loop.
|
||||
\return True if successful, false if not. */
|
||||
virtual bool setFrameLoop(s32 begin, s32 end) = 0;
|
||||
virtual bool setFrameLoop(f32 begin, f32 end) = 0;
|
||||
|
||||
//! Sets the speed with which the animation is played.
|
||||
/** \param framesPerSecond: Frames per second played. */
|
||||
|
@ -108,9 +108,9 @@ public:
|
|||
//! Returns the currently displayed frame number.
|
||||
virtual f32 getFrameNr() const = 0;
|
||||
//! Returns the current start frame number.
|
||||
virtual s32 getStartFrame() const = 0;
|
||||
virtual f32 getStartFrame() const = 0;
|
||||
//! Returns the current end frame number.
|
||||
virtual s32 getEndFrame() const = 0;
|
||||
virtual f32 getEndFrame() const = 0;
|
||||
|
||||
//! Sets looping mode which is on by default.
|
||||
/** If set to false, animations will not be played looped. */
|
||||
|
|
|
@ -36,11 +36,9 @@ struct SAnimatedMesh final : public IAnimatedMesh
|
|||
mesh->drop();
|
||||
}
|
||||
|
||||
//! Gets the frame count of the animated mesh.
|
||||
/** \return Amount of frames. If the amount is 1, it is a static, non animated mesh. */
|
||||
u32 getFrameCount() const override
|
||||
f32 getMaxFrameNumber() const override
|
||||
{
|
||||
return static_cast<u32>(Meshes.size());
|
||||
return static_cast<f32>(Meshes.size() - 1);
|
||||
}
|
||||
|
||||
//! Gets the default animation speed of the animated mesh.
|
||||
|
@ -59,19 +57,14 @@ struct SAnimatedMesh final : public IAnimatedMesh
|
|||
}
|
||||
|
||||
//! Returns the IMesh interface for a frame.
|
||||
/** \param frame: Frame number as zero based index. The maximum frame number is
|
||||
getFrameCount() - 1;
|
||||
\param detailLevel: Level of detail. 0 is the lowest,
|
||||
255 the highest level of detail. Most meshes will ignore the detail level.
|
||||
\param startFrameLoop: start frame
|
||||
\param endFrameLoop: end frame
|
||||
\return The animated mesh based on a detail level. */
|
||||
IMesh *getMesh(s32 frame, s32 detailLevel = 255, s32 startFrameLoop = -1, s32 endFrameLoop = -1) override
|
||||
/** \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 0;
|
||||
return nullptr;
|
||||
|
||||
return Meshes[frame];
|
||||
return Meshes[static_cast<s32>(frame)];
|
||||
}
|
||||
|
||||
//! adds a Mesh
|
||||
|
|
|
@ -38,6 +38,12 @@ public:
|
|||
explicit constexpr vector2d(const std::array<T, 2> &arr) :
|
||||
X(arr[0]), Y(arr[1]) {}
|
||||
|
||||
template <class U>
|
||||
constexpr static vector2d<T> from(const vector2d<U> &other)
|
||||
{
|
||||
return {static_cast<T>(other.X), static_cast<T>(other.Y)};
|
||||
}
|
||||
|
||||
// operators
|
||||
|
||||
vector2d<T> operator-() const { return vector2d<T>(-X, -Y); }
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "IAnimatedMesh.h"
|
||||
#include "IFileSystem.h"
|
||||
#include "quaternion.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
|
@ -80,7 +81,7 @@ void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs)
|
|||
}
|
||||
|
||||
if (StartFrame == EndFrame) {
|
||||
CurrentFrameNr = (f32)StartFrame; // Support for non animated meshes
|
||||
CurrentFrameNr = StartFrame; // Support for non animated meshes
|
||||
} else if (Looping) {
|
||||
// play animation looped
|
||||
CurrentFrameNr += timeMs * FramesPerSecond;
|
||||
|
@ -89,26 +90,26 @@ void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs)
|
|||
// the last frame must be identical to first one with our current solution.
|
||||
if (FramesPerSecond > 0.f) { // forwards...
|
||||
if (CurrentFrameNr > EndFrame)
|
||||
CurrentFrameNr = StartFrame + fmodf(CurrentFrameNr - StartFrame, (f32)(EndFrame - StartFrame));
|
||||
CurrentFrameNr = StartFrame + fmodf(CurrentFrameNr - StartFrame, EndFrame - StartFrame);
|
||||
} else // backwards...
|
||||
{
|
||||
if (CurrentFrameNr < StartFrame)
|
||||
CurrentFrameNr = EndFrame - fmodf(EndFrame - CurrentFrameNr, (f32)(EndFrame - StartFrame));
|
||||
CurrentFrameNr = EndFrame - fmodf(EndFrame - CurrentFrameNr, EndFrame - StartFrame);
|
||||
}
|
||||
} else {
|
||||
// play animation non looped
|
||||
|
||||
CurrentFrameNr += timeMs * FramesPerSecond;
|
||||
if (FramesPerSecond > 0.f) { // forwards...
|
||||
if (CurrentFrameNr > (f32)EndFrame) {
|
||||
CurrentFrameNr = (f32)EndFrame;
|
||||
if (CurrentFrameNr > EndFrame) {
|
||||
CurrentFrameNr = EndFrame;
|
||||
if (LoopCallBack)
|
||||
LoopCallBack->OnAnimationEnd(this);
|
||||
}
|
||||
} else // backwards...
|
||||
{
|
||||
if (CurrentFrameNr < (f32)StartFrame) {
|
||||
CurrentFrameNr = (f32)StartFrame;
|
||||
if (CurrentFrameNr < StartFrame) {
|
||||
CurrentFrameNr = StartFrame;
|
||||
if (LoopCallBack)
|
||||
LoopCallBack->OnAnimationEnd(this);
|
||||
}
|
||||
|
@ -159,9 +160,7 @@ void CAnimatedMeshSceneNode::OnRegisterSceneNode()
|
|||
IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame()
|
||||
{
|
||||
if (Mesh->getMeshType() != EAMT_SKINNED) {
|
||||
s32 frameNr = (s32)getFrameNr();
|
||||
s32 frameBlend = (s32)(core::fract(getFrameNr()) * 1000.f);
|
||||
return Mesh->getMesh(frameNr, frameBlend, StartFrame, EndFrame);
|
||||
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.
|
||||
|
@ -331,33 +330,33 @@ void CAnimatedMeshSceneNode::render()
|
|||
}
|
||||
|
||||
//! Returns the current start frame number.
|
||||
s32 CAnimatedMeshSceneNode::getStartFrame() const
|
||||
f32 CAnimatedMeshSceneNode::getStartFrame() const
|
||||
{
|
||||
return StartFrame;
|
||||
}
|
||||
|
||||
//! Returns the current start frame number.
|
||||
s32 CAnimatedMeshSceneNode::getEndFrame() const
|
||||
f32 CAnimatedMeshSceneNode::getEndFrame() const
|
||||
{
|
||||
return EndFrame;
|
||||
}
|
||||
|
||||
//! sets the frames between the animation is looped.
|
||||
//! the default is 0 - MaximalFrameCount of the mesh.
|
||||
bool CAnimatedMeshSceneNode::setFrameLoop(s32 begin, s32 end)
|
||||
bool CAnimatedMeshSceneNode::setFrameLoop(f32 begin, f32 end)
|
||||
{
|
||||
const s32 maxFrameCount = Mesh->getFrameCount() - 1;
|
||||
const f32 maxFrame = Mesh->getMaxFrameNumber();
|
||||
if (end < begin) {
|
||||
StartFrame = core::s32_clamp(end, 0, maxFrameCount);
|
||||
EndFrame = core::s32_clamp(begin, StartFrame, maxFrameCount);
|
||||
StartFrame = std::clamp<f32>(end, 0, maxFrame);
|
||||
EndFrame = std::clamp<f32>(begin, StartFrame, maxFrame);
|
||||
} else {
|
||||
StartFrame = core::s32_clamp(begin, 0, maxFrameCount);
|
||||
EndFrame = core::s32_clamp(end, StartFrame, maxFrameCount);
|
||||
StartFrame = std::clamp<f32>(begin, 0, maxFrame);
|
||||
EndFrame = std::clamp<f32>(end, StartFrame, maxFrame);
|
||||
}
|
||||
if (FramesPerSecond < 0)
|
||||
setCurrentFrame((f32)EndFrame);
|
||||
setCurrentFrame(EndFrame);
|
||||
else
|
||||
setCurrentFrame((f32)StartFrame);
|
||||
setCurrentFrame(StartFrame);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -532,7 +531,7 @@ void CAnimatedMeshSceneNode::setMesh(IAnimatedMesh *mesh)
|
|||
// get materials and bounding box
|
||||
Box = Mesh->getBoundingBox();
|
||||
|
||||
IMesh *m = Mesh->getMesh(0, 0);
|
||||
IMesh *m = Mesh->getMesh(0);
|
||||
if (m) {
|
||||
Materials.clear();
|
||||
Materials.reallocate(m->getMeshBufferCount());
|
||||
|
@ -554,7 +553,7 @@ void CAnimatedMeshSceneNode::setMesh(IAnimatedMesh *mesh)
|
|||
|
||||
// get start and begin time
|
||||
setAnimationSpeed(Mesh->getAnimationSpeed()); // NOTE: This had been commented out (but not removed!) in r3526. Which caused meshloader-values for speed to be ignored unless users specified explicitly. Missing a test-case where this could go wrong so I put the code back in.
|
||||
setFrameLoop(0, Mesh->getFrameCount() - 1);
|
||||
setFrameLoop(0, Mesh->getMaxFrameNumber());
|
||||
}
|
||||
|
||||
//! updates the absolute position based on the relative and the parents position
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
//! sets the frames between the animation is looped.
|
||||
//! the default is 0 - MaximalFrameCount of the mesh.
|
||||
//! NOTE: setMesh will also change this value and set it to the full range of animations of the mesh
|
||||
bool setFrameLoop(s32 begin, s32 end) override;
|
||||
bool setFrameLoop(f32 begin, f32 end) override;
|
||||
|
||||
//! Sets looping mode which is on by default. If set to false,
|
||||
//! animations will not be looped.
|
||||
|
@ -93,9 +93,9 @@ public:
|
|||
//! Returns the current displayed frame number.
|
||||
f32 getFrameNr() const override;
|
||||
//! Returns the current start frame number.
|
||||
s32 getStartFrame() const override;
|
||||
f32 getStartFrame() const override;
|
||||
//! Returns the current end frame number.
|
||||
s32 getEndFrame() const override;
|
||||
f32 getEndFrame() const override;
|
||||
|
||||
//! 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 causing all mesh scene nodes
|
||||
|
@ -148,8 +148,8 @@ private:
|
|||
core::aabbox3d<f32> Box;
|
||||
IAnimatedMesh *Mesh;
|
||||
|
||||
s32 StartFrame;
|
||||
s32 EndFrame;
|
||||
f32 StartFrame;
|
||||
f32 EndFrame;
|
||||
f32 FramesPerSecond;
|
||||
f32 CurrentFrameNr;
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ s32 CMeshManipulator::getPolyCount(scene::IMesh *mesh) const
|
|||
//! Returns amount of polygons in mesh.
|
||||
s32 CMeshManipulator::getPolyCount(scene::IAnimatedMesh *mesh) const
|
||||
{
|
||||
if (mesh && mesh->getFrameCount() != 0)
|
||||
if (mesh && mesh->getMaxFrameNumber() != 0)
|
||||
return getPolyCount(mesh->getMesh(0));
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -111,11 +111,9 @@ CSkinnedMesh::~CSkinnedMesh()
|
|||
}
|
||||
}
|
||||
|
||||
//! returns the amount of frames in milliseconds.
|
||||
//! If the amount is 1, it is a static (=non animated) mesh.
|
||||
u32 CSkinnedMesh::getFrameCount() const
|
||||
f32 CSkinnedMesh::getMaxFrameNumber() const
|
||||
{
|
||||
return core::floor32(EndFrame + 1.f);
|
||||
return EndFrame;
|
||||
}
|
||||
|
||||
//! Gets the default animation speed of the animated mesh.
|
||||
|
@ -133,14 +131,14 @@ void CSkinnedMesh::setAnimationSpeed(f32 fps)
|
|||
FramesPerSecond = fps;
|
||||
}
|
||||
|
||||
//! returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail. Note, that some Meshes will ignore the detail level.
|
||||
IMesh *CSkinnedMesh::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)
|
||||
//! returns the animated mesh based
|
||||
IMesh *CSkinnedMesh::getMesh(f32 frame)
|
||||
{
|
||||
// animate(frame,startFrameLoop, endFrameLoop);
|
||||
if (frame == -1)
|
||||
return this;
|
||||
|
||||
animateMesh((f32)frame, 1.0f);
|
||||
animateMesh(frame, 1.0f);
|
||||
skinMesh();
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ public:
|
|||
//! destructor
|
||||
virtual ~CSkinnedMesh();
|
||||
|
||||
//! returns the amount of frames. If the amount is 1, it is a static (=non animated) mesh.
|
||||
u32 getFrameCount() const override;
|
||||
//! If the duration is 0, it is a static (=non animated) mesh.
|
||||
f32 getMaxFrameNumber() const override;
|
||||
|
||||
//! 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. */
|
||||
|
@ -39,8 +39,8 @@ public:
|
|||
The actual speed is set in the scene node the mesh is instantiated in.*/
|
||||
void setAnimationSpeed(f32 fps) override;
|
||||
|
||||
//! returns the animated mesh based on a detail level (which is ignored)
|
||||
IMesh *getMesh(s32 frame, s32 detailLevel = 255, s32 startFrameLoop = -1, s32 endFrameLoop = -1) override;
|
||||
//! returns the animated mesh for the given frame
|
||||
IMesh *getMesh(f32) override;
|
||||
|
||||
//! Animates this mesh's joints based on frame input
|
||||
//! blend: {0-old position, 1-New position}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue