Smoothed yaw rotation for objects (#6825)
authorSmallJoker <SmallJoker@users.noreply.github.com>
Thu, 2 Aug 2018 22:25:37 +0000 (00:25 +0200)
committerParamat <paramat@users.noreply.github.com>
Thu, 2 Aug 2018 22:25:37 +0000 (23:25 +0100)
src/content_cao.cpp
src/content_cao.h
src/content_sao.cpp
src/util/numeric.h

index 1955559b5f4061c91a585d0a533cdc8126636bf5..e2d146d43d256d0483493739ee551a0b4a8f59f3 100644 (file)
@@ -53,51 +53,65 @@ struct ToolCapabilities;
 
 std::unordered_map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
 
-void SmoothTranslator::init(v3f vect)
+template<typename T>
+void SmoothTranslator<T>::init(T current)
 {
-       vect_old = vect;
-       vect_show = vect;
-       vect_aim = vect;
-       anim_counter = 0;
+       val_old = current;
+       val_current = current;
+       val_target = current;
        anim_time = 0;
        anim_time_counter = 0;
        aim_is_end = true;
 }
 
-void SmoothTranslator::update(v3f vect_new, bool is_end_position, float update_interval)
+template<typename T>
+void SmoothTranslator<T>::update(T new_target, bool is_end_position, float update_interval)
 {
        aim_is_end = is_end_position;
-       vect_old = vect_show;
-       vect_aim = vect_new;
-       if(update_interval > 0)
-       {
+       val_old = val_current;
+       val_target = new_target;
+       if (update_interval > 0) {
                anim_time = update_interval;
        } else {
-               if(anim_time < 0.001 || anim_time > 1.0)
+               if (anim_time < 0.001 || anim_time > 1.0)
                        anim_time = anim_time_counter;
                else
                        anim_time = anim_time * 0.9 + anim_time_counter * 0.1;
        }
        anim_time_counter = 0;
-       anim_counter = 0;
 }
 
-void SmoothTranslator::translate(f32 dtime)
+template<typename T>
+void SmoothTranslator<T>::translate(f32 dtime)
 {
        anim_time_counter = anim_time_counter + dtime;
-       anim_counter = anim_counter + dtime;
-       v3f vect_move = vect_aim - vect_old;
+       T val_diff = val_target - val_old;
        f32 moveratio = 1.0;
-       if(anim_time > 0.001)
+       if (anim_time > 0.001)
                moveratio = anim_time_counter / anim_time;
+       f32 move_end = aim_is_end ? 1.0 : 1.5;
+
        // Move a bit less than should, to avoid oscillation
-       moveratio = moveratio * 0.8;
-       float move_end = 1.5;
-       if(aim_is_end)
-               move_end = 1.0;
-       if(moveratio > move_end)
-               moveratio = move_end;
-       vect_show = vect_old + vect_move * moveratio;
+       moveratio = std::min(moveratio * 0.8f, move_end);
+       val_current = val_old + val_diff * moveratio;
+}
+
+void SmoothTranslatorWrapped::translate(f32 dtime)
+{
+       anim_time_counter = anim_time_counter + dtime;
+       f32 val_diff = std::abs(val_target - val_old);
+       if (val_diff > 180.f)
+               val_diff = 360.f - val_diff;
+
+       f32 moveratio = 1.0;
+       if (anim_time > 0.001)
+               moveratio = anim_time_counter / anim_time;
+       f32 move_end = aim_is_end ? 1.0 : 1.5;
+
+       // Move a bit less than should, to avoid oscillation
+       moveratio = std::min(moveratio * 0.8f, move_end);
+       wrappedApproachShortest(val_current, val_target,
+               val_diff * moveratio, 360.f);
 }
 
 /*
@@ -331,7 +345,9 @@ void GenericCAO::processInitData(const std::string &data)
                processMessage(message);
        }
 
+       m_yaw = wrapDegrees_0_360(m_yaw);
        pos_translator.init(m_position);
+       yaw_translator.init(m_yaw);
        updateNodePos();
 }
 
@@ -359,7 +375,7 @@ v3f GenericCAO::getPosition()
 
                return m_position;
        }
-       return pos_translator.vect_show;
+       return pos_translator.val_current;
 }
 
 const bool GenericCAO::isImmortal()
@@ -717,10 +733,10 @@ void GenericCAO::updateNodePos()
 
        if (node) {
                v3s16 camera_offset = m_env->getCameraOffset();
-               node->setPosition(pos_translator.vect_show - intToFloat(camera_offset, BS));
+               node->setPosition(pos_translator.val_current - intToFloat(camera_offset, BS));
                if (node != m_spritenode) { // rotate if not a sprite
                        v3f rot = node->getRotation();
-                       rot.Y = -m_yaw;
+                       rot.Y = m_is_local_player ? -m_yaw : -yaw_translator.val_current;
                        node->setRotation(rot);
                }
        }
@@ -735,10 +751,11 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
                        int old_anim = player->last_animation;
                        float old_anim_speed = player->last_animation_speed;
                        m_position = player->getPosition();
+                       m_yaw = wrapDegrees_0_360(player->getYaw());
                        m_velocity = v3f(0,0,0);
                        m_acceleration = v3f(0,0,0);
-                       pos_translator.vect_show = m_position;
-                       m_yaw = player->getYaw();
+                       pos_translator.val_current = m_position;
+                       yaw_translator.val_current = m_yaw;
                        const PlayerControl &controls = player->getPlayerControl();
 
                        bool walking = false;
@@ -841,7 +858,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
                m_position = getPosition();
                m_velocity = v3f(0,0,0);
                m_acceleration = v3f(0,0,0);
-               pos_translator.vect_show = m_position;
+               pos_translator.val_current = m_position;
 
                if(m_is_local_player) // Update local player attachment position
                {
@@ -850,7 +867,8 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
                        m_env->getLocalPlayer()->parent = getParent();
                }
        } else {
-               v3f lastpos = pos_translator.vect_show;
+               yaw_translator.translate(dtime);
+               v3f lastpos = pos_translator.val_current;
 
                if(m_prop.physical)
                {
@@ -882,7 +900,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
                        updateNodePos();
                }
 
-               float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
+               float moved = lastpos.getDistanceFrom(pos_translator.val_current);
                m_step_distance_counter += moved;
                if (m_step_distance_counter > 1.5f * BS) {
                        m_step_distance_counter = 0.0f;
@@ -921,6 +939,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
        }
        if (!getParent() && std::fabs(m_prop.automatic_rotate) > 0.001) {
                m_yaw += dtime * m_prop.automatic_rotate * 180 / M_PI;
+               yaw_translator.val_current = m_yaw;
                updateNodePos();
        }
 
@@ -931,14 +950,9 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
                                + m_prop.automatic_face_movement_dir_offset;
                float max_rotation_delta =
                                dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
-               float delta = wrapDegrees_0_360(target_yaw - m_yaw);
 
-               if (delta > max_rotation_delta && 360 - delta > max_rotation_delta) {
-                       m_yaw += (delta < 180) ? max_rotation_delta : -max_rotation_delta;
-                       m_yaw = wrapDegrees_0_360(m_yaw);
-               } else {
-                       m_yaw = target_yaw;
-               }
+               wrappedApproachShortest(m_yaw, target_yaw, max_rotation_delta, 360.f);
+               yaw_translator.val_current = m_yaw;
                updateNodePos();
        }
 }
@@ -1304,6 +1318,7 @@ void GenericCAO::processMessage(const std::string &data)
                        m_yaw = readF1000(is);
                else
                        readF1000(is);
+               m_yaw = wrapDegrees_0_360(m_yaw);
                bool do_interpolate = readU8(is);
                bool is_end_position = readU8(is);
                float update_interval = readF1000(is);
@@ -1323,6 +1338,7 @@ void GenericCAO::processMessage(const std::string &data)
                } else {
                        pos_translator.init(m_position);
                }
+               yaw_translator.update(m_yaw, false, update_interval);
                updateNodePos();
        } else if (cmd == GENERIC_CMD_SET_TEXTURE_MOD) {
                std::string mod = deSerializeString(is);
index d2c8d772e35af57ae75e445b31581053bc06a787..cd58681bb1c4b7cc44e5d975d100912219eb174f 100644 (file)
@@ -34,25 +34,31 @@ struct Nametag;
        SmoothTranslator
 */
 
+template<typename T>
 struct SmoothTranslator
 {
-       v3f vect_old;
-       v3f vect_show;
-       v3f vect_aim;
-       f32 anim_counter = 0;
+       T val_old;
+       T val_current;
+       T val_target;
        f32 anim_time = 0;
        f32 anim_time_counter = 0;
        bool aim_is_end = true;
 
        SmoothTranslator() = default;
 
-       void init(v3f vect);
+       void init(T current);
 
-       void update(v3f vect_new, bool is_end_position=false, float update_interval=-1);
+       void update(T new_target, bool is_end_position = false,
+               float update_interval = -1);
 
        void translate(f32 dtime);
 };
 
+struct SmoothTranslatorWrapped : SmoothTranslator<f32>
+{
+       void translate(f32 dtime);
+};
+
 class GenericCAO : public ClientActiveObject
 {
 private:
@@ -76,7 +82,8 @@ private:
        v3f m_acceleration;
        float m_yaw = 0.0f;
        s16 m_hp = 1;
-       SmoothTranslator pos_translator;
+       SmoothTranslator<v3f> pos_translator;
+       SmoothTranslatorWrapped yaw_translator;
        // Spritesheet/animation stuff
        v2f m_tx_size = v2f(1,1);
        v2s16 m_tx_basepos;
index b67a63e3b8c9f823131c9d774c73a6900e286b4c..4ef52c7f29c2ac46826fce9245135141c5692d67 100644 (file)
@@ -443,14 +443,9 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
                                + m_prop.automatic_face_movement_dir_offset;
                        float max_rotation_delta =
                                        dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
-                       float delta = wrapDegrees_0_360(target_yaw - m_yaw);
 
-                       if (delta > max_rotation_delta && 360 - delta > max_rotation_delta) {
-                               m_yaw += (delta < 180) ? max_rotation_delta : -max_rotation_delta;
-                               m_yaw = wrapDegrees_0_360(m_yaw);
-                       } else {
-                               m_yaw = target_yaw;
-                       }
+                       m_yaw = wrapDegrees_0_360(m_yaw);
+                       wrappedApproachShortest(m_yaw, target_yaw, max_rotation_delta, 360.f);
                }
        }
 
index f7df19ca94f75a05ba7d2460d4e0fa5e56837390..61370a3f4ba2f2d6ae2d288d50ff29a920420916 100644 (file)
@@ -376,3 +376,22 @@ inline u32 npot2(u32 orig) {
        orig |= orig >> 16;
        return orig + 1;
 }
+
+// Gradual steps towards the target value in a wrapped (circular) system
+// using the shorter of both ways
+template<typename T>
+inline void wrappedApproachShortest(T &current, const T target, const T stepsize,
+       const T maximum)
+{
+       T delta = target - current;
+       if (delta < 0)
+               delta += maximum;
+
+       if (delta > stepsize && maximum - delta > stepsize) {
+               current += (delta < maximum / 2) ? stepsize : -stepsize;
+               if (current >= maximum)
+                       current -= maximum;
+       } else {
+               current = target;
+       }
+}