Support floating-point animation frame numbers

This commit is contained in:
Lars Mueller 2024-09-02 21:11:08 +02:00 committed by Lars Müller
parent 323fc0a798
commit 06907aa99b
22 changed files with 111 additions and 105 deletions

@ -8059,8 +8059,7 @@ child will follow movement and rotation of that bone.
* Animation interpolates towards the end frame but stops when it is reached * Animation interpolates towards the end frame but stops when it is reached
* If looped, there is no interpolation back to the start frame * If looped, there is no interpolation back to the start frame
* If looped, the model should look identical at start and end * If looped, the model should look identical at start and end
* Only integer numbers are supported * default: `{x=1.0, y=1.0}`
* default: `{x=1, y=1}`
* `frame_speed`: How fast the animation plays, in frames per second (number) * `frame_speed`: How fast the animation plays, in frames per second (number)
* default: `15.0` * default: `15.0`
* `frame_blend`: number, default: `0.0` * `frame_blend`: number, default: `0.0`

@ -55,10 +55,20 @@ minetest.register_entity("gltf:simple_skin", {
end end
}) })
-- Note: Model has an animation, but we can use it as a static test nevertheless
-- The claws rendering incorrectly from one side is expected behavior: -- The claws rendering incorrectly from one side is expected behavior:
-- They use an unsupported double-sided material. -- They use an unsupported double-sided material.
register_entity("frog", {"gltf_frog.png"}, false) minetest.register_entity("gltf:frog", {
initial_properties = {
visual = "mesh",
mesh = "gltf_frog.gltf",
textures = {"gltf_frog.png"},
backface_culling = false
},
on_activate = function(self)
self.object:set_animation({x = 0, y = 0.75}, 1)
end
})
minetest.register_node("gltf:frog", { minetest.register_node("gltf:frog", {
description = "glTF frog, but it's a node", description = "glTF frog, but it's a node",

@ -19,11 +19,8 @@ irr::scene::SMeshBuffer etc. */
class IAnimatedMesh : public IMesh class IAnimatedMesh : public IMesh
{ {
public: public:
//! Gets the frame count of the animated mesh. //! Gets the maximum frame number, 0 if the mesh is static.
/** Note that the play-time is usually getFrameCount()-1 as it stops as soon as the last frame-key is reached. virtual f32 getMaxFrameNumber() const = 0;
\return The amount of frames. If the amount is 1,
it is a static, non animated mesh. */
virtual u32 getFrameCount() const = 0;
//! Gets the animation speed of the animated mesh. //! Gets the animation speed of the animated mesh.
/** \return The number of frames per second to play the /** \return The number of frames per second to play the
@ -39,19 +36,10 @@ public:
virtual void setAnimationSpeed(f32 fps) = 0; virtual void setAnimationSpeed(f32 fps) = 0;
//! Returns the IMesh interface for a frame. //! Returns the IMesh interface for a frame.
/** \param frame: Frame number as zero based index. The maximum /** \param frame: Frame number, >= 0, <= getMaxFrameNumber()
frame number is getFrameCount() - 1; Linear interpolation is used if this is between two frames.
\param detailLevel: Level of detail. 0 is the lowest, 255 the \return Returns the animated mesh for the given frame */
highest level of detail. Most meshes will ignore the detail level. virtual IMesh *getMesh(f32 frame) = 0;
\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;
//! Returns the type of the animated mesh. //! Returns the type of the animated mesh.
/** In most cases it is not necessary to use this method. /** In most cases it is not necessary to use this method.

@ -63,7 +63,7 @@ public:
virtual void setCurrentFrame(f32 frame) = 0; virtual void setCurrentFrame(f32 frame) = 0;
//! Sets the frame numbers between the animation is looped. //! 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. Number of played frames is end-start.
It interpolates toward the last frame but stops when it is reached. It interpolates toward the last frame but stops when it is reached.
It does not interpolate back to start even when looping. It does not interpolate back to start even when looping.
@ -71,7 +71,7 @@ public:
\param begin: Start frame number of the loop. \param begin: Start frame number of the loop.
\param end: End frame number of the loop. \param end: End frame number of the loop.
\return True if successful, false if not. */ \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. //! Sets the speed with which the animation is played.
/** \param framesPerSecond: Frames per second played. */ /** \param framesPerSecond: Frames per second played. */
@ -108,9 +108,9 @@ public:
//! Returns the currently displayed frame number. //! Returns the currently displayed frame number.
virtual f32 getFrameNr() const = 0; virtual f32 getFrameNr() const = 0;
//! Returns the current start frame number. //! Returns the current start frame number.
virtual s32 getStartFrame() const = 0; virtual f32 getStartFrame() const = 0;
//! Returns the current end frame number. //! Returns the current end frame number.
virtual s32 getEndFrame() const = 0; virtual f32 getEndFrame() const = 0;
//! Sets looping mode which is on by default. //! Sets looping mode which is on by default.
/** If set to false, animations will not be played looped. */ /** If set to false, animations will not be played looped. */

@ -36,11 +36,9 @@ struct SAnimatedMesh final : public IAnimatedMesh
mesh->drop(); mesh->drop();
} }
//! Gets the frame count of the animated mesh. f32 getMaxFrameNumber() const override
/** \return Amount of frames. If the amount is 1, it is a static, non animated mesh. */
u32 getFrameCount() const override
{ {
return static_cast<u32>(Meshes.size()); return static_cast<f32>(Meshes.size() - 1);
} }
//! Gets the default animation speed of the animated mesh. //! 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. //! Returns the IMesh interface for a frame.
/** \param frame: Frame number as zero based index. The maximum frame number is /** \param frame: Frame number as zero based index.
getFrameCount() - 1; \return The animated mesh based for the given frame */
\param detailLevel: Level of detail. 0 is the lowest, IMesh *getMesh(f32 frame) override
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
{ {
if (Meshes.empty()) if (Meshes.empty())
return 0; return nullptr;
return Meshes[frame]; return Meshes[static_cast<s32>(frame)];
} }
//! adds a Mesh //! adds a Mesh

@ -38,6 +38,12 @@ public:
explicit constexpr vector2d(const std::array<T, 2> &arr) : explicit constexpr vector2d(const std::array<T, 2> &arr) :
X(arr[0]), Y(arr[1]) {} 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 // operators
vector2d<T> operator-() const { return vector2d<T>(-X, -Y); } vector2d<T> operator-() const { return vector2d<T>(-X, -Y); }

@ -16,6 +16,7 @@
#include "IAnimatedMesh.h" #include "IAnimatedMesh.h"
#include "IFileSystem.h" #include "IFileSystem.h"
#include "quaternion.h" #include "quaternion.h"
#include <algorithm>
namespace irr namespace irr
{ {
@ -80,7 +81,7 @@ void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs)
} }
if (StartFrame == EndFrame) { if (StartFrame == EndFrame) {
CurrentFrameNr = (f32)StartFrame; // Support for non animated meshes CurrentFrameNr = StartFrame; // Support for non animated meshes
} else if (Looping) { } else if (Looping) {
// play animation looped // play animation looped
CurrentFrameNr += timeMs * FramesPerSecond; 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. // the last frame must be identical to first one with our current solution.
if (FramesPerSecond > 0.f) { // forwards... if (FramesPerSecond > 0.f) { // forwards...
if (CurrentFrameNr > EndFrame) if (CurrentFrameNr > EndFrame)
CurrentFrameNr = StartFrame + fmodf(CurrentFrameNr - StartFrame, (f32)(EndFrame - StartFrame)); CurrentFrameNr = StartFrame + fmodf(CurrentFrameNr - StartFrame, EndFrame - StartFrame);
} else // backwards... } else // backwards...
{ {
if (CurrentFrameNr < StartFrame) if (CurrentFrameNr < StartFrame)
CurrentFrameNr = EndFrame - fmodf(EndFrame - CurrentFrameNr, (f32)(EndFrame - StartFrame)); CurrentFrameNr = EndFrame - fmodf(EndFrame - CurrentFrameNr, EndFrame - StartFrame);
} }
} else { } else {
// play animation non looped // play animation non looped
CurrentFrameNr += timeMs * FramesPerSecond; CurrentFrameNr += timeMs * FramesPerSecond;
if (FramesPerSecond > 0.f) { // forwards... if (FramesPerSecond > 0.f) { // forwards...
if (CurrentFrameNr > (f32)EndFrame) { if (CurrentFrameNr > EndFrame) {
CurrentFrameNr = (f32)EndFrame; CurrentFrameNr = EndFrame;
if (LoopCallBack) if (LoopCallBack)
LoopCallBack->OnAnimationEnd(this); LoopCallBack->OnAnimationEnd(this);
} }
} else // backwards... } else // backwards...
{ {
if (CurrentFrameNr < (f32)StartFrame) { if (CurrentFrameNr < StartFrame) {
CurrentFrameNr = (f32)StartFrame; CurrentFrameNr = StartFrame;
if (LoopCallBack) if (LoopCallBack)
LoopCallBack->OnAnimationEnd(this); LoopCallBack->OnAnimationEnd(this);
} }
@ -159,9 +160,7 @@ void CAnimatedMeshSceneNode::OnRegisterSceneNode()
IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame() IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame()
{ {
if (Mesh->getMeshType() != EAMT_SKINNED) { if (Mesh->getMeshType() != EAMT_SKINNED) {
s32 frameNr = (s32)getFrameNr(); return Mesh->getMesh(getFrameNr());
s32 frameBlend = (s32)(core::fract(getFrameNr()) * 1000.f);
return Mesh->getMesh(frameNr, frameBlend, StartFrame, EndFrame);
} else { } else {
// As multiple scene nodes may be sharing the same skinned mesh, we have to // 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. // 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. //! Returns the current start frame number.
s32 CAnimatedMeshSceneNode::getStartFrame() const f32 CAnimatedMeshSceneNode::getStartFrame() const
{ {
return StartFrame; return StartFrame;
} }
//! Returns the current start frame number. //! Returns the current start frame number.
s32 CAnimatedMeshSceneNode::getEndFrame() const f32 CAnimatedMeshSceneNode::getEndFrame() const
{ {
return EndFrame; return EndFrame;
} }
//! sets the frames between the animation is looped. //! sets the frames between the animation is looped.
//! the default is 0 - MaximalFrameCount of the mesh. //! 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) { if (end < begin) {
StartFrame = core::s32_clamp(end, 0, maxFrameCount); StartFrame = std::clamp<f32>(end, 0, maxFrame);
EndFrame = core::s32_clamp(begin, StartFrame, maxFrameCount); EndFrame = std::clamp<f32>(begin, StartFrame, maxFrame);
} else { } else {
StartFrame = core::s32_clamp(begin, 0, maxFrameCount); StartFrame = std::clamp<f32>(begin, 0, maxFrame);
EndFrame = core::s32_clamp(end, StartFrame, maxFrameCount); EndFrame = std::clamp<f32>(end, StartFrame, maxFrame);
} }
if (FramesPerSecond < 0) if (FramesPerSecond < 0)
setCurrentFrame((f32)EndFrame); setCurrentFrame(EndFrame);
else else
setCurrentFrame((f32)StartFrame); setCurrentFrame(StartFrame);
return true; return true;
} }
@ -532,7 +531,7 @@ void CAnimatedMeshSceneNode::setMesh(IAnimatedMesh *mesh)
// get materials and bounding box // get materials and bounding box
Box = Mesh->getBoundingBox(); Box = Mesh->getBoundingBox();
IMesh *m = Mesh->getMesh(0, 0); IMesh *m = Mesh->getMesh(0);
if (m) { if (m) {
Materials.clear(); Materials.clear();
Materials.reallocate(m->getMeshBufferCount()); Materials.reallocate(m->getMeshBufferCount());
@ -554,7 +553,7 @@ void CAnimatedMeshSceneNode::setMesh(IAnimatedMesh *mesh)
// get start and begin time // 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. 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 //! 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. //! sets the frames between the animation is looped.
//! the default is 0 - MaximalFrameCount of the mesh. //! 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 //! 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, //! Sets looping mode which is on by default. If set to false,
//! animations will not be looped. //! animations will not be looped.
@ -93,9 +93,9 @@ public:
//! Returns the current displayed frame number. //! Returns the current displayed frame number.
f32 getFrameNr() const override; f32 getFrameNr() const override;
//! Returns the current start frame number. //! Returns the current start frame number.
s32 getStartFrame() const override; f32 getStartFrame() const override;
//! Returns the current end frame number. //! 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. //! 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 /* 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; core::aabbox3d<f32> Box;
IAnimatedMesh *Mesh; IAnimatedMesh *Mesh;
s32 StartFrame; f32 StartFrame;
s32 EndFrame; f32 EndFrame;
f32 FramesPerSecond; f32 FramesPerSecond;
f32 CurrentFrameNr; f32 CurrentFrameNr;

@ -193,7 +193,7 @@ s32 CMeshManipulator::getPolyCount(scene::IMesh *mesh) const
//! Returns amount of polygons in mesh. //! Returns amount of polygons in mesh.
s32 CMeshManipulator::getPolyCount(scene::IAnimatedMesh *mesh) const s32 CMeshManipulator::getPolyCount(scene::IAnimatedMesh *mesh) const
{ {
if (mesh && mesh->getFrameCount() != 0) if (mesh && mesh->getMaxFrameNumber() != 0)
return getPolyCount(mesh->getMesh(0)); return getPolyCount(mesh->getMesh(0));
return 0; return 0;

@ -111,11 +111,9 @@ CSkinnedMesh::~CSkinnedMesh()
} }
} }
//! returns the amount of frames in milliseconds. f32 CSkinnedMesh::getMaxFrameNumber() const
//! If the amount is 1, it is a static (=non animated) mesh.
u32 CSkinnedMesh::getFrameCount() const
{ {
return core::floor32(EndFrame + 1.f); return EndFrame;
} }
//! Gets the default animation speed of the animated mesh. //! Gets the default animation speed of the animated mesh.
@ -133,14 +131,14 @@ void CSkinnedMesh::setAnimationSpeed(f32 fps)
FramesPerSecond = 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. //! returns the animated mesh based
IMesh *CSkinnedMesh::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop) IMesh *CSkinnedMesh::getMesh(f32 frame)
{ {
// animate(frame,startFrameLoop, endFrameLoop); // animate(frame,startFrameLoop, endFrameLoop);
if (frame == -1) if (frame == -1)
return this; return this;
animateMesh((f32)frame, 1.0f); animateMesh(frame, 1.0f);
skinMesh(); skinMesh();
return this; return this;
} }

@ -27,8 +27,8 @@ public:
//! destructor //! destructor
virtual ~CSkinnedMesh(); virtual ~CSkinnedMesh();
//! returns the amount of frames. If the amount is 1, it is a static (=non animated) mesh. //! If the duration is 0, it is a static (=non animated) mesh.
u32 getFrameCount() const override; f32 getMaxFrameNumber() const override;
//! Gets the default animation speed of the animated mesh. //! 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. */ /** \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.*/ The actual speed is set in the scene node the mesh is instantiated in.*/
void setAnimationSpeed(f32 fps) override; void setAnimationSpeed(f32 fps) override;
//! returns the animated mesh based on a detail level (which is ignored) //! returns the animated mesh for the given frame
IMesh *getMesh(s32 frame, s32 detailLevel = 255, s32 startFrameLoop = -1, s32 endFrameLoop = -1) override; IMesh *getMesh(f32) override;
//! Animates this mesh's joints based on frame input //! Animates this mesh's joints based on frame input
//! blend: {0-old position, 1-New position} //! blend: {0-old position, 1-New position}

@ -1052,7 +1052,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
walking = true; walking = true;
} }
v2s32 new_anim = v2s32(0,0); v2f new_anim(0,0);
bool allow_update = false; bool allow_update = false;
// increase speed if using fast or flying fast // increase speed if using fast or flying fast
@ -1799,10 +1799,9 @@ void GenericCAO::processMessage(const std::string &data)
phys.speed_walk = override_speed_walk; phys.speed_walk = override_speed_walk;
} }
} else if (cmd == AO_CMD_SET_ANIMATION) { } else if (cmd == AO_CMD_SET_ANIMATION) {
// TODO: change frames send as v2s32 value
v2f range = readV2F32(is); v2f range = readV2F32(is);
if (!m_is_local_player) { if (!m_is_local_player) {
m_animation_range = v2s32((s32)range.X, (s32)range.Y); m_animation_range = range;
m_animation_speed = readF32(is); m_animation_speed = readF32(is);
m_animation_blend = readF32(is); m_animation_blend = readF32(is);
// these are sent inverted so we get true when the server sends nothing // these are sent inverted so we get true when the server sends nothing
@ -1812,7 +1811,7 @@ void GenericCAO::processMessage(const std::string &data)
LocalPlayer *player = m_env->getLocalPlayer(); LocalPlayer *player = m_env->getLocalPlayer();
if(player->last_animation == LocalPlayerAnimation::NO_ANIM) if(player->last_animation == LocalPlayerAnimation::NO_ANIM)
{ {
m_animation_range = v2s32((s32)range.X, (s32)range.Y); m_animation_range = range;
m_animation_speed = readF32(is); m_animation_speed = readF32(is);
m_animation_blend = readF32(is); m_animation_blend = readF32(is);
// these are sent inverted so we get true when the server sends nothing // these are sent inverted so we get true when the server sends nothing

@ -99,7 +99,7 @@ private:
v2s16 m_tx_basepos; v2s16 m_tx_basepos;
bool m_initial_tx_basepos_set = false; bool m_initial_tx_basepos_set = false;
bool m_tx_select_horiz_by_yawpitch = false; bool m_tx_select_horiz_by_yawpitch = false;
v2s32 m_animation_range; v2f m_animation_range;
float m_animation_speed = 15.0f; float m_animation_speed = 15.0f;
float m_animation_blend = 0.0f; float m_animation_blend = 0.0f;
bool m_animation_loop = true; bool m_animation_loop = true;

@ -157,7 +157,7 @@ void GUIScene::setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES> &sty
/** /**
* Sets the frame loop range for the mesh * Sets the frame loop range for the mesh
*/ */
void GUIScene::setFrameLoop(s32 begin, s32 end) void GUIScene::setFrameLoop(f32 begin, f32 end)
{ {
if (m_mesh->getStartFrame() != begin || m_mesh->getEndFrame() != end) if (m_mesh->getStartFrame() != begin || m_mesh->getEndFrame() != end)
m_mesh->setFrameLoop(begin, end); m_mesh->setFrameLoop(begin, end);

@ -36,7 +36,7 @@ public:
scene::IAnimatedMeshSceneNode *setMesh(scene::IAnimatedMesh *mesh = nullptr); scene::IAnimatedMeshSceneNode *setMesh(scene::IAnimatedMesh *mesh = nullptr);
void setTexture(u32 idx, video::ITexture *texture); void setTexture(u32 idx, video::ITexture *texture);
void setBackgroundColor(const video::SColor &color) noexcept { m_bgcolor = color; }; void setBackgroundColor(const video::SColor &color) noexcept { m_bgcolor = color; };
void setFrameLoop(s32 begin, s32 end); void setFrameLoop(f32 begin, f32 end);
void setAnimationSpeed(f32 speed); void setAnimationSpeed(f32 speed);
void enableMouseControl(bool enable) noexcept { m_mouse_ctrl = enable; }; void enableMouseControl(bool enable) noexcept { m_mouse_ctrl = enable; };
void setRotation(v2f rot) noexcept { m_custom_rot = rot; }; void setRotation(v2f rot) noexcept { m_custom_rot = rot; };

@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/client.h" #include "client/client.h"
#include "irr_v2d.h"
#include "util/base64.h" #include "util/base64.h"
#include "client/camera.h" #include "client/camera.h"
#include "client/mesh_generator_thread.h" #include "client/mesh_generator_thread.h"
@ -1516,11 +1517,15 @@ void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
LocalPlayer *player = m_env.getLocalPlayer(); LocalPlayer *player = m_env.getLocalPlayer();
assert(player != NULL); assert(player != NULL);
*pkt >> player->local_animations[0]; for (int i = 0; i < 4; ++i) {
*pkt >> player->local_animations[1]; if (getProtoVersion() >= 46) {
*pkt >> player->local_animations[2]; *pkt >> player->local_animations[i];
*pkt >> player->local_animations[3]; } else {
*pkt >> player->local_animation_speed; v2s32 local_animation;
*pkt >> local_animation;
player->local_animations[i] = v2f::from(local_animation);
}
}
player->last_animation = LocalPlayerAnimation::NO_ANIM; player->last_animation = LocalPlayerAnimation::NO_ANIM;
} }

@ -57,6 +57,7 @@
old servers. old servers.
Rename TOCLIENT_DEATHSCREEN to TOCLIENT_DEATHSCREEN_LEGACY Rename TOCLIENT_DEATHSCREEN to TOCLIENT_DEATHSCREEN_LEGACY
Rename TOSERVER_RESPAWN to TOSERVER_RESPAWN_LEGACY Rename TOSERVER_RESPAWN to TOSERVER_RESPAWN_LEGACY
Support float animation frame numbers in TOCLIENT_LOCAL_PLAYER_ANIMATIONS
[scheduled bump for 5.10.0] [scheduled bump for 5.10.0]
*/ */

@ -203,7 +203,7 @@ public:
f32 movement_liquid_sink; f32 movement_liquid_sink;
f32 movement_gravity; f32 movement_gravity;
v2s32 local_animations[4]; v2f local_animations[4];
float local_animation_speed; float local_animation_speed;
std::string inventory_formspec; std::string inventory_formspec;

@ -113,14 +113,14 @@ public:
inline void setModified(const bool x) { m_dirty = x; } inline void setModified(const bool x) { m_dirty = x; }
void setLocalAnimations(v2s32 frames[4], float frame_speed) void setLocalAnimations(v2f frames[4], float frame_speed)
{ {
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
local_animations[i] = frames[i]; local_animations[i] = frames[i];
local_animation_speed = frame_speed; local_animation_speed = frame_speed;
} }
void getLocalAnimations(v2s32 *frames, float *frame_speed) void getLocalAnimations(v2f *frames, float *frame_speed)
{ {
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
frames[i] = local_animations[i]; frames[i] = local_animations[i];

@ -433,10 +433,10 @@ int ObjectRef::l_set_local_animation(lua_State *L)
if (player == nullptr) if (player == nullptr)
return 0; return 0;
v2s32 frames[4]; v2f frames[4];
for (int i=0;i<4;i++) { for (int i=0;i<4;i++) {
if (!lua_isnil(L, 2+1)) if (!lua_isnil(L, 2+1))
frames[i] = read_v2s32(L, 2+i); frames[i] = read_v2f(L, 2+i);
} }
float frame_speed = readParam<float>(L, 6, 30.0f); float frame_speed = readParam<float>(L, 6, 30.0f);
@ -453,12 +453,12 @@ int ObjectRef::l_get_local_animation(lua_State *L)
if (player == nullptr) if (player == nullptr)
return 0; return 0;
v2s32 frames[4]; v2f frames[4];
float frame_speed; float frame_speed;
player->getLocalAnimations(frames, &frame_speed); player->getLocalAnimations(frames, &frame_speed);
for (const v2s32 &frame : frames) { for (const v2f &frame : frames) {
push_v2s32(L, frame); push_v2f(L, frame);
} }
lua_pushnumber(L, frame_speed); lua_pushnumber(L, frame_speed);

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iostream> #include <iostream>
#include <queue> #include <queue>
#include <algorithm> #include <algorithm>
#include "irr_v2d.h"
#include "network/connection.h" #include "network/connection.h"
#include "network/networkprotocol.h" #include "network/networkprotocol.h"
#include "network/serveropcodes.h" #include "network/serveropcodes.h"
@ -1987,14 +1988,21 @@ void Server::SendPlayerFov(session_t peer_id)
Send(&pkt); Send(&pkt);
} }
void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4], void Server::SendLocalPlayerAnimations(session_t peer_id, v2f animation_frames[4],
f32 animation_speed) f32 animation_speed)
{ {
NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0, NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
peer_id); peer_id);
pkt << animation_frames[0] << animation_frames[1] << animation_frames[2] for (int i = 0; i < 4; ++i) {
<< animation_frames[3] << animation_speed; if (m_clients.getProtocolVersion(peer_id) >= 46) {
pkt << animation_frames[i];
} else {
pkt << v2s32::from(animation_frames[i]);
}
}
pkt << animation_speed;
Send(&pkt); Send(&pkt);
} }
@ -3424,7 +3432,7 @@ Address Server::getPeerAddress(session_t peer_id)
} }
void Server::setLocalPlayerAnimations(RemotePlayer *player, void Server::setLocalPlayerAnimations(RemotePlayer *player,
v2s32 animation_frames[4], f32 frame_speed) v2f animation_frames[4], f32 frame_speed)
{ {
sanity_check(player); sanity_check(player);
player->setLocalAnimations(animation_frames, frame_speed); player->setLocalAnimations(animation_frames, frame_speed);

@ -344,7 +344,7 @@ public:
Address getPeerAddress(session_t peer_id); Address getPeerAddress(session_t peer_id);
void setLocalPlayerAnimations(RemotePlayer *player, v2s32 animation_frames[4], void setLocalPlayerAnimations(RemotePlayer *player, v2f animation_frames[4],
f32 frame_speed); f32 frame_speed);
void setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third, v3f third_front); void setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third, v3f third_front);
@ -501,7 +501,7 @@ private:
virtual void SendChatMessage(session_t peer_id, const ChatMessage &message); virtual void SendChatMessage(session_t peer_id, const ChatMessage &message);
void SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed); void SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed);
void SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4], void SendLocalPlayerAnimations(session_t peer_id, v2f animation_frames[4],
f32 animation_speed); f32 animation_speed);
void SendEyeOffset(session_t peer_id, v3f first, v3f third, v3f third_front); void SendEyeOffset(session_t peer_id, v3f first, v3f third, v3f third_front);
void SendPlayerPrivileges(session_t peer_id); void SendPlayerPrivileges(session_t peer_id);