From 0d61598d8aef6466fe232e97bd0de1ed4e726a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Wed, 20 Dec 2023 21:21:53 +0100 Subject: [PATCH] Extend bone override capabilities (#12388) --- doc/lua_api.md | 37 +++++--- src/activeobject.h | 74 +++++++++++++++ src/client/content_cao.cpp | 75 ++++++++++++--- src/client/content_cao.h | 4 +- src/network/networkprotocol.h | 5 +- src/script/lua_api/l_object.cpp | 159 +++++++++++++++++++++++++++++--- src/script/lua_api/l_object.h | 9 ++ src/server/luaentity_sao.cpp | 10 +- src/server/player_sao.cpp | 12 +-- src/server/serveractiveobject.h | 11 ++- src/server/unit_sao.cpp | 45 +++++---- src/server/unit_sao.h | 14 +-- 12 files changed, 375 insertions(+), 80 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 4b03db52c..67d9cf604 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -7556,17 +7556,32 @@ child will follow movement and rotation of that bone. object. * `set_detach()`: Detaches object. No-op if object was not attached. * `set_bone_position([bone, position, rotation])` - * `bone`: string. Default is `""`, the root bone - * `position`: `{x=num, y=num, z=num}`, relative, `default {x=0, y=0, z=0}` - * `rotation`: `{x=num, y=num, z=num}`, default `{x=0, y=0, z=0}` -* `get_bone_position(bone)`: - * returns bone parameters previously set by `set_bone_position` - * returns `position, rotation` of the specified bone (as vectors) - * note: position is relative to the object -* `set_properties(object property table)`: - * set a number of object properties in the given table - * only properties listed in the table will be changed - * see the 'Object properties' section for details + * Shorthand for `set_bone_override(bone, {position = position, rotation = rotation:apply(math.rad)})` using absolute values. + * **Note:** Rotation is in degrees, not radians. + * **Deprecated:** Use `set_bone_override` instead. +* `get_bone_position(bone)`: returns the previously set position and rotation of the bone + * Shorthand for `get_bone_override(bone).position.vec, get_bone_override(bone).rotation.vec:apply(math.deg)`. + * **Note:** Returned rotation is in degrees, not radians. + * **Deprecated:** Use `get_bone_override` instead. +* `set_bone_override(bone, override)` + * `bone`: string + * `override`: `{ position = property, rotation = property, scale = property }` or `nil` + * `property`: `{ vec = vector, interpolation = 0, absolute = false}` or `nil`; + * `vec` is in the same coordinate system as the model, and in degrees for rotation + * `property = nil` is equivalent to no override on that property + * `absolute`: If set to `false`, the override will be relative to the animated property: + * Transposition in the case of `position`; + * Composition in the case of `rotation`; + * Multiplication in the case of `scale` + * `interpolation`: Old and new values are interpolated over this timeframe (in seconds) + * `override = nil` (including omission) is shorthand for `override = {}` which clears the override + * **Note:** Unlike `set_bone_position`, the rotation is in radians, not degrees. + * Compatibility note: Clients prior to 5.9.0 only support absolute position and rotation. + All values are treated as absolute and are set immediately (no interpolation). +* `get_bone_override(bone)`: returns `override` in the above format + * **Note:** Unlike `get_bone_position`, the returned rotation is in radians, not degrees. +* `get_bone_overrides()`: returns all bone overrides as table `{[bonename] = override, ...}` +* `set_properties(object property table)` * `get_properties()`: returns a table of all object properties * `is_player()`: returns true for players, false otherwise * `get_nametag_attributes()` diff --git a/src/activeobject.h b/src/activeobject.h index 1d8a3712b..02c16b557 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -21,7 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_aabb3d.h" #include "irr_v3d.h" +#include #include +#include enum ActiveObjectType { @@ -72,6 +74,78 @@ enum ActiveObjectCommand { AO_CMD_SET_ANIMATION_SPEED }; +struct BoneOverride +{ + struct PositionProperty + { + v3f previous; + v3f vector; + bool absolute = false; + f32 interp_timer = 0; + } position; + + v3f getPosition(v3f anim_pos) const { + f32 progress = dtime_passed / position.interp_timer; + if (progress > 1.0f || position.interp_timer == 0.0f) + progress = 1.0f; + return position.vector.getInterpolated(position.previous, progress) + + (position.absolute ? v3f() : anim_pos); + } + + struct RotationProperty + { + core::quaternion previous; + core::quaternion next; + bool absolute = false; + f32 interp_timer = 0; + } rotation; + + v3f getRotationEulerDeg(v3f anim_rot_euler) const { + core::quaternion rot; + + f32 progress = dtime_passed / rotation.interp_timer; + if (progress > 1.0f || rotation.interp_timer == 0.0f) + progress = 1.0f; + rot.slerp(rotation.previous, rotation.next, progress); + if (!rotation.absolute) { + core::quaternion anim_rot(anim_rot_euler * core::DEGTORAD); + rot = rot * anim_rot; // first rotate by anim. bone rot., then rot. + } + + v3f rot_euler; + rot.toEuler(rot_euler); + return rot_euler * core::RADTODEG; + } + + struct ScaleProperty + { + v3f previous; + v3f vector{1, 1, 1}; + bool absolute = false; + f32 interp_timer = 0; + } scale; + + v3f getScale(v3f anim_scale) const { + f32 progress = dtime_passed / scale.interp_timer; + if (progress > 1.0f || scale.interp_timer == 0.0f) + progress = 1.0f; + return scale.vector.getInterpolated(scale.previous, progress) + * (scale.absolute ? v3f(1) : anim_scale); + } + + f32 dtime_passed = 0; + + bool isIdentity() const + { + return !position.absolute && position.vector == v3f() + && !rotation.absolute && rotation.next == core::quaternion() + && !scale.absolute && scale.vector == v3f(1); + } +}; + +typedef std::unordered_map BoneOverrideMap; + + /* Parent class for ServerActiveObject and ClientActiveObject */ diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 1c66062e0..d3d2285dd 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -46,6 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "client/shader.h" #include "client/minimap.h" +#include class Settings; struct ToolCapabilities; @@ -828,7 +829,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) updateMarker(); updateNodePos(); updateAnimation(); - updateBonePosition(); + updateBones(.0f); updateAttachments(); setNodeLight(m_last_light); updateMeshCulling(); @@ -1246,7 +1247,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) updatePositionRecursive(m_matrixnode); m_animated_meshnode->updateAbsolutePosition(); m_animated_meshnode->animateJoints(); - updateBonePosition(); + updateBones(dtime); } } @@ -1527,19 +1528,28 @@ void GenericCAO::updateAnimationSpeed() m_animated_meshnode->setAnimationSpeed(m_animation_speed); } -void GenericCAO::updateBonePosition() +void GenericCAO::updateBones(f32 dtime) { - if (m_bone_position.empty() || !m_animated_meshnode) + if (!m_animated_meshnode) return; + if (m_bone_override.empty()) { + m_animated_meshnode->setJointMode(scene::EJUOR_NONE); + return; + } m_animated_meshnode->setJointMode(scene::EJUOR_CONTROL); // To write positions to the mesh on render - for (auto &it : m_bone_position) { + for (auto &it : m_bone_override) { std::string bone_name = it.first; scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str()); - if (bone) { - bone->setPosition(it.second.X); - bone->setRotation(it.second.Y); - } + if (!bone) + continue; + + BoneOverride &props = it.second; + props.dtime_passed += dtime; + + bone->setPosition(props.getPosition(bone->getPosition())); + bone->setRotation(props.getRotationEulerDeg(bone->getRotation())); + bone->setScale(props.getScale(bone->getScale())); } // search through bones to find mistakenly rotated bones due to bug in Irrlicht @@ -1550,7 +1560,7 @@ void GenericCAO::updateBonePosition() //If bone is manually positioned there is no need to perform the bug check bool skip = false; - for (auto &it : m_bone_position) { + for (auto &it : m_bone_override) { if (it.first == bone->getName()) { skip = true; break; @@ -1852,11 +1862,46 @@ void GenericCAO::processMessage(const std::string &data) updateAnimationSpeed(); } else if (cmd == AO_CMD_SET_BONE_POSITION) { std::string bone = deSerializeString16(is); - v3f position = readV3F32(is); - v3f rotation = readV3F32(is); - m_bone_position[bone] = core::vector2d(position, rotation); - - // updateBonePosition(); now called every step + auto it = m_bone_override.find(bone); + BoneOverride props; + if (it != m_bone_override.end()) { + props = it->second; + // Reset timer + props.dtime_passed = 0; + // Save previous values for interpolation + props.position.previous = props.position.vector; + props.rotation.previous = props.rotation.next; + props.scale.previous = props.scale.vector; + } else { + // Disable interpolation + props.position.interp_timer = 0.0f; + props.rotation.interp_timer = 0.0f; + props.scale.interp_timer = 0.0f; + } + // Read new values + props.position.vector = readV3F32(is); + props.rotation.next = core::quaternion(readV3F32(is) * core::DEGTORAD); + props.scale.vector = readV3F32(is); // reads past end of string on older cmds + if (is.eof()) { + // Backwards compatibility + props.scale.vector = v3f(1, 1, 1); // restore the scale which was not sent + props.position.absolute = true; + props.rotation.absolute = true; + } else { + props.position.interp_timer = readF32(is); + props.rotation.interp_timer = readF32(is); + props.scale.interp_timer = readF32(is); + u8 absoluteFlag = readU8(is); + props.position.absolute = (absoluteFlag & 1) > 0; + props.rotation.absolute = (absoluteFlag & 2) > 0; + props.scale.absolute = (absoluteFlag & 4) > 0; + } + if (props.isIdentity()) { + m_bone_override.erase(bone); + } else { + m_bone_override[bone] = props; + } + // updateBones(); now called every step } else if (cmd == AO_CMD_ATTACH_TO) { u16 parent_id = readS16(is); std::string bone = deSerializeString16(is); diff --git a/src/client/content_cao.h b/src/client/content_cao.h index b090cdc08..05cce5bcf 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -104,7 +104,7 @@ private: float m_animation_blend = 0.0f; bool m_animation_loop = true; // stores position and rotation for each bone name - std::unordered_map> m_bone_position; + BoneOverrideMap m_bone_override; int m_attachment_parent_id = 0; std::unordered_set m_attachment_child_ids; @@ -267,7 +267,7 @@ public: void updateAnimationSpeed(); - void updateBonePosition(); + void updateBones(f32 dtime); void processMessage(const std::string &data) override; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 5b9ead992..0ededba38 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -219,9 +219,12 @@ with this program; if not, write to the Free Software Foundation, Inc., "start_time" added to TOCLIENT_PLAY_SOUND place_param2 type change u8 -> optional [scheduled bump for 5.8.0] + PROTOCOL VERSION 44: + AO_CMD_SET_BONE_POSITION extended + [scheduled bump for 5.9.0] */ -#define LATEST_PROTOCOL_VERSION 43 +#define LATEST_PROTOCOL_VERSION 44 #define LATEST_PROTOCOL_VERSION_STRING TOSTRING(LATEST_PROTOCOL_VERSION) // Server's supported network protocol range diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 3815f815f..a0e8f69a5 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server/luaentity_sao.h" #include "server/player_sao.h" #include "server/serverinventorymgr.h" +#include "server/unit_sao.h" /* ObjectRef @@ -518,16 +519,25 @@ int ObjectRef::l_set_animation_frame_speed(lua_State *L) int ObjectRef::l_set_bone_position(lua_State *L) { NO_MAP_LOCK_REQUIRED; + + log_deprecated(L,"Deprecated call to set_bone_position, use set_bone_override instead"); + ObjectRef *ref = checkObject(L, 1); ServerActiveObject *sao = getobject(ref); if (sao == nullptr) return 0; - std::string bone = readParam(L, 2, ""); - v3f position = readParam(L, 3, v3f(0, 0, 0)); - v3f rotation = readParam(L, 4, v3f(0, 0, 0)); - - sao->setBonePosition(bone, position, rotation); + std::string bone; + if (!lua_isnil(L, 2)) + bone = readParam(L, 2); + BoneOverride props; + if (!lua_isnil(L, 3)) + props.position.vector = check_v3f(L, 3); + if (!lua_isnil(L, 4)) + props.rotation.next = core::quaternion(check_v3f(L, 4) * core::DEGTORAD); + props.position.absolute = true; + props.rotation.absolute = true; + sao->setBoneOverride(bone, props); return 0; } @@ -535,22 +545,144 @@ int ObjectRef::l_set_bone_position(lua_State *L) int ObjectRef::l_get_bone_position(lua_State *L) { NO_MAP_LOCK_REQUIRED; + + log_deprecated(L,"Deprecated call to get_bone_position, use get_bone_override instead"); + ObjectRef *ref = checkObject(L, 1); ServerActiveObject *sao = getobject(ref); if (sao == nullptr) return 0; std::string bone = readParam(L, 2, ""); - - v3f position = v3f(0, 0, 0); - v3f rotation = v3f(0, 0, 0); - sao->getBonePosition(bone, &position, &rotation); - - push_v3f(L, position); - push_v3f(L, rotation); + 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); return 2; } +// set_bone_override(self, bone, override) +int ObjectRef::l_set_bone_override(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkObject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == NULL) + return 0; + + std::string bone = readParam(L, 2); + + BoneOverride props; + if (lua_isnoneornil(L, 3)) { + sao->setBoneOverride(bone, props); + return 0; + } + + auto read_prop_attrs = [L](auto &prop) { + lua_getfield(L, -1, "absolute"); + prop.absolute = lua_toboolean(L, -1); + lua_pop(L, 1); + + lua_getfield(L, -1, "interpolate"); + if (lua_isnumber(L, -1)) + prop.interp_timer = lua_tonumber(L, -1); + lua_pop(L, 1); + }; + + lua_getfield(L, 3, "position"); + if (!lua_isnil(L, -1)) { + lua_getfield(L, -1, "vec"); + if (!lua_isnil(L, -1)) + props.position.vector = check_v3f(L, -1); + lua_pop(L, 1); + + read_prop_attrs(props.position); + } + lua_pop(L, 1); + + 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)); + lua_pop(L, 1); + + read_prop_attrs(props.rotation); + } + lua_pop(L, 1); + + lua_getfield(L, 3, "scale"); + if (!lua_isnil(L, -1)) { + lua_getfield(L, -1, "vec"); + props.scale.vector = lua_isnil(L, -1) ? v3f(1) : check_v3f(L, -1); + lua_pop(L, 1); + + read_prop_attrs(props.scale); + } + lua_pop(L, 1); + + sao->setBoneOverride(bone, props); + return 0; +} + +static void push_bone_override(lua_State *L, const BoneOverride &props) +{ + lua_newtable(L); + + auto push_prop = [L](const char *name, const auto &prop, v3f vec) { + lua_newtable(L); + push_v3f(L, vec); + lua_setfield(L, -2, "vec"); + lua_pushnumber(L, prop.interp_timer); + lua_setfield(L, -2, "interpolate"); + lua_pushboolean(L, prop.absolute); + lua_setfield(L, -2, "absolute"); + lua_setfield(L, -2, name); + }; + + push_prop("position", props.position, props.position.vector); + + v3f euler_rot; + props.rotation.next.toEuler(euler_rot); + push_prop("rotation", props.rotation, euler_rot); + + push_prop("scale", props.scale, props.scale.vector); + + // leave only override table on top of the stack +} + +// get_bone_override(self, bone) +int ObjectRef::l_get_bone_override(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkObject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == NULL) + return 0; + + std::string bone = readParam(L, 2); + + push_bone_override(L, sao->getBoneOverride(bone)); + return 1; +} + +// get_bone_overrides(self) +int ObjectRef::l_get_bone_overrides(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkObject(L, 1); + ServerActiveObject *co = getobject(ref); + if (co == NULL) + return 0; + lua_newtable(L); + for (const auto &bone_pos : co->getBoneOverrides()) { + push_bone_override(L, bone_pos.second); + lua_setfield(L, -2, bone_pos.first.c_str()); + } + return 1; +} + // set_attach(self, parent, bone, position, rotation, force_visible) int ObjectRef::l_set_attach(lua_State *L) { @@ -2471,6 +2603,9 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, set_animation_frame_speed), luamethod(ObjectRef, set_bone_position), luamethod(ObjectRef, get_bone_position), + luamethod(ObjectRef, set_bone_override), + luamethod(ObjectRef, get_bone_override), + luamethod(ObjectRef, get_bone_overrides), luamethod(ObjectRef, set_attach), luamethod(ObjectRef, get_attach), luamethod(ObjectRef, get_children), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index cbbe4f921..4465a823f 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -130,6 +130,15 @@ private: // get_bone_position(self, bone) static int l_get_bone_position(lua_State *L); + // set_bone_override(self, bone) + static int l_set_bone_override(lua_State *L); + + // get_bone_override(self, bone) + static int l_get_bone_override(lua_State *L); + + // get_bone_override(self) + static int l_get_bone_overrides(lua_State *L); + // set_attach(self, parent, bone, position, rotation) static int l_set_attach(lua_State *L); diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index f18318f4c..e4c392eb2 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -256,13 +256,13 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) msg_os << serializeString32(getPropertyPacket()); // message 1 msg_os << serializeString32(generateUpdateArmorGroupsCommand()); // 2 msg_os << serializeString32(generateUpdateAnimationCommand()); // 3 - for (const auto &bone_pos : m_bone_position) { - msg_os << serializeString32(generateUpdateBonePositionCommand( - bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // 3 + N + for (const auto &bone_override : m_bone_override) { + msg_os << serializeString32(generateUpdateBoneOverrideCommand( + bone_override.first, bone_override.second)); // 3 + N } - msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_position.size + msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_override.size - int message_count = 4 + m_bone_position.size(); + int message_count = 4 + m_bone_override.size(); for (const auto &id : getAttachmentChildIds()) { if (ServerActiveObject *obj = m_env->getActiveObject(id)) { diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 158bb8c64..681841f23 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -121,14 +121,14 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) msg_os << serializeString32(getPropertyPacket()); // message 1 msg_os << serializeString32(generateUpdateArmorGroupsCommand()); // 2 msg_os << serializeString32(generateUpdateAnimationCommand()); // 3 - for (const auto &bone_pos : m_bone_position) { - msg_os << serializeString32(generateUpdateBonePositionCommand( - bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // 3 + N + for (const auto &it : m_bone_override) { + msg_os << serializeString32(generateUpdateBoneOverrideCommand( + it.first, it.second)); // 3 + N } - msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_position.size - msg_os << serializeString32(generateUpdatePhysicsOverrideCommand()); // 5 + m_bone_position.size + msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_override.size + msg_os << serializeString32(generateUpdatePhysicsOverrideCommand()); // 5 + m_bone_override.size - int message_count = 5 + m_bone_position.size(); + int message_count = 5 + m_bone_override.size(); for (const auto &id : getAttachmentChildIds()) { if (ServerActiveObject *obj = m_env->getActiveObject(id)) { diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h index e9cf3ee37..a42096f7b 100644 --- a/src/server/serveractiveobject.h +++ b/src/server/serveractiveobject.h @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemgroup.h" #include "util/container.h" + /* Some planning @@ -167,14 +168,16 @@ public: {} virtual void setAnimationSpeed(float frame_speed) {} - virtual void setBonePosition(const std::string &bone, v3f position, v3f rotation) - {} - virtual void getBonePosition(const std::string &bone, v3f *position, v3f *lotation) + virtual void setBoneOverride(const std::string &bone, const BoneOverride &props) {} + virtual BoneOverride getBoneOverride(const std::string &bone) + { BoneOverride props; return props; } + virtual const BoneOverrideMap &getBoneOverrides() const + { static BoneOverrideMap rv; return rv; } virtual const std::unordered_set &getAttachmentChildIds() const { static std::unordered_set rv; return rv; } virtual ServerActiveObject *getParent() const { return nullptr; } - virtual ObjectProperties* accessObjectProperties() + virtual ObjectProperties *accessObjectProperties() { return NULL; } virtual void notifyObjectPropertiesModified() {} diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp index c928b830f..fd44ae323 100644 --- a/src/server/unit_sao.cpp +++ b/src/server/unit_sao.cpp @@ -76,20 +76,20 @@ void UnitSAO::setAnimationSpeed(float frame_speed) m_animation_speed_sent = false; } -void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation) +void UnitSAO::setBoneOverride(const std::string &bone, const BoneOverride &props) { // store these so they can be updated to clients - m_bone_position[bone] = core::vector2d(position, rotation); - m_bone_position_sent = false; + m_bone_override[bone] = props; + m_bone_override_sent = false; } -void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation) +BoneOverride UnitSAO::getBoneOverride(const std::string &bone) { - auto it = m_bone_position.find(bone); - if (it != m_bone_position.end()) { - *position = it->second.X; - *rotation = it->second.Y; - } + auto it = m_bone_override.find(bone); + BoneOverride props; + if (it != m_bone_override.end()) + props = it->second; + return props; } void UnitSAO::sendOutdatedData() @@ -109,11 +109,11 @@ void UnitSAO::sendOutdatedData() m_messages_out.emplace(getId(), true, generateUpdateAnimationSpeedCommand()); } - if (!m_bone_position_sent) { - m_bone_position_sent = true; - for (const auto &bone_pos : m_bone_position) { - m_messages_out.emplace(getId(), true, generateUpdateBonePositionCommand( - bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); + if (!m_bone_override_sent) { + m_bone_override_sent = true; + for (const auto &bone_pos : m_bone_override) { + m_messages_out.emplace(getId(), true, generateUpdateBoneOverrideCommand( + bone_pos.first, bone_pos.second)); } } @@ -275,16 +275,25 @@ std::string UnitSAO::generateUpdateAttachmentCommand() const return os.str(); } -std::string UnitSAO::generateUpdateBonePositionCommand( - const std::string &bone, const v3f &position, const v3f &rotation) +std::string UnitSAO::generateUpdateBoneOverrideCommand( + const std::string &bone, const BoneOverride &props) { std::ostringstream os(std::ios::binary); // command writeU8(os, AO_CMD_SET_BONE_POSITION); // parameters os << serializeString16(bone); - writeV3F32(os, position); - writeV3F32(os, rotation); + writeV3F32(os, props.position.vector); + v3f euler_rot; + props.rotation.next.toEuler(euler_rot); + writeV3F32(os, euler_rot * core::RADTODEG); + writeV3F32(os, props.scale.vector); + writeF32(os, props.position.interp_timer); + writeF32(os, props.rotation.interp_timer); + writeF32(os, props.scale.interp_timer); + writeU8(os, (props.position.absolute & 1) << 0 + | (props.rotation.absolute & 1) << 1 + | (props.scale.absolute & 1) << 2); return os.str(); } diff --git a/src/server/unit_sao.h b/src/server/unit_sao.h index dedb1874e..cbd845708 100644 --- a/src/server/unit_sao.h +++ b/src/server/unit_sao.h @@ -70,8 +70,10 @@ public: void setAnimationSpeed(float frame_speed); // Bone position - void setBonePosition(const std::string &bone, v3f position, v3f rotation); - void getBonePosition(const std::string &bone, v3f *position, v3f *rotation); + void setBoneOverride(const std::string &bone, const BoneOverride &props); + BoneOverride getBoneOverride(const std::string &bone); + const std::unordered_map + &getBoneOverrides() const { return m_bone_override; }; // Attachments ServerActiveObject *getParent() const; @@ -100,8 +102,8 @@ public: const v3f &velocity, const v3f &acceleration, const v3f &rotation, bool do_interpolate, bool is_movement_end, f32 update_interval); std::string generateSetPropertiesCommand(const ObjectProperties &prop) const; - static std::string generateUpdateBonePositionCommand(const std::string &bone, - const v3f &position, const v3f &rotation); + static std::string generateUpdateBoneOverrideCommand( + const std::string &bone, const BoneOverride &props); void sendPunchCommand(); protected: @@ -117,7 +119,7 @@ protected: ObjectProperties m_prop; // Stores position and rotation for each bone name - std::unordered_map> m_bone_position; + std::unordered_map m_bone_override; int m_attachment_parent_id = 0; @@ -139,7 +141,7 @@ private: bool m_animation_speed_sent = false; // Bone positions - bool m_bone_position_sent = false; + bool m_bone_override_sent = false; // Attachments std::unordered_set m_attachment_child_ids;