From 02db521c80e071a5afd3b50760675df7850e362f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Mon, 26 Aug 2024 21:22:38 +0200 Subject: [PATCH] Make getting bone overrides return the "same" euler angles (#15007) --- games/devtest/mods/unittests/entity.lua | 20 ++++++++++++++++++++ src/activeobject.h | 3 +++ src/script/lua_api/l_object.cpp | 24 ++++++++++++++---------- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/games/devtest/mods/unittests/entity.lua b/games/devtest/mods/unittests/entity.lua index 38d026663..4e308d173 100644 --- a/games/devtest/mods/unittests/entity.lua +++ b/games/devtest/mods/unittests/entity.lua @@ -184,3 +184,23 @@ unittests.register("test_objects_in_area", function(_, pos) return core.objects_in_area(pos:offset(-1, -1, -1), pos:offset(1, 1, 1)) end) end, {map=true}) + +-- Tests that bone rotation euler angles are preserved (see #14992) +local function test_get_bone_rot(_, pos) + local obj = core.add_entity(pos, "unittests:dummy") + for _ = 1, 100 do + local function assert_similar(euler_angles) + local _, rot = obj:get_bone_position("bonename") + assert(euler_angles:distance(rot) < 1e-3) + local override = obj:get_bone_override("bonename") + assert(euler_angles:distance(override.rotation.vec:apply(math.deg)) < 1e-3) + end + local deg = 1e3 * vector.new(math.random(), math.random(), math.random()) + obj:set_bone_position("bonename", vector.zero(), deg) + assert_similar(deg) + local rad = 3 * math.pi * vector.new(math.random(), math.random(), math.random()) + obj:set_bone_override("bonename", {rotation = {vec = rad}}) + assert_similar(rad:apply(math.deg)) + end +end +unittests.register("test_get_bone_rot", test_get_bone_rot, {map=true}) diff --git a/src/activeobject.h b/src/activeobject.h index 52f997fdf..87b74d865 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -96,6 +96,9 @@ struct BoneOverride { core::quaternion previous; core::quaternion next; + // Redundantly store the euler angles serverside + // so that we can return them in the appropriate getters + v3f next_radians; bool absolute = false; f32 interp_timer = 0; } rotation; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 6baf146e5..dae2455e1 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -561,8 +561,10 @@ int ObjectRef::l_set_bone_position(lua_State *L) BoneOverride props; if (!lua_isnoneornil(L, 3)) props.position.vector = check_v3f(L, 3); - if (!lua_isnoneornil(L, 4)) - props.rotation.next = core::quaternion(check_v3f(L, 4) * core::DEGTORAD); + if (!lua_isnoneornil(L, 4)) { + props.rotation.next_radians = check_v3f(L, 4) * core::DEGTORAD; + props.rotation.next = core::quaternion(props.rotation.next_radians); + } props.position.absolute = true; props.rotation.absolute = true; sao->setBoneOverride(bone, props); @@ -584,9 +586,9 @@ int ObjectRef::l_get_bone_position(lua_State *L) std::string bone = readParam(L, 2, ""); BoneOverride props = sao->getBoneOverride(bone); push_v3f(L, props.position.vector); - v3f euler_rot; - props.rotation.next.toEuler(euler_rot); - push_v3f(L, euler_rot * core::RADTODEG); + // In order to give modders back the euler angles they passed in, + // this **must not** compute equivalent euler angles from the quaternion + push_v3f(L, props.rotation.next_radians * core::RADTODEG); return 2; } @@ -632,8 +634,10 @@ int ObjectRef::l_set_bone_override(lua_State *L) lua_getfield(L, 3, "rotation"); if (!lua_isnil(L, -1)) { lua_getfield(L, -1, "vec"); - if (!lua_isnil(L, -1)) - props.rotation.next = core::quaternion(check_v3f(L, -1)); + if (!lua_isnil(L, -1)) { + props.rotation.next_radians = check_v3f(L, -1); + props.rotation.next = core::quaternion(props.rotation.next_radians); + } lua_pop(L, 1); read_prop_attrs(props.rotation); @@ -671,9 +675,9 @@ static void push_bone_override(lua_State *L, const BoneOverride &props) push_prop("position", props.position, props.position.vector); - v3f euler_rot; - props.rotation.next.toEuler(euler_rot); - push_prop("rotation", props.rotation, euler_rot); + // In order to give modders back the euler angles they passed in, + // this **must not** compute equivalent euler angles from the quaternion + push_prop("rotation", props.rotation, props.rotation.next_radians); push_prop("scale", props.scale, props.scale.vector);