From 3b4353a2c9334d9ee25f66b6da6337dad6801652 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 18 Jun 2018 00:06:32 +0800 Subject: [PATCH] Add curved smoothing for moveable in network --- src/karts/abstract_kart.hpp | 2 +- src/karts/kart_rewinder.cpp | 7 +- src/karts/kart_rewinder.hpp | 2 - src/karts/moveable.cpp | 165 ++++++++++++++++++++++++++++------- src/karts/moveable.hpp | 44 ++++++++-- src/network/rewind_queue.cpp | 4 +- 6 files changed, 178 insertions(+), 46 deletions(-) diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index 5381c6683..792b7072b 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -140,7 +140,7 @@ public: // ------------------------------------------------------------------------ /** Returns a unique identifier for this kart (name of the directory the * kart was loaded from). */ - const std::string& getIdent() const; + virtual const std::string& getIdent() const; // ------------------------------------------------------------------------ /** Returns the maximum steering angle for this kart, which depends on the * speed. */ diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index a1c52b235..aa23de795 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -59,16 +59,13 @@ void KartRewinder::reset() */ void KartRewinder::saveTransform() { - m_saved_transform = getTrans(); + Moveable::prepareSmoothing(); } // saveTransform // ---------------------------------------------------------------------------- void KartRewinder::computeError() { - //btTransform error = getTrans() - m_saved_transform; - Vec3 pos_error = getTrans().getOrigin() - m_saved_transform.getOrigin(); - btQuaternion rot_error(0, 0, 0, 1); - Kart::addError(pos_error, rot_error); + Moveable::checkSmoothing(); } // computeError // ---------------------------------------------------------------------------- diff --git a/src/karts/kart_rewinder.hpp b/src/karts/kart_rewinder.hpp index d3e16c85f..98fc477d5 100644 --- a/src/karts/kart_rewinder.hpp +++ b/src/karts/kart_rewinder.hpp @@ -34,8 +34,6 @@ private: enum { EVENT_CONTROL = 0x01, EVENT_ATTACH = 0x02 }; - /** The transform of the kart before a rewind starts. */ - btTransform m_saved_transform; public: KartRewinder(const std::string& ident, unsigned int world_kart_id, diff --git a/src/karts/moveable.cpp b/src/karts/moveable.cpp index 0ca87bd8a..411fd4098 100644 --- a/src/karts/moveable.cpp +++ b/src/karts/moveable.cpp @@ -38,8 +38,14 @@ Moveable::Moveable() m_mesh = NULL; m_node = NULL; m_heading = 0; - m_positional_error = Vec3(0.0f, 0.0f, 0.0f); - m_rotational_error = btQuaternion(0.0f, 0.0f, 0.0f, 1.0f); + + m_smoothed_transform = btTransform(btQuaternion(0.0f, 0.0f, 0.0f, 1.0f)); + m_start_smoothing_postion = m_adjust_position = + std::make_pair(Vec3(0.0f, 0.0f, 0.0f), + btQuaternion(0.0f, 0.0f, 0.0f, 1.0f)); + m_prev_position_data = std::make_pair(m_smoothed_transform, Vec3()); + m_smoothing = SS_NONE; + m_adjust_time = m_adjust_time_dt = 0.0f; } // Moveable //----------------------------------------------------------------------------- @@ -61,25 +67,61 @@ void Moveable::setNode(scene::ISceneNode *n) m_node = n; } // setNode +//----------------------------------------------------------------------------- +void Moveable::prepareSmoothing() +{ + // Continuous smooth enabled + //if (m_smoothing != SS_NONE) + // return; + + m_prev_position_data = std::make_pair(m_transform, getVelocity()); +} // prepareSmoothing + //----------------------------------------------------------------------------- /** Adds a new error between graphical and physical position/rotation. Called * in case of a rewind to allow to for smoothing the visuals in case of * incorrect client prediction. - * \param pos_error Positional error to add. - * \param rot_Error Rotational error to add. */ -void Moveable::addError(const Vec3& pos_error, - const btQuaternion &rot_error) +void Moveable::checkSmoothing() { - m_positional_error += pos_error; -#ifdef DEBUG_VISUAL_ERROR - Log::info("VisualError", "time %f addError %f %f %f size %f", - World::getWorld()->getTime(), - m_positional_error.getX(), m_positional_error.getY(), m_positional_error.getZ(), - m_positional_error.length()); -#endif - m_rotational_error *= rot_error; -} // addError + // Continuous smooth enabled + //if (m_smoothing != SS_NONE) + // return; + + float adjust_length = (m_transform.getOrigin() - + m_prev_position_data.first.getOrigin()).length(); + if (adjust_length < 0.1f || adjust_length > 4.0f) + return; + + float speed = m_prev_position_data.second.length(); + speed = std::max(speed, getVelocity().length()); + if (speed < 0.3f) + return; + + float adjust_time = (adjust_length * 2.0f) / speed; + if (adjust_time > 2.0f) + return; + + m_smoothing = SS_TO_ADJUST; + m_adjust_time_dt = 0.0f; + m_adjust_time = adjust_time; + + m_start_smoothing_postion.first = m_smoothing == SS_NONE ? + m_prev_position_data.first.getOrigin() : + m_smoothed_transform.getOrigin(); + m_start_smoothing_postion.second = m_smoothing == SS_NONE ? + m_prev_position_data.first.getRotation() : + m_smoothed_transform.getRotation(); + + m_adjust_control_point = m_start_smoothing_postion.first + + m_prev_position_data.second * m_adjust_time; + Vec3 p2 = m_transform.getOrigin() + getVelocity() * + m_adjust_time; + + m_adjust_position.first.setInterpolate3(m_adjust_control_point, p2, 0.5f); + m_adjust_position.second = m_transform.getRotation(); + m_adjust_position.second.slerp(m_start_smoothing_postion.second, 0.5f); +} // checkSmoothing //----------------------------------------------------------------------------- /** Updates the graphics model. Mainly set the graphical position to be the @@ -91,24 +133,84 @@ void Moveable::addError(const Vec3& pos_error, void Moveable::updateGraphics(float dt, const Vec3& offset_xyz, const btQuaternion& rotation) { - // If this is a client, don't smooth error during rewinds - if (World::getWorld()->isNetworkWorld() && - NetworkConfig::get()->isClient() && - !RewindManager::get()->isRewinding()) - { - float error = m_positional_error.length(); - m_positional_error *= stk_config->m_positional_smoothing.get(error); -#ifdef DEBUG_VISUAL_ERROR - Log::info("VisualError", "time %f reduceError %f %f %f size %f", - World::getWorld()->getTime(), - m_positional_error.getX(), m_positional_error.getY(), m_positional_error.getZ(), - m_positional_error.length()); -#endif - } #ifndef SERVER_ONLY - Vec3 xyz=getXYZ()+offset_xyz - m_positional_error; + Vec3 cur_xyz = getXYZ(); + btQuaternion cur_rot = getRotation(); + + float ratio = 0.0f; + if (m_smoothing != SS_NONE) + { + float adjust_time_dt = m_adjust_time_dt + dt; + ratio = adjust_time_dt / m_adjust_time; + if (ratio > 1.0f) + { + ratio -= 1.0f; + m_adjust_time_dt = adjust_time_dt - m_adjust_time; + if (m_smoothing == SS_TO_ADJUST) + { + m_smoothing = SS_TO_REAL; + m_adjust_control_point = m_adjust_position.first + + getVelocity() * m_adjust_time; + } + else + m_smoothing = SS_NONE; + } + else + m_adjust_time_dt = adjust_time_dt; + } + + assert(m_adjust_time_dt >= 0.0f); + assert(ratio >= 0.0f); + if (m_smoothing == SS_TO_ADJUST) + { + cur_xyz.setInterpolate3(m_start_smoothing_postion.first, + m_adjust_position.first, ratio); + Vec3 to_control; + to_control.setInterpolate3(m_start_smoothing_postion.first, + m_adjust_control_point, ratio); + cur_xyz.setInterpolate3(cur_xyz, to_control, 1.0f - ratio); + if (smoothRotation()) + { + cur_rot = m_start_smoothing_postion.second; + cur_rot.slerp(m_adjust_position.second, ratio); + } + } + else if (m_smoothing == SS_TO_REAL) + { + Vec3 to_control; + to_control.setInterpolate3(m_adjust_position.first, + m_adjust_control_point, ratio); + float ratio_sqrt = sqrtf(ratio); + cur_xyz.setInterpolate3(m_adjust_position.first, cur_xyz, ratio_sqrt); + cur_xyz.setInterpolate3(to_control, cur_xyz, ratio); + if (smoothRotation()) + cur_rot.slerp(m_adjust_position.second, 1.0f - ratio); + } + + m_smoothed_transform.setOrigin(cur_xyz); + m_smoothed_transform.setRotation(cur_rot); + + if (m_smoothing != SS_NONE) + { + Vec3 lc = m_transform.inverse()(cur_xyz); + // Adjust vertical position for up/down-sloping + cur_xyz = m_smoothed_transform(Vec3(0.0f, -lc.y(), 0.0f)); + m_smoothed_transform.setOrigin(cur_xyz); + } + +#undef DEBUG_SMOOTHING +#ifdef DEBUG_SMOOTHING + // Gnuplot compare command + // plot "stdout.log" u 6:8 w lp lw 2, "stdout.log" u 10:12 w lp lw 2 + Log::verbose("Smoothing", "%s smoothed-xyz(6-8) %f %f %f " + "xyz(10-12) %f %f %f", getIdent().c_str(), + cur_xyz.getX(), cur_xyz.getY(), cur_xyz.getZ(), + getXYZ().getX(), getXYZ().getY(), getXYZ().getZ()); +#endif + + Vec3 xyz = cur_xyz + offset_xyz; m_node->setPosition(xyz.toIrrVector()); - btQuaternion r_all = getRotation()*rotation; + btQuaternion r_all = cur_rot * rotation; if(btFuzzyZero(r_all.getX()) && btFuzzyZero(r_all.getY()-0.70710677f) && btFuzzyZero(r_all.getZ()) && btFuzzyZero(r_all.getW()-0.70710677f) ) r_all.setX(0.000001f); @@ -133,6 +235,7 @@ void Moveable::reset() m_node->setVisible(true); // In case that the objects was eliminated #endif + m_smoothed_transform = m_transform; Vec3 up = getTrans().getBasis().getColumn(1); m_pitch = atan2(up.getZ(), fabsf(up.getY())); m_roll = atan2(up.getX(), up.getY()); diff --git a/src/karts/moveable.hpp b/src/karts/moveable.hpp index 9281ddabd..d18757bf4 100644 --- a/src/karts/moveable.hpp +++ b/src/karts/moveable.hpp @@ -32,6 +32,8 @@ using namespace irr; #include "utils/no_copy.hpp" #include "utils/vec3.hpp" +#include + class Material; /** @@ -40,6 +42,13 @@ class Material; class Moveable: public NoCopy { private: + enum SmoothingState + { + SS_NONE = 0, + SS_TO_ADJUST, + SS_TO_REAL + }; + Vec3 m_velocityLC; /** m_start_smoothing_postion, + m_adjust_position; - /** Similar to m_positional_error for rotation. */ - btQuaternion m_rotational_error; + Vec3 m_adjust_control_point; + + std::pair m_prev_position_data; + + float m_adjust_time, m_adjust_time_dt; + + SmoothingState m_smoothing; + + btTransform m_smoothed_transform; protected: UserPointer m_user_pointer; @@ -129,14 +146,31 @@ public: &getTrans() const {return m_transform;} void setTrans(const btTransform& t); void updatePosition(); - void addError(const Vec3& pos_error, - const btQuaternion &rot_error); // ------------------------------------------------------------------------ /** Called once per rendered frame. It is used to only update any graphical * effects. * \param dt Time step size (since last call). */ virtual void updateGraphics(float dt) = 0; + // ------------------------------------------------------------------------ + void prepareSmoothing(); + // ------------------------------------------------------------------------ + void checkSmoothing(); + // ------------------------------------------------------------------------ + const btTransform &getSmoothedTrans() const + { return m_smoothed_transform; } + // ------------------------------------------------------------------------ + const Vec3& getSmoothedXYZ() const + { return (Vec3&)m_smoothed_transform.getOrigin(); } + // ------------------------------------------------------------------------ + virtual bool smoothRotation() const { return true; } + // ------------------------------------------------------------------------ + virtual const std::string& getIdent() const + { + static std::string unused("unused"); + return unused; + } + }; // class Moveable #endif diff --git a/src/network/rewind_queue.cpp b/src/network/rewind_queue.cpp index 81aa39ee8..e0e7a021c 100644 --- a/src/network/rewind_queue.cpp +++ b/src/network/rewind_queue.cpp @@ -210,7 +210,6 @@ void RewindQueue::mergeNetworkData(int world_ticks, bool *needs_rewind, // Only a client ever rewinds. So the rewind time should be the latest // received state before current world time (if any) *rewind_ticks = -9999; - bool adjust_next = false; // FIXME: making m_network_events sorted would prevent the need to // go through the whole list of events @@ -261,7 +260,8 @@ void RewindQueue::mergeNetworkData(int world_ticks, bool *needs_rewind, // any server message should be in the client's past - but it can // happen during debugging) we need to rewind to getTicks (in order // to get the latest state). - if (NetworkConfig::get()->isClient() && (*i)->getTicks() <= world_ticks) + if (NetworkConfig::get()->isClient() && + (*i)->getTicks() <= world_ticks && (*i)->isState()) { // We need rewind if we receive an event in the past. This will // then trigger a rewind later. Note that we only rewind to the