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:
@@ -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,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user