1
0
mirror of https://github.com/luanti-org/luanti.git synced 2025-11-01 15:55:26 +01:00

Fix interpolating to identity bone overrides (#16609)

The previous code immediately dropped identity overrides, even if there still was an interpolation to be done.
Also a little bit of cleanup, and setting an appropriate identity default for the scale property when interpolating.

For modders: As a workaround, you can add a tiny offset so that overrides aren't identity overrides.
This commit is contained in:
Lars Müller
2025-10-29 10:25:50 +01:00
committed by GitHub
parent 93ccb4b355
commit 7331156650
5 changed files with 49 additions and 41 deletions

View File

@@ -95,7 +95,7 @@ core.register_entity("testentities:sam", {
self.object:set_bone_override("Head", { self.object:set_bone_override("Head", {
scale = { scale = {
vec = vector.new(s, s, s), vec = vector.new(s, s, s),
absolute = true, absolute = false,
interpolation = self._head_anim_duration, interpolation = self._head_anim_duration,
}, },
}) })

View File

@@ -66,12 +66,12 @@ struct BoneOverride
v3f previous; v3f previous;
v3f vector; v3f vector;
bool absolute = false; bool absolute = false;
f32 interp_timer = 0; f32 interp_duration = 0.0f;
} position; } position;
v3f getPosition(v3f anim_pos) const { v3f getPosition(v3f anim_pos) const {
f32 progress = dtime_passed / position.interp_timer; f32 progress = dtime_passed / position.interp_duration;
if (progress > 1.0f || position.interp_timer == 0.0f) if (progress > 1.0f || position.interp_duration == 0.0f)
progress = 1.0f; progress = 1.0f;
return position.vector.getInterpolated(position.previous, progress) return position.vector.getInterpolated(position.previous, progress)
+ (position.absolute ? v3f() : anim_pos); + (position.absolute ? v3f() : anim_pos);
@@ -85,14 +85,14 @@ struct BoneOverride
// so that we can return them in the appropriate getters // so that we can return them in the appropriate getters
v3f next_radians; v3f next_radians;
bool absolute = false; bool absolute = false;
f32 interp_timer = 0; f32 interp_duration = 0.0f;
} rotation; } rotation;
v3f getRotationEulerDeg(v3f anim_rot_euler) const { v3f getRotationEulerDeg(v3f anim_rot_euler) const {
core::quaternion rot; core::quaternion rot;
f32 progress = dtime_passed / rotation.interp_timer; f32 progress = dtime_passed / rotation.interp_duration;
if (progress > 1.0f || rotation.interp_timer == 0.0f) if (progress > 1.0f || rotation.interp_duration == 0.0f)
progress = 1.0f; progress = 1.0f;
rot.slerp(rotation.previous, rotation.next, progress); rot.slerp(rotation.previous, rotation.next, progress);
if (!rotation.absolute) { if (!rotation.absolute) {
@@ -107,27 +107,35 @@ struct BoneOverride
struct ScaleProperty struct ScaleProperty
{ {
v3f previous; v3f previous = v3f(1.0f);
v3f vector{1, 1, 1}; v3f vector = v3f(1.0f);
bool absolute = false; bool absolute = false;
f32 interp_timer = 0; f32 interp_duration = 0.0f;
} scale; } scale;
v3f getScale(v3f anim_scale) const { v3f getScale(v3f anim_scale) const {
f32 progress = dtime_passed / scale.interp_timer; f32 progress = dtime_passed / scale.interp_duration;
if (progress > 1.0f || scale.interp_timer == 0.0f) if (progress > 1.0f || scale.interp_duration == 0.0f)
progress = 1.0f; progress = 1.0f;
return scale.vector.getInterpolated(scale.previous, progress) return scale.vector.getInterpolated(scale.previous, progress)
* (scale.absolute ? v3f(1) : anim_scale); * (scale.absolute ? v3f(1.0f) : anim_scale);
} }
f32 dtime_passed = 0; f32 dtime_passed = 0.0f;
bool finishedInterpolation() const
{
return dtime_passed >= std::max(std::max(
position.interp_duration, rotation.interp_duration),
scale.interp_duration);
}
bool isIdentity() const bool isIdentity() const
{ {
return !position.absolute && position.vector == v3f() return finishedInterpolation()
&& !position.absolute && position.vector == v3f()
&& !rotation.absolute && rotation.next == core::quaternion() && !rotation.absolute && rotation.next == core::quaternion()
&& !scale.absolute && scale.vector == v3f(1); && !scale.absolute && scale.vector == v3f(1.0f);
} }
}; };

View File

@@ -702,17 +702,21 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr)
}); });
m_animated_meshnode->setOnAnimateCallback([&](f32 dtime) { m_animated_meshnode->setOnAnimateCallback([&](f32 dtime) {
for (auto &it : m_bone_override) { for (auto it = m_bone_override.begin(); it != m_bone_override.end();) {
auto* bone = m_animated_meshnode->getJointNode(it.first.c_str()); BoneOverride &props = it->second;
if (!bone)
continue;
BoneOverride &props = it.second;
props.dtime_passed += dtime; props.dtime_passed += dtime;
bone->setPosition(props.getPosition(bone->getPosition())); if (props.isIdentity()) {
bone->setRotation(props.getRotationEulerDeg(bone->getRotation())); it = m_bone_override.erase(it);
bone->setScale(props.getScale(bone->getScale())); continue;
}
if (auto *bone = m_animated_meshnode->getJointNode(it->first.c_str())) {
bone->setPosition(props.getPosition(bone->getPosition()));
bone->setRotation(props.getRotationEulerDeg(bone->getRotation()));
bone->setScale(props.getScale(bone->getScale()));
}
++it;
} }
}); });
} else } else
@@ -1689,9 +1693,9 @@ void GenericCAO::processMessage(const std::string &data)
props.scale.previous = props.scale.vector; props.scale.previous = props.scale.vector;
} else { } else {
// Disable interpolation // Disable interpolation
props.position.interp_timer = 0.0f; props.position.interp_duration = 0.0f;
props.rotation.interp_timer = 0.0f; props.rotation.interp_duration = 0.0f;
props.scale.interp_timer = 0.0f; props.scale.interp_duration = 0.0f;
} }
// Read new values // Read new values
props.position.vector = readV3F32(is); props.position.vector = readV3F32(is);
@@ -1703,19 +1707,15 @@ void GenericCAO::processMessage(const std::string &data)
props.position.absolute = true; props.position.absolute = true;
props.rotation.absolute = true; props.rotation.absolute = true;
} else { } else {
props.position.interp_timer = readF32(is); props.position.interp_duration = readF32(is);
props.rotation.interp_timer = readF32(is); props.rotation.interp_duration = readF32(is);
props.scale.interp_timer = readF32(is); props.scale.interp_duration = readF32(is);
u8 absoluteFlag = readU8(is); u8 absoluteFlag = readU8(is);
props.position.absolute = (absoluteFlag & 1) > 0; props.position.absolute = (absoluteFlag & 1) > 0;
props.rotation.absolute = (absoluteFlag & 2) > 0; props.rotation.absolute = (absoluteFlag & 2) > 0;
props.scale.absolute = (absoluteFlag & 4) > 0; props.scale.absolute = (absoluteFlag & 4) > 0;
} }
if (props.isIdentity()) { m_bone_override[bone] = props;
m_bone_override.erase(bone);
} else {
m_bone_override[bone] = props;
}
} else if (cmd == AO_CMD_ATTACH_TO) { } else if (cmd == AO_CMD_ATTACH_TO) {
u16 parent_id = readS16(is); u16 parent_id = readS16(is);
std::string bone = deSerializeString16(is); std::string bone = deSerializeString16(is);

View File

@@ -668,7 +668,7 @@ int ObjectRef::l_set_bone_override(lua_State *L)
lua_getfield(L, -1, "interpolation"); lua_getfield(L, -1, "interpolation");
if (lua_isnumber(L, -1)) if (lua_isnumber(L, -1))
prop.interp_timer = lua_tonumber(L, -1); prop.interp_duration = lua_tonumber(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
}; };
@@ -718,7 +718,7 @@ static void push_bone_override(lua_State *L, const BoneOverride &props)
lua_newtable(L); lua_newtable(L);
push_v3f(L, vec); push_v3f(L, vec);
lua_setfield(L, -2, "vec"); lua_setfield(L, -2, "vec");
lua_pushnumber(L, prop.interp_timer); lua_pushnumber(L, prop.interp_duration);
lua_setfield(L, -2, "interpolation"); lua_setfield(L, -2, "interpolation");
lua_pushboolean(L, prop.absolute); lua_pushboolean(L, prop.absolute);
lua_setfield(L, -2, "absolute"); lua_setfield(L, -2, "absolute");

View File

@@ -312,9 +312,9 @@ std::string UnitSAO::generateUpdateBoneOverrideCommand(
props.rotation.next.toEuler(euler_rot); props.rotation.next.toEuler(euler_rot);
writeV3F32(os, euler_rot * core::RADTODEG); writeV3F32(os, euler_rot * core::RADTODEG);
writeV3F32(os, props.scale.vector); writeV3F32(os, props.scale.vector);
writeF32(os, props.position.interp_timer); writeF32(os, props.position.interp_duration);
writeF32(os, props.rotation.interp_timer); writeF32(os, props.rotation.interp_duration);
writeF32(os, props.scale.interp_timer); writeF32(os, props.scale.interp_duration);
writeU8(os, (props.position.absolute & 1) << 0 writeU8(os, (props.position.absolute & 1) << 0
| (props.rotation.absolute & 1) << 1 | (props.rotation.absolute & 1) << 1
| (props.scale.absolute & 1) << 2); | (props.scale.absolute & 1) << 2);