From 30cff0075783655208a5d573a67d749cd9596835 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 15 Feb 2017 23:56:43 +1100 Subject: [PATCH 01/17] Reduce internal physics timestep to test if collision handling improves. --- src/physics/physics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index bd3e92f55..06926a3cd 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -152,7 +152,7 @@ void Physics::update(float dt) // Maximum of three substeps. This will work for framerate down to // 20 FPS (bullet default frequency is 60 HZ). - m_dynamics_world->stepSimulation(dt, 3); + m_dynamics_world->stepSimulation(dt, 6, 1.0f/120.0f); // Now handle the actual collision. Note: flyables can not be removed // inside of this loop, since the same flyables might hit more than one From a17b537699a1ff65255a6b4e9e28741c6101c97d Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 20 Feb 2017 07:41:33 +1100 Subject: [PATCH 02/17] Make cannon smoother. --- src/animations/ipo.cpp | 48 ++++++++++++++++++++++++++++++++++ src/animations/ipo.hpp | 11 ++++---- src/karts/cannon_animation.cpp | 23 ++++++++++------ src/karts/cannon_animation.hpp | 19 ++++++++++---- src/tracks/check_cannon.cpp | 11 ++++---- src/tracks/check_cannon.hpp | 3 ++- src/tracks/check_line.hpp | 4 +++ 7 files changed, 95 insertions(+), 24 deletions(-) diff --git a/src/animations/ipo.cpp b/src/animations/ipo.cpp index 4499592af..139378710 100644 --- a/src/animations/ipo.cpp +++ b/src/animations/ipo.cpp @@ -363,6 +363,38 @@ float Ipo::IpoData::getCubicBezier(float t, float p0, float p1, return ((a*t+b)*t+c)*t+p0; } // bezier +// ---------------------------------------------------------------------------- +/** Determines the rotation between the start and end of this curve. + */ +btQuaternion Ipo::IpoData::getOverallRotation() +{ + // Vectors at start and end of curve + Vec3 start, end; + + if (m_interpolation == IP_BEZIER) + { + // In case of Bezier use the handles to get initial and final + // orientation + start = m_handle2[0] - m_handle1[0]; + end = *m_handle2.back() - *m_handle1.back(); + } + else // Const or linear + { + // In this case determine the start vector by selecting using the + // beginning and a second point a bit further on the curve + start.setX(get(m_start_time + 0.1f, 0, 0) - m_points[0].getX()); + start.setY(get(m_start_time + 0.1f, 1, 0) - m_points[0].getY()); + start.setZ(get(m_start_time + 0.1f, 2, 0) - m_points[0].getZ()); + int n = m_points.size() - 2; + end. setX(get(m_end_time - 0.1f, 0, n) - m_points[n].getX()); + end. setY(get(m_end_time - 0.1f, 1, n) - m_points[n].getY()); + end. setZ(get(m_end_time - 0.1f, 2, n) - m_points[n].getZ()); + } + + btQuaternion q = shortestArcQuatNormalize2(start, end); + return q; +} // IpData::getOverallRoation + // ============================================================================ /** The Ipo constructor. Ipos can share the actual data to interpolate, which * is stored in a separate IpoData object, see Ipo(const Ipo *ipo) @@ -499,3 +531,19 @@ float Ipo::get(float time, unsigned int index) const assert(!std::isnan(rval)); return rval; } // get + +// ---------------------------------------------------------------------------- +/** Return the quaternion that rotates an object form the start of the IPO + * to the end. + */ +btQuaternion Ipo::getOverallRotation() +{ + // In case of a single point only: + if (m_next_n == 0) + { + // Return a unit quaternion + btQuaternion q(0, 0, 0, 1); + return q; + } + return m_ipo_data->getOverallRotation(); +} // getOverallRoation \ No newline at end of file diff --git a/src/animations/ipo.hpp b/src/animations/ipo.hpp index fadff2028..39eda9816 100644 --- a/src/animations/ipo.hpp +++ b/src/animations/ipo.hpp @@ -81,10 +81,10 @@ private: private: float getCubicBezier(float t, float p0, float p1, float p2, float p3) const; - void approximateBezier(float t0, float t1, - const Vec3 &p0, const Vec3 &p1, - const Vec3 &h0, const Vec3 &h2, - unsigned int rec_level = 0); + void approximateBezier(float t0, float t1, + const Vec3 &p0, const Vec3 &p1, + const Vec3 &h0, const Vec3 &h2, + unsigned int rec_level = 0); public: IpoData(const XMLNode &curve, float fps, bool reverse); void readCurve(const XMLNode &node, bool reverse); @@ -94,6 +94,7 @@ private: const Vec3 &h1, const Vec3 &h2); float adjustTime(float time); float get(float time, unsigned int index, unsigned int n); + btQuaternion getOverallRotation(); }; // IpoData // ------------------------------------------------------------------------ @@ -123,7 +124,7 @@ public: float get(float time, unsigned int index) const; void setInitialTransform(const Vec3 &xyz, const Vec3 &hpr); void reset(); - + btQuaternion getOverallRotation(); // ------------------------------------------------------------------------ /** Returns the raw data points for this IPO. */ const std::vector& getPoints() const { return m_ipo_data->m_points; } diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index 39ce1a933..036a7108a 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -27,8 +27,10 @@ #include "LinearMath/btTransform.h" -CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo) - : AbstractKartAnimation(kart, "CannonAnimation") +CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, + const Vec3 &start_left, const Vec3 &start_right, + const Vec3 &end_left, const Vec3 &end_right ) + : AbstractKartAnimation(kart, "CannonAnimation") { m_curve = new AnimationBase(ipo); m_timer = ipo->getEndTime(); @@ -39,6 +41,12 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo) m_curve->update(0, &m_previous_orig_xyz); m_delta = kart->getXYZ() - m_previous_orig_xyz; + m_delta_rotation = + shortestArcQuatNormalize2(start_left - start_right, + end_left - end_right ); + + + // Now the delta vector needs to be rotated back, so that it will point // in the right direction when it is (in update) rotated to be the same // as the kart's heading. To estimate the angle at the start, use the @@ -46,11 +54,6 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo) const float dt = 0.1f; Vec3 xyz1; m_curve->update(dt, &xyz1); - core::vector3df rot = (m_previous_orig_xyz-xyz1).toIrrVector() - .getHorizontalAngle(); - btQuaternion q(Vec3(0,1,0),rot.Y*DEGREE_TO_RAD); - btMatrix3x3 m(q); - m_delta = m * m_delta; // The previous call to m_curve->update will set the internal timer // of the curve to dt. Reset it to 0 to make sure the timer is in @@ -104,7 +107,11 @@ void CannonAnimation::update(float dt) } m_previous_orig_xyz = xyz; - Vec3 rotated_delta = m_kart->getTrans().getBasis()*m_delta; + btQuaternion zero(0, 0, 0, 1); + // The timer count backwards, so the fraction goes from 1 to 0 + float f = m_timer / m_curve->getAnimationDuration(); + btQuaternion current_rot = m_delta_rotation.slerp(zero, f); + Vec3 rotated_delta = quatRotate(current_rot, m_delta); m_kart->setXYZ(xyz + rotated_delta); AbstractKartAnimation::update(dt); diff --git a/src/karts/cannon_animation.hpp b/src/karts/cannon_animation.hpp index bb8022c25..44019efea 100644 --- a/src/karts/cannon_animation.hpp +++ b/src/karts/cannon_animation.hpp @@ -22,15 +22,18 @@ #include "karts/abstract_kart_animation.hpp" #include "utils/vec3.hpp" -/** This animation shoots the kart to a specified point on the track. - * - * \ingroup karts - */ +#include "LinearMath/btQuaternion.h" class AbstractKart; class AnimationBase; class Ipo; + +/** This animation shoots the kart to a specified point on the track. + * + * \ingroup karts + */ + class CannonAnimation: public AbstractKartAnimation { protected: @@ -40,6 +43,10 @@ protected: * kart position (so the kart moves relative to the curve). */ Vec3 m_delta; + /** The amount of rotation to be applied to m_delta so that it keeps + * being on the 'right' side of the curve. */ + btQuaternion m_delta_rotation; + /** Stores the curve interpolation for the cannon. */ AnimationBase *m_curve; @@ -48,7 +55,9 @@ protected: Vec3 m_previous_orig_xyz; public: - CannonAnimation(AbstractKart *kart, Ipo *ipo); + CannonAnimation(AbstractKart *kart, Ipo *ipo, + const Vec3 &start_left, const Vec3 &start_right, + const Vec3 &end_left, const Vec3 &end_right); virtual ~CannonAnimation(); virtual void update(float dt); diff --git a/src/tracks/check_cannon.cpp b/src/tracks/check_cannon.cpp index 2fb3000c8..1d2f68a3d 100644 --- a/src/tracks/check_cannon.cpp +++ b/src/tracks/check_cannon.cpp @@ -35,13 +35,14 @@ CheckCannon::CheckCannon(const XMLNode &node, unsigned int index) : CheckLine(node, index) { - core::vector3df p1, p2; - if(!node.get("target-p1", &p1) || !node.get("target-p2", &p2)) + if( !node.get("target-p1", &m_target_left ) || + !node.get("target-p2", &m_target_right) ) Log::fatal("CheckCannon", "No target line specified."); - m_target.setLine(p1, p2); + m_curve = new Ipo(*(node.getNode("curve")), /*fps*/25, /*reverse*/race_manager->getReverseTrack()); + #if defined(DEBUG) && !defined(SERVER_ONLY) if(UserConfigParams::m_track_debug) { @@ -73,9 +74,9 @@ CheckCannon::~CheckCannon() */ void CheckCannon::trigger(unsigned int kart_index) { - Vec3 target(m_target.getMiddle()); AbstractKart *kart = World::getWorld()->getKart(kart_index); if(kart->getKartAnimation()) return; - new CannonAnimation(kart, m_curve->clone()); + new CannonAnimation(kart, m_curve->clone(), getLeftPoint(), getRightPoint(), + m_target_left, m_target_right); } // CheckCannon diff --git a/src/tracks/check_cannon.hpp b/src/tracks/check_cannon.hpp index 3507195d3..a57fb4893 100644 --- a/src/tracks/check_cannon.hpp +++ b/src/tracks/check_cannon.hpp @@ -37,7 +37,8 @@ class CheckCannon : public CheckLine { private: /** The target point the kart will fly to. */ - core::line3df m_target; + Vec3 m_target_left; + Vec3 m_target_right; /** Stores the cannon curve data. */ Ipo *m_curve; diff --git a/src/tracks/check_line.hpp b/src/tracks/check_line.hpp index 1da3ec34c..3b06e2ca8 100644 --- a/src/tracks/check_line.hpp +++ b/src/tracks/check_line.hpp @@ -71,6 +71,10 @@ private: /** How much a kart is allowed to be over the minimum height of a * quad and still considered to be able to cross it. */ static const int m_over_min_height = 4; +protected: + const Vec3 &getLeftPoint() const { return m_left_point; } + const Vec3 &getRightPoint() const { return m_right_point; } + public: CheckLine(const XMLNode &node, unsigned int index); virtual ~CheckLine(); From 41df6afbf9ed48b720c8a96afed6735eeeb9f625 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 20 Feb 2017 09:22:09 +1100 Subject: [PATCH 03/17] Fixed compilation. --- src/karts/cannon_animation.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index 036a7108a..5f0666b50 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -41,9 +41,9 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, m_curve->update(0, &m_previous_orig_xyz); m_delta = kart->getXYZ() - m_previous_orig_xyz; - m_delta_rotation = - shortestArcQuatNormalize2(start_left - start_right, - end_left - end_right ); + Vec3 v1 = start_left - start_right; + Vec3 v2 = end_left - end_right; + m_delta_rotation = shortestArcQuatNormalize2(v1, v2 ); From f0efce62b10bdb1075335d0da097060cf838ab13 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 23 Feb 2017 09:00:07 +1100 Subject: [PATCH 04/17] Added debug view of target cannon line. --- src/tracks/check_cannon.cpp | 56 +++++++++++++++++++++++++++++++++++++ src/tracks/check_cannon.hpp | 4 +++ 2 files changed, 60 insertions(+) diff --git a/src/tracks/check_cannon.cpp b/src/tracks/check_cannon.cpp index 1d2f68a3d..4b4317f7f 100644 --- a/src/tracks/check_cannon.cpp +++ b/src/tracks/check_cannon.cpp @@ -21,7 +21,9 @@ #include "animations/animation_base.hpp" #include "animations/ipo.hpp" #include "config/user_config.hpp" +#include "graphics/irr_driver.hpp" #include "graphics/show_curve.hpp" +#include "graphics/stk_tex_manager.hpp" #include "io/xml_node.hpp" #include "karts/abstract_kart.hpp" #include "karts/cannon_animation.hpp" @@ -51,7 +53,41 @@ CheckCannon::CheckCannon(const XMLNode &node, unsigned int index) for(unsigned int i=0; iaddPoint(p[i]); } + if (UserConfigParams::m_check_debug) + { + video::SMaterial material; + material.setFlag(video::EMF_BACK_FACE_CULLING, false); + material.setFlag(video::EMF_LIGHTING, false); + material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; + scene::IMesh *mesh = irr_driver->createQuadMesh(&material, + /*create mesh*/true); + scene::IMeshBuffer *buffer = mesh->getMeshBuffer(0); + + assert(buffer->getVertexType() == video::EVT_STANDARD); + irr::video::S3DVertex* vertices + = (video::S3DVertex*)buffer->getVertices(); + Vec3 height(0, 3, 0); + vertices[0].Pos = m_target_left.toIrrVector(); + vertices[1].Pos = m_target_right.toIrrVector(); + vertices[2].Pos = Vec3(m_target_right + height).toIrrVector(); + vertices[3].Pos = Vec3(m_target_left + height).toIrrVector(); + for (unsigned int i = 0; i<4; i++) + { + vertices[i].Color = m_active_at_reset + ? video::SColor(128, 255, 0, 0) + : video::SColor(128, 128, 128, 128); + } + buffer->recalculateBoundingBox(); + buffer->getMaterial().setTexture(0, STKTexManager::getInstance()->getUnicolorTexture(video::SColor(128, 255, 105, 180))); + buffer->getMaterial().setTexture(1, STKTexManager::getInstance()->getUnicolorTexture(video::SColor(0, 0, 0, 0))); + buffer->getMaterial().setTexture(2, STKTexManager::getInstance()->getUnicolorTexture(video::SColor(0, 0, 0, 0))); + buffer->getMaterial().BackfaceCulling = false; + //mesh->setBoundingBox(buffer->getBoundingBox()); + m_debug_target_node = irr_driver->addMesh(mesh, "checkdebug"); + mesh->drop(); + } #endif // DEBUG AND !SERVER_ONLY + } // CheckCannon // ---------------------------------------------------------------------------- @@ -67,6 +103,26 @@ CheckCannon::~CheckCannon() #endif } // ~CheckCannon +// ---------------------------------------------------------------------------- +void CheckCannon::changeDebugColor(bool is_active) +{ +#if defined(DEBUG) && !defined(SERVER_ONLY) + CheckLine::changeDebugColor(is_active); + + scene::IMesh *mesh = m_debug_target_node->getMesh(); + scene::IMeshBuffer *buffer = mesh->getMeshBuffer(0); + irr::video::S3DVertex* vertices + = (video::S3DVertex*)buffer->getVertices(); + video::SColor color = is_active ? video::SColor(192, 255, 0, 0) + : video::SColor(192, 128, 128, 128); + for (unsigned int i = 0; i<4; i++) + { + vertices[i].Color = color; + } + buffer->getMaterial().setTexture(0, STKTexManager::getInstance()->getUnicolorTexture(color)); +#endif +} // changeDebugColor + // ---------------------------------------------------------------------------- /** Called when the check line is triggered. This function creates a cannon * animation object and attaches it to the kart. diff --git a/src/tracks/check_cannon.hpp b/src/tracks/check_cannon.hpp index a57fb4893..0708d944a 100644 --- a/src/tracks/check_cannon.hpp +++ b/src/tracks/check_cannon.hpp @@ -47,12 +47,16 @@ private: /** If track debugging is enabled, this will show the the curve of * the cannon in the race. */ ShowCurve * m_show_curve; + + /** Used to display debug information about checklines. */ + scene::IMeshSceneNode *m_debug_target_node; #endif public: CheckCannon(const XMLNode &node, unsigned int index); virtual ~CheckCannon(); virtual void trigger(unsigned int kart_index); + virtual void changeDebugColor(bool is_active); }; // CheckLine #endif From b884f6429d017a30fe8ecbf8309811de85be6fb5 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 23 Feb 2017 17:12:15 +1100 Subject: [PATCH 05/17] Fix stuttering of camera/kart when usnig a cannon (caused by the cannon animation updating the (physical) kart position after the graphical position was taken from the physical position, causing a stuttering because of the differences between those two values. --- src/karts/kart.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 43f54ae84..4a6278775 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1225,7 +1225,19 @@ void Kart::update(float dt) } } - // Update the position and other data taken from the physics + // This is to avoid a rescue immediately after an explosion + const bool has_animation_before = m_kart_animation != NULL; + // A kart animation can change the xyz position. This needs to be done + // before updating the graphical position (which is done in + // Moveable::update() ), otherwise 'stuttering' can happen (caused by + // graphical and physical position not being the same). + if (has_animation_before) + { + m_kart_animation->update(dt); + } + // Update the position and other data taken from the physics (or + // an animation which calls setXYZ(), which also updates the kart + // physical position). Moveable::update(dt); Vec3 front(0, 0, getKartLength()*0.5f); @@ -1309,9 +1321,6 @@ void Kart::update(float dt) // Used to prevent creating a rescue animation after an explosion animation // got deleted - const bool has_animation_before = m_kart_animation!= NULL; - if(has_animation_before) - m_kart_animation->update(dt); m_attachment->update(dt); From ae375c50b60c962f6507d91390712b7df5ffc237 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 7 Mar 2017 22:11:15 +1100 Subject: [PATCH 06/17] Allow the player to go to the left and right when in a cannon. --- src/animations/animation_base.cpp | 33 +++++++ src/animations/animation_base.hpp | 7 +- src/animations/ipo.cpp | 156 +++++++++++++++++++++--------- src/animations/ipo.hpp | 8 +- src/karts/cannon_animation.cpp | 142 ++++++++++++++++++++------- src/karts/cannon_animation.hpp | 16 +-- 6 files changed, 271 insertions(+), 91 deletions(-) diff --git a/src/animations/animation_base.cpp b/src/animations/animation_base.cpp index ff10089e0..029273cf7 100644 --- a/src/animations/animation_base.cpp +++ b/src/animations/animation_base.cpp @@ -107,3 +107,36 @@ void AnimationBase::update(float dt, Vec3 *xyz, Vec3 *hpr, Vec3 *scale) } } // update +// ---------------------------------------------------------------------------- +/** Return the time, position and rotation at the specified time. It does not + * update the internal timer as update() does. + * \param dt Time since last call. + * \param xyz Position to be updated. + * \param hpr Rotation to be updated. + */ +void AnimationBase::getAt(float time, Vec3 *xyz, Vec3 *hpr, Vec3 *scale) +{ + assert(!std::isnan(time)); + + // Don't do anything if the animation is disabled + if (!m_playing) return; + + for_var_in(Ipo*, curr, m_all_ipos) + { + curr->update(time, xyz, hpr, scale); + } +} // getAt + +// ---------------------------------------------------------------------------- +/** Returns the derivative at the specified point. + * \param time The time for which to determine the derivative. + * \param xyz Float pointer to store the result. + */ +void AnimationBase::getDerivativeAt(float time, Vec3 *xyz) +{ + for_var_in(Ipo*, curr, m_all_ipos) + { + curr->getDerivative(time, xyz); + } + xyz->normalize(); +} diff --git a/src/animations/animation_base.hpp b/src/animations/animation_base.hpp index 98e30f4ca..8f5430542 100644 --- a/src/animations/animation_base.hpp +++ b/src/animations/animation_base.hpp @@ -67,8 +67,11 @@ public: AnimationBase(const XMLNode &node); AnimationBase(Ipo *ipo); virtual ~AnimationBase() {} - virtual void update(float dt, Vec3 *xyz=NULL, Vec3 *hpr=NULL, - Vec3 *scale=NULL); + virtual void update(float dt, Vec3 *xyz=NULL, Vec3 *hpr=NULL, + Vec3 *scale=NULL); + virtual void getAt(float time, Vec3 *xyz = NULL, Vec3 *hpr = NULL, + Vec3 *scale = NULL); + virtual void getDerivativeAt(float time, Vec3 *xyz); /** This needs to be implemented by the inheriting classes. It is called * once per frame from the track. It has a dummy implementation that * just asserts so that this class can be instantiated in diff --git a/src/animations/ipo.cpp b/src/animations/ipo.cpp index 139378710..2338a4ec1 100644 --- a/src/animations/ipo.cpp +++ b/src/animations/ipo.cpp @@ -346,7 +346,7 @@ float Ipo::IpoData::get(float time, unsigned int index, unsigned int n) } // switch // Keep the compiler happy: return 0; -} // get +} // IpoData::get // ---------------------------------------------------------------------------- /** Computes a cubic bezier curve for a given t in [0,1] and four control @@ -361,39 +361,65 @@ float Ipo::IpoData::getCubicBezier(float t, float p0, float p1, float b = 3.0f*(p2-p1)-c; float a = p3 - p0 - c - b; return ((a*t+b)*t+c)*t+p0; -} // bezier +} // getCubicBezier // ---------------------------------------------------------------------------- -/** Determines the rotation between the start and end of this curve. +/** Determines the derivative of a IPO at a given point. + * \param time At what time value the derivative is to be computed. + * \param index IpoData is based on 3d data. The index specified which + * value to use (0=x, 1=y, 2=z). + * \param n Curve segment to be used for the computation. It must be correct + * for the specified time value. */ -btQuaternion Ipo::IpoData::getOverallRotation() +float Ipo::IpoData::getDerivative(float time, unsigned int index, + unsigned int n) { - // Vectors at start and end of curve - Vec3 start, end; - - if (m_interpolation == IP_BEZIER) + switch (m_interpolation) { - // In case of Bezier use the handles to get initial and final - // orientation - start = m_handle2[0] - m_handle1[0]; - end = *m_handle2.back() - *m_handle1.back(); - } - else // Const or linear - { - // In this case determine the start vector by selecting using the - // beginning and a second point a bit further on the curve - start.setX(get(m_start_time + 0.1f, 0, 0) - m_points[0].getX()); - start.setY(get(m_start_time + 0.1f, 1, 0) - m_points[0].getY()); - start.setZ(get(m_start_time + 0.1f, 2, 0) - m_points[0].getZ()); - int n = m_points.size() - 2; - end. setX(get(m_end_time - 0.1f, 0, n) - m_points[n].getX()); - end. setY(get(m_end_time - 0.1f, 1, n) - m_points[n].getY()); - end. setZ(get(m_end_time - 0.1f, 2, n) - m_points[n].getZ()); + case IP_CONST: return 0; // Const --> Derivative is 0 + case IP_LINEAR: { + return (m_points[n + 1][index] - m_points[n][index]) / + (m_points[n + 1].getW() - m_points[n].getW()); } + case IP_BEZIER: { + if (n == m_points.size() - 1) + { + // Only const, so derivative is 0 + return 0; + } + float t = (time - m_points[n].getW()) + / (m_points[n + 1].getW() - m_points[n].getW()); + return getCubicBezierDerivative(t, + m_points [n ][index], + m_handle2[n ][index], + m_handle1[n + 1][index], + m_points [n + 1][index] ); + } // case IPBEZIER + default: + Log::warn("Ipo::IpoData", "Incorrect interpolation %d", + m_interpolation); + } // switch + return 0; +} // IpoData::getDerivative - btQuaternion q = shortestArcQuatNormalize2(start, end); - return q; -} // IpData::getOverallRoation + +// ---------------------------------------------------------------------------- +/** Returns the derivative of a cubic bezier curve for a given t in [0,1] and + * four control points. The curve will go through p0 (t=0). + * \param t The parameter for the bezier curve, must be in [0,1]. + * \param p0, p1, p2, p3 The four control points. + */ +float Ipo::IpoData::getCubicBezierDerivative(float t, float p0, float p1, + float p2, float p3) const +{ + float c = 3.0f*(p1 - p0); + float b = 3.0f*(p2 - p1) - c; + float a = p3 - p0 - c - b; + // f(t) = ((a*t + b)*t + c)*t + p0; + // = a*t^3 +b*t^2 + c*t + p0 + // --> f'(t) = 3*a*t^2 + 2*b*t + c + return (3*a * t + 2*b) * t + c; +} // bezier // ============================================================================ /** The Ipo constructor. Ipos can share the actual data to interpolate, which @@ -503,6 +529,28 @@ void Ipo::update(float time, Vec3 *xyz, Vec3 *hpr,Vec3 *scale) } // update +// ---------------------------------------------------------------------------- +/** Updates the value of m_next_n to point to the right ipo segment based on + * the time. + * \param t Time for which m_next_n needs to be updated. + */ +void Ipo::updateNextN(float time) const +{ + time = m_ipo_data->adjustTime(time); + + // Time was reset since the last cached value for n, + // reset n to start from the beginning again. + if (time < m_ipo_data->m_points[m_next_n - 1].getW()) + m_next_n = 1; + // Search for the first point in the (sorted) array which is greater or equal + // to the current time. + while (m_next_n < m_ipo_data->m_points.size() - 1 && + time >= m_ipo_data->m_points[m_next_n].getW()) + { + m_next_n++; + } // while +} // updateNextN + // ---------------------------------------------------------------------------- /** Returns the interpolated value at the current time (which this objects * keeps track of). @@ -516,34 +564,50 @@ float Ipo::get(float time, unsigned int index) const if(m_next_n==0) return m_ipo_data->m_points[0][index]; - time = m_ipo_data->adjustTime(time); + updateNextN(time); - // Time was reset since the last cached value for n, - // reset n to start from the beginning again. - if(time < m_ipo_data->m_points[m_next_n-1].getW()) - m_next_n = 1; - // Search for the first point in the (sorted) array which is greater or equal - // to the current time. - while(m_next_nm_points.size()-1 && - time >=m_ipo_data->m_points[m_next_n].getW()) - m_next_n++; float rval = m_ipo_data->get(time, index, m_next_n-1); assert(!std::isnan(rval)); return rval; } // get // ---------------------------------------------------------------------------- -/** Return the quaternion that rotates an object form the start of the IPO - * to the end. +/** Returns the derivative for any location based curves. + * \param time Time for which the derivative is being computed. + * \param xyz Pointer where the results should be stored. */ -btQuaternion Ipo::getOverallRotation() +void Ipo::getDerivative(float time, Vec3 *xyz) { - // In case of a single point only: + // Avoid crash in case that only one point is given for this IPO. if (m_next_n == 0) { - // Return a unit quaternion - btQuaternion q(0, 0, 0, 1); - return q; + // Derivative has no real meaning in case of a single point. + // So just return a dummy value. + xyz->setValue(1, 0, 0); + return; } - return m_ipo_data->getOverallRotation(); -} // getOverallRoation \ No newline at end of file + + updateNextN(time); + switch (m_ipo_data->m_channel) + { + case Ipo::IPO_LOCX: xyz->setX(m_ipo_data->getDerivative(time, m_next_n, 0)); break; + case Ipo::IPO_LOCY: xyz->setY(m_ipo_data->getDerivative(time, m_next_n, 0)); break; + case Ipo::IPO_LOCZ: xyz->setZ(m_ipo_data->getDerivative(time, m_next_n, 0)); break; + case Ipo::IPO_LOCXYZ: + { + if (xyz) + { + for (unsigned int j = 0; j < 3; j++) + (*xyz)[j] = m_ipo_data->getDerivative(time, j, m_next_n-1); + } + break; + } + default: Log::warn("IPO", "Unexpected channel %d for derivate.", + m_ipo_data->m_channel ); + xyz->setValue(1, 0, 0); + break; + } // switch + + +} // getDerivative + diff --git a/src/animations/ipo.hpp b/src/animations/ipo.hpp index 39eda9816..f1b83a3e9 100644 --- a/src/animations/ipo.hpp +++ b/src/animations/ipo.hpp @@ -81,6 +81,8 @@ private: private: float getCubicBezier(float t, float p0, float p1, float p2, float p3) const; + float getCubicBezierDerivative(float t, float p0, float p1, + float p2, float p3) const; void approximateBezier(float t0, float t1, const Vec3 &p0, const Vec3 &p1, const Vec3 &h0, const Vec3 &h2, @@ -94,7 +96,7 @@ private: const Vec3 &h1, const Vec3 &h2); float adjustTime(float time); float get(float time, unsigned int index, unsigned int n); - btQuaternion getOverallRotation(); + float getDerivative(float time, unsigned int index, unsigned int n); }; // IpoData // ------------------------------------------------------------------------ @@ -114,6 +116,8 @@ private: * it is declared mutable). */ mutable unsigned int m_next_n; + void updateNextN(float time) const; + Ipo(const Ipo *ipo); public: Ipo(const XMLNode &curve, float fps=25, bool reverse=false); @@ -121,10 +125,10 @@ public: Ipo *clone(); void update(float time, Vec3 *xyz=NULL, Vec3 *hpr=NULL, Vec3 *scale=NULL); + void getDerivative(float time, Vec3 *xyz); float get(float time, unsigned int index) const; void setInitialTransform(const Vec3 &xyz, const Vec3 &hpr); void reset(); - btQuaternion getOverallRotation(); // ------------------------------------------------------------------------ /** Returns the raw data points for this IPO. */ const std::vector& getPoints() const { return m_ipo_data->m_points; } diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index 5f0666b50..10e7ec328 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -35,26 +35,90 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, m_curve = new AnimationBase(ipo); m_timer = ipo->getEndTime(); + float kw2 = m_kart->getKartModel()->getWidth()*0.5f; + + // First make sure that left and right points are indeed correct + Vec3 my_start_left = start_left; + Vec3 my_start_right = start_right; + Vec3 p0, p1; + m_curve->getAt(0, &p0); + m_curve->getAt(0.1f, &p1); + Vec3 p2 = 0.5f*(p0 + p1) + m_kart->getNormal(); + if (start_left.sideofPlane(p0, p1, p2) < 0) + { + my_start_left = start_right; + my_start_right = start_left; + } + // First adjust start and end points to take on each side half the kart + // width into account: + Vec3 direction = my_start_right - my_start_left; + direction.normalize(); + + Vec3 adj_start_left = my_start_left + kw2 * direction; + Vec3 adj_start_right = my_start_right - kw2 * direction; + + // Same adjustments for end points + float t = m_curve->getAnimationDuration(); + Vec3 my_end_left = end_left; + Vec3 my_end_right = end_right; + m_curve->getAt(t-0.1f, &p0); + m_curve->getAt(t, &p1); + p2 = 0.5f*(p0 + p1) + m_kart->getNormal(); + if (end_left.sideofPlane(p0, p1, p2) < 0) + { + my_end_left = end_right; + my_end_right = end_left; + } + // Left and right end points are sometimes swapped + direction = my_end_right - my_end_left; + direction.normalize(); + + Vec3 adj_end_left = my_end_left + kw2 * direction; + Vec3 adj_end_right = my_end_right - kw2 * direction; + + // The kart position is divided into three components: + // 1) The point at the curve at t=0. + // 2) A component parallel to the start line. This component is scaled + // depending on time, length of start- and end-line (e.g. if the + // end line is twice as long as the start line, this will make sure + // that a kart starting at the very left of the start line will end + // up at the very left of the end line. This component can also be + // adjusted by steering while in the air. This is done by modifying + // m_fraction_of_line, which is multiplied with the current width + // vector. + // 3) The rest, i.e. the amoount that the kart is ahead and above the + // start line. This is stored in m_delta. + // // Compute the delta between the kart position and the start of the curve. // This delta is rotated with the kart and added to the interpolated curve // position to get the actual kart position during the animation. - m_curve->update(0, &m_previous_orig_xyz); - m_delta = kart->getXYZ() - m_previous_orig_xyz; - Vec3 v1 = start_left - start_right; - Vec3 v2 = end_left - end_right; - m_delta_rotation = shortestArcQuatNormalize2(v1, v2 ); + Vec3 curve_xyz; + m_curve->update(0, &curve_xyz); + m_delta = kart->getXYZ() - curve_xyz; + m_start_line = 0.5f*(adj_start_right - adj_start_left); + m_end_line = 0.5f*(adj_end_right - adj_end_left ); + Vec3 v = adj_start_left - adj_start_right; + float l = v.length(); + v /= l; - // Now the delta vector needs to be rotated back, so that it will point - // in the right direction when it is (in update) rotated to be the same - // as the kart's heading. To estimate the angle at the start, use the - // interpolated value at t=dt: - const float dt = 0.1f; - Vec3 xyz1; - m_curve->update(dt, &xyz1); + // Compute on which fraction of the start line the kart is + float f = v.dot(adj_start_left - kart->getXYZ()); + if (f <= 0) + f = 0; + else if (f >= l) + f = l; + else + f = f / l; + // Now f is in [0,1]. Convert it to [-1,1] assuming that the + // ipo for the cannon is in the middle of the start and end line + m_fraction_of_line = 2.0f*f - 1.0f; + Vec3 delta = m_start_line * m_fraction_of_line; + m_delta = m_delta - delta; + // The previous call to m_curve->update will set the internal timer // of the curve to dt. Reset it to 0 to make sure the timer is in // synch with the timer of the CanonAnimation @@ -68,8 +132,9 @@ CannonAnimation::~CannonAnimation() btTransform pos; pos.setOrigin(m_kart->getXYZ()); - pos.setRotation(btQuaternion(btVector3(0.0f, 1.0f, 0.0f), - m_kart->getHeading() )); + //pos.setRotation(btQuaternion(btVector3(0.0f, 1.0f, 0.0f), + // m_kart->getHeading() )); + pos.setRotation(m_kart->getRotation()); m_kart->getBody()->setCenterOfMassTransform(pos); Vec3 v(0, 0, m_kart->getKartProperties()->getEngineMaxSpeed()); @@ -89,30 +154,39 @@ void CannonAnimation::update(float dt) return; } + // Adjust the horizontal location based on steering + m_fraction_of_line += m_kart->getSteerPercent()*dt*2.0f; + + // The timer count backwards, so the fraction goes from 1 to 0 + float f = m_timer / m_curve->getAnimationDuration(); + + btClamp(m_fraction_of_line, -1.0f, 1.0f); + Vec3 current_width = m_start_line * f + m_end_line * (1.0f - f); + + // Get the tangent = derivative at the current point to compute the + // orientation of the kart + Vec3 tangent; + m_curve->getDerivativeAt(m_curve->getAnimationDuration() - m_timer, + &tangent); + + Vec3 forward = m_kart->getTrans().getBasis().getColumn(2); + forward.normalize(); + + // Only adjust the heading. I tried to also adjust pitch, + // but that adds a strong roll to the kart on some cannons + Vec3 v1(tangent), v2(forward); + v1.setY(0); v2.setY(0); + btQuaternion q = m_kart->getRotation()*shortestArcQuatNormalize2(v2, v1); + + m_kart->setRotation(q); + Vec3 xyz; m_curve->update(dt, &xyz); - // It can happen that the same position is returned, e.g. if the end of - // the curve is reached, but due to floating point differences the - // end is not detected in the above test. To avoid that the kart then - // rotates to a heading of 0, do not rotate in this case at all, i.e. - // the previous rotation is kept. - if(xyz!=m_previous_orig_xyz) - { - btQuaternion prev_rot = m_kart->getRotation(); - core::vector3df rot = (xyz-m_previous_orig_xyz).toIrrVector() - .getHorizontalAngle(); - btQuaternion q(Vec3(0,1,0),rot.Y*DEGREE_TO_RAD); - m_kart->setRotation(prev_rot.slerp(q,0.1f)); - } - m_previous_orig_xyz = xyz; + Vec3 rotated_delta = quatRotate(q, m_delta ) + current_width * m_fraction_of_line; + m_kart->setXYZ(xyz+rotated_delta); + // m_kart->setXYZ(xyz); - btQuaternion zero(0, 0, 0, 1); - // The timer count backwards, so the fraction goes from 1 to 0 - float f = m_timer / m_curve->getAnimationDuration(); - btQuaternion current_rot = m_delta_rotation.slerp(zero, f); - Vec3 rotated_delta = quatRotate(current_rot, m_delta); - m_kart->setXYZ(xyz + rotated_delta); AbstractKartAnimation::update(dt); } // update diff --git a/src/karts/cannon_animation.hpp b/src/karts/cannon_animation.hpp index 44019efea..74673396a 100644 --- a/src/karts/cannon_animation.hpp +++ b/src/karts/cannon_animation.hpp @@ -43,16 +43,18 @@ protected: * kart position (so the kart moves relative to the curve). */ Vec3 m_delta; - /** The amount of rotation to be applied to m_delta so that it keeps - * being on the 'right' side of the curve. */ - btQuaternion m_delta_rotation; - /** Stores the curve interpolation for the cannon. */ AnimationBase *m_curve; - /** This stores the original (unmodified) interpolated curve value. THis - * is used to determine the orientation of the kart. */ - Vec3 m_previous_orig_xyz; + /** The original checkline at start. */ + Vec3 m_start_line; + + /** The original checkline at end. */ + Vec3 m_end_line; + + /** Stores the position of the kart relative to the line width + * at the current location. */ + float m_fraction_of_line; public: CannonAnimation(AbstractKart *kart, Ipo *ipo, From 2436161b056d40fcc9280d2c476e9bed740b17ab Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 7 Mar 2017 22:24:46 +1100 Subject: [PATCH 07/17] Approximate bezier curves better to avoid stuttering in cannons. --- src/animations/ipo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/animations/ipo.cpp b/src/animations/ipo.cpp index 2338a4ec1..faa765ef0 100644 --- a/src/animations/ipo.cpp +++ b/src/animations/ipo.cpp @@ -228,7 +228,7 @@ void Ipo::IpoData::approximateBezier(float t0, float t1, // A more sophisticated estimation might be useful (e.g. taking the // difference between a linear approximation and the actual bezier // curve into accound. - if(distance<=2.0f) + if(distance<=0.2f) return; // Insert one point at (t0+t1)/2. First split the left part of From bedb565f249a6605f416c49f04488b336c8e5535 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 8 Mar 2017 17:57:03 +1100 Subject: [PATCH 08/17] Simplified maths. --- src/karts/cannon_animation.cpp | 107 ++++++++++++++++----------------- src/karts/cannon_animation.hpp | 10 +-- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index 10e7ec328..fafaa65de 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -34,77 +34,67 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, { m_curve = new AnimationBase(ipo); m_timer = ipo->getEndTime(); - - float kw2 = m_kart->getKartModel()->getWidth()*0.5f; - + // First make sure that left and right points are indeed correct + // ------------------------------------------------------------- Vec3 my_start_left = start_left; Vec3 my_start_right = start_right; Vec3 p0, p1; + // Define a plane that goes through the middle of the start line + // (the curve's origin must be in the middle of the line. m_curve->getAt(0, &p0); m_curve->getAt(0.1f, &p1); Vec3 p2 = 0.5f*(p0 + p1) + m_kart->getNormal(); if (start_left.sideofPlane(p0, p1, p2) < 0) { + // Left and right start line needs to be swapped my_start_left = start_right; my_start_right = start_left; } + // First adjust start and end points to take on each side half the kart // width into account: Vec3 direction = my_start_right - my_start_left; direction.normalize(); - Vec3 adj_start_left = my_start_left + kw2 * direction; - Vec3 adj_start_right = my_start_right - kw2 * direction; + float kw = m_kart->getKartModel()->getWidth(); + Vec3 adj_start_left = my_start_left + (0.5f*kw) * direction; + Vec3 adj_start_right = my_start_right - (0.5f*kw) * direction; - // Same adjustments for end points - float t = m_curve->getAnimationDuration(); - Vec3 my_end_left = end_left; - Vec3 my_end_right = end_right; - m_curve->getAt(t-0.1f, &p0); - m_curve->getAt(t, &p1); - p2 = 0.5f*(p0 + p1) + m_kart->getNormal(); - if (end_left.sideofPlane(p0, p1, p2) < 0) - { - my_end_left = end_right; - my_end_right = end_left; - } - // Left and right end points are sometimes swapped - direction = my_end_right - my_end_left; - direction.normalize(); + // Store the length of the start and end line, which is used + // during update() to adjust the distance to center + m_start_line_length = (adj_start_left - adj_start_right).length(); + m_end_line_length = (end_left - end_right).length() - kw; - Vec3 adj_end_left = my_end_left + kw2 * direction; - Vec3 adj_end_right = my_end_right - kw2 * direction; - - // The kart position is divided into three components: - // 1) The point at the curve at t=0. - // 2) A component parallel to the start line. This component is scaled - // depending on time, length of start- and end-line (e.g. if the + // The current kart position is divided into three components: + // kart.xyz = curve.xyz + parallel_to_start_line_component + rest + // 1) curve.xyz: The point at the curve at t=0. + // 2) parallel_to_start_line_component: + // A component parallel to the start line. This component is scaled + // depending on time and length of start- and end-line (e.g. if the // end line is twice as long as the start line, this will make sure // that a kart starting at the very left of the start line will end - // up at the very left of the end line. This component can also be + // up at the very left of the end line). This component can also be // adjusted by steering while in the air. This is done by modifying // m_fraction_of_line, which is multiplied with the current width // vector. - // 3) The rest, i.e. the amoount that the kart is ahead and above the - // start line. This is stored in m_delta. + // 3) rest: The amoount that the kart is ahead and above the + // start line. This is stored in m_delta and will be added to the + // newly computed curve xyz coordinates. // // Compute the delta between the kart position and the start of the curve. // This delta is rotated with the kart and added to the interpolated curve // position to get the actual kart position during the animation. - Vec3 curve_xyz; m_curve->update(0, &curve_xyz); m_delta = kart->getXYZ() - curve_xyz; - - m_start_line = 0.5f*(adj_start_right - adj_start_left); - m_end_line = 0.5f*(adj_end_right - adj_end_left ); + // Compute on which fraction of the start line the kart is, to get the + // second component of the kart position: distance along start line Vec3 v = adj_start_left - adj_start_right; float l = v.length(); v /= l; - // Compute on which fraction of the start line the kart is float f = v.dot(adj_start_left - kart->getXYZ()); if (f <= 0) f = 0; @@ -112,11 +102,14 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, f = l; else f = f / l; - // Now f is in [0,1]. Convert it to [-1,1] assuming that the - // ipo for the cannon is in the middle of the start and end line + // Now f is in [0,1] - 0 in case of left side, 1 if the kart is at the + // very right. Convert this to [-1,1] assuming that the ipo for the + // cannon is in the middle of the start and end line m_fraction_of_line = 2.0f*f - 1.0f; - Vec3 delta = m_start_line * m_fraction_of_line; + Vec3 delta = 0.5f*m_fraction_of_line * (adj_start_right - adj_start_left); + // Subtract the horizontal difference, to get the constant offset the + // kart has from the curve. m_delta = m_delta - delta; // The previous call to m_curve->update will set the internal timer @@ -154,21 +147,14 @@ void CannonAnimation::update(float dt) return; } - // Adjust the horizontal location based on steering - m_fraction_of_line += m_kart->getSteerPercent()*dt*2.0f; - - // The timer count backwards, so the fraction goes from 1 to 0 - float f = m_timer / m_curve->getAnimationDuration(); - - btClamp(m_fraction_of_line, -1.0f, 1.0f); - Vec3 current_width = m_start_line * f + m_end_line * (1.0f - f); - + // First compute the current rotation + // ----------------------------------- // Get the tangent = derivative at the current point to compute the - // orientation of the kart + // new orientation of the kart Vec3 tangent; m_curve->getDerivativeAt(m_curve->getAnimationDuration() - m_timer, &tangent); - + // Get the current kart orientation Vec3 forward = m_kart->getTrans().getBasis().getColumn(2); forward.normalize(); @@ -180,13 +166,26 @@ void CannonAnimation::update(float dt) m_kart->setRotation(q); - Vec3 xyz; - m_curve->update(dt, &xyz); + // Then compute the new location of the kart + // ----------------------------------------- + // The timer counts backwards, so the fraction goes from 1 to 0 + float f = m_timer / m_curve->getAnimationDuration(); + float f_current_width = m_start_line_length * f + + m_end_line_length * (1.0f - f); - Vec3 rotated_delta = quatRotate(q, m_delta ) + current_width * m_fraction_of_line; - m_kart->setXYZ(xyz+rotated_delta); - // m_kart->setXYZ(xyz); + // Adjust the horizontal location based on steering + m_fraction_of_line += m_kart->getSteerPercent()*dt*2.0f; + btClamp(m_fraction_of_line, -1.0f, 1.0f); + // horiz_delta is in kart coordinates, the rotation by q will + // transform it to the global coordinate system + Vec3 horiz_delta = Vec3(0.5f*m_fraction_of_line * f_current_width, 0, 0); + + Vec3 rotated_delta = quatRotate(q, m_delta + horiz_delta); + + Vec3 curve_xyz; + m_curve->update(dt, &curve_xyz); + m_kart->setXYZ(curve_xyz+rotated_delta); AbstractKartAnimation::update(dt); } // update diff --git a/src/karts/cannon_animation.hpp b/src/karts/cannon_animation.hpp index 74673396a..51ded2bd8 100644 --- a/src/karts/cannon_animation.hpp +++ b/src/karts/cannon_animation.hpp @@ -46,11 +46,13 @@ protected: /** Stores the curve interpolation for the cannon. */ AnimationBase *m_curve; - /** The original checkline at start. */ - Vec3 m_start_line; + /** Length of the (adjusted, i.e. taking kart width into account) + * start line. */ + float m_start_line_length; - /** The original checkline at end. */ - Vec3 m_end_line; + /** Length of the (adjusted, i.e. taking kart width into account) + * end line. */ + float m_end_line_length; /** Stores the position of the kart relative to the line width * at the current location. */ From 87950c9a94881fb84ba306b8a8faab5b728a2983 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 9 Mar 2017 08:56:01 +1100 Subject: [PATCH 09/17] Adjust up vector of kart slowly so that the kart is upright (even when it was not upright at the start of the cannon). --- src/karts/cannon_animation.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index fafaa65de..05b5bd95d 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -157,14 +157,27 @@ void CannonAnimation::update(float dt) // Get the current kart orientation Vec3 forward = m_kart->getTrans().getBasis().getColumn(2); forward.normalize(); - - // Only adjust the heading. I tried to also adjust pitch, - // but that adds a strong roll to the kart on some cannons + + // Only adjust the heading. + // ------------------------ + // I tried to also adjust pitch at the same time, but that adds a strong + // roll to the kart on some cannons Vec3 v1(tangent), v2(forward); v1.setY(0); v2.setY(0); btQuaternion q = m_kart->getRotation()*shortestArcQuatNormalize2(v2, v1); - m_kart->setRotation(q); + // While start and end line have to have the same 'up' vector, karts can + // sometimes be not parallel to them. So slowly adjust this over time + Vec3 up = m_kart->getTrans().getBasis().getColumn(1); + up.normalize(); + Vec3 gravity = -m_kart->getBody()->getGravity(); + gravity.normalize(); + // Adjust only 5% towards the real up vector. This will smoothly + // adjust the kart. + Vec3 target_up_vector = (gravity*0.05f + up*0.95f).normalize(); + btQuaternion q_up = shortestArcQuat(up, target_up_vector); + + m_kart->setRotation(q_up * q); // Then compute the new location of the kart // ----------------------------------------- From 5640436aa893741fbe004c95630d9504fa923ea8 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 10 Mar 2017 08:38:03 +1100 Subject: [PATCH 10/17] Support cannons in reverse tracks. --- src/tracks/check_cannon.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/tracks/check_cannon.cpp b/src/tracks/check_cannon.cpp index 4b4317f7f..cad7e9820 100644 --- a/src/tracks/check_cannon.cpp +++ b/src/tracks/check_cannon.cpp @@ -37,8 +37,17 @@ CheckCannon::CheckCannon(const XMLNode &node, unsigned int index) : CheckLine(node, index) { - if( !node.get("target-p1", &m_target_left ) || - !node.get("target-p2", &m_target_right) ) + std::string p1("target-p1"); + std::string p2("target-p2"); + + if (race_manager->getReverseTrack()) + { + p1 = "p1"; + p2 = "p2"; + } + + if( !node.get(p1, &m_target_left ) || + !node.get(p2, &m_target_right) ) Log::fatal("CheckCannon", "No target line specified."); m_curve = new Ipo(*(node.getNode("curve")), From aac92f6111ad4d9770ba2687b8000a46c66d7b6f Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 10 Mar 2017 08:44:51 +1100 Subject: [PATCH 11/17] Code simplification. --- src/karts/cannon_animation.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index 05b5bd95d..75c877039 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -123,12 +123,7 @@ CannonAnimation::~CannonAnimation() { delete m_curve; - btTransform pos; - pos.setOrigin(m_kart->getXYZ()); - //pos.setRotation(btQuaternion(btVector3(0.0f, 1.0f, 0.0f), - // m_kart->getHeading() )); - pos.setRotation(m_kart->getRotation()); - + btTransform pos = m_kart->getTrans(); m_kart->getBody()->setCenterOfMassTransform(pos); Vec3 v(0, 0, m_kart->getKartProperties()->getEngineMaxSpeed()); m_kart->setVelocity(pos.getBasis()*v); From 0bd459c2aa1e9419008c22ebae58b473422d1062 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 10 Mar 2017 17:11:54 +1100 Subject: [PATCH 12/17] Fix the transition from physics orientation to cannon (i.e. no more yerk when the kart is not parallel to the tangent of the curve at start). --- src/karts/cannon_animation.cpp | 62 ++++++++++++++++++++++++++++------ src/karts/cannon_animation.hpp | 3 ++ 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index 75c877039..4fc3e62b6 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -111,7 +111,27 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, // Subtract the horizontal difference, to get the constant offset the // kart has from the curve. m_delta = m_delta - delta; - + + // Compute the original heading of the kart. At the end of the cannon, + // the kart should be parallel to the curve, but at the beginning it + // the kart should be parallel to the curve and facing forwards, but + // at the beginning it might not be. The initial rotation between the + // tangent of the curce and the kart is stored as a m_delta_heading, + // which will be applied to the curve orientation in update, but reduced + // over time till it becomes 0 at the end of the curve. The effect is that + // initially (t=0) the kart will keep its (non-orhtogonal) rotation, + // but smoothly this will adjusted until at the end the kart will be + // facing forwards again. + Vec3 tangent; + m_curve->getDerivativeAt(0, &tangent); + // Get the current kart orientation + Vec3 forward = m_kart->getTrans().getBasis().getColumn(2); + forward.normalize(); + Vec3 v1(tangent), v2(forward); + v1.setY(0); v2.setY(0); + m_delta_heading = shortestArcQuatNormalize2(v1, v2); + + // The previous call to m_curve->update will set the internal timer // of the curve to dt. Reset it to 0 to make sure the timer is in // synch with the timer of the CanonAnimation @@ -143,7 +163,7 @@ void CannonAnimation::update(float dt) } // First compute the current rotation - // ----------------------------------- + // ================================== // Get the tangent = derivative at the current point to compute the // new orientation of the kart Vec3 tangent; @@ -153,14 +173,18 @@ void CannonAnimation::update(float dt) Vec3 forward = m_kart->getTrans().getBasis().getColumn(2); forward.normalize(); - // Only adjust the heading. - // ------------------------ + // Heading + // ------- // I tried to also adjust pitch at the same time, but that adds a strong - // roll to the kart on some cannons + // roll to the kart on some cannons while it is in the air (caused by + // the rotation axis returned shortestArc not being orthogonal to the + // up vector). Vec3 v1(tangent), v2(forward); v1.setY(0); v2.setY(0); - btQuaternion q = m_kart->getRotation()*shortestArcQuatNormalize2(v2, v1); + btQuaternion heading = shortestArcQuatNormalize2(v2, v1); + // Align to up-vector + // ------------------ // While start and end line have to have the same 'up' vector, karts can // sometimes be not parallel to them. So slowly adjust this over time Vec3 up = m_kart->getTrans().getBasis().getColumn(1); @@ -168,16 +192,32 @@ void CannonAnimation::update(float dt) Vec3 gravity = -m_kart->getBody()->getGravity(); gravity.normalize(); // Adjust only 5% towards the real up vector. This will smoothly - // adjust the kart. + // adjust the kart while the kart is in the air Vec3 target_up_vector = (gravity*0.05f + up*0.95f).normalize(); btQuaternion q_up = shortestArcQuat(up, target_up_vector); - m_kart->setRotation(q_up * q); + // Additional kart rotation + // ------------------------ + // Apply any additional rotation the kart had when crossing the start + // line. This rotation will be reduced the closer the kart gets to + // the end line, with the result that at the start line the kart will + // be not rotated at all (so the visuals from physics to cannon will + // be smoothed), and at the end line the kart will face in the + // forward direction. + + // The timer counts backwards, so the fraction goes from 1 to 0 + float f = m_timer / m_curve->getAnimationDuration(); + + btQuaternion zero(gravity, 0); + btQuaternion current_delta_heading = zero.slerp(m_delta_heading, f); + + btQuaternion all_heading = m_kart->getRotation()*current_delta_heading*heading; + + m_kart->setRotation(q_up * all_heading); + // Then compute the new location of the kart // ----------------------------------------- - // The timer counts backwards, so the fraction goes from 1 to 0 - float f = m_timer / m_curve->getAnimationDuration(); float f_current_width = m_start_line_length * f + m_end_line_length * (1.0f - f); @@ -189,7 +229,7 @@ void CannonAnimation::update(float dt) // transform it to the global coordinate system Vec3 horiz_delta = Vec3(0.5f*m_fraction_of_line * f_current_width, 0, 0); - Vec3 rotated_delta = quatRotate(q, m_delta + horiz_delta); + Vec3 rotated_delta = quatRotate(all_heading, m_delta + horiz_delta); Vec3 curve_xyz; m_curve->update(dt, &curve_xyz); diff --git a/src/karts/cannon_animation.hpp b/src/karts/cannon_animation.hpp index 51ded2bd8..aba4e8653 100644 --- a/src/karts/cannon_animation.hpp +++ b/src/karts/cannon_animation.hpp @@ -58,6 +58,9 @@ protected: * at the current location. */ float m_fraction_of_line; + /** The initial heading of the kart when crossing the line. This is + * used to smoothly orient the kart towards the normal of the cuve. */ + btQuaternion m_delta_heading; public: CannonAnimation(AbstractKart *kart, Ipo *ipo, const Vec3 &start_left, const Vec3 &start_right, From d33d72dfb83fc8b5dda813c14cef3b40bc825190 Mon Sep 17 00:00:00 2001 From: rogue-spectre Date: Sat, 11 Mar 2017 15:04:38 +0100 Subject: [PATCH 13/17] fix feedback offroad #2761 (#2806) * Fix updateenginesfx smooth engine sound and fix https://github.com/supertuxkart/stk-code/issues/2761 * update enginesfx * remove m_last_max_speed useless * forgot : float max_speed * Add missing space around = * add space around = m_last_factor_engine_sound --- src/karts/kart.cpp | 38 +++++++++++++++++++++++++------------- src/karts/kart.hpp | 5 ++++- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index dc5ae15c7..66fbf1980 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -153,6 +153,7 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id, m_reset_transform = init_transform; m_speed = 0.0f; m_smoothed_speed = 0.0f; + m_last_factor_engine_sound = 0.0f; m_kart_model->setKart(this); @@ -1375,6 +1376,7 @@ void Kart::update(float dt) fabs(getSpeed()) < 3.0f) { new RescueAnimation(this, /*is_auto_rescue*/true); + m_last_factor_engine_sound = 0.0f; } } @@ -1439,7 +1441,10 @@ void Kart::update(float dt) if((min->getY() - getXYZ().getY() > 17 || dist_to_sector > 25) && !m_flying && !getKartAnimation()) + { new RescueAnimation(this); + m_last_factor_engine_sound = 0.0f; + } } else { @@ -1458,7 +1463,10 @@ void Kart::update(float dt) } // if !flying handleMaterialSFX(material); if (material->isDriveReset() && isOnGround()) + { new RescueAnimation(this); + m_last_factor_engine_sound = 0.0f; + } else if(material->isZipper() && isOnGround()) { handleZipper(material); @@ -2094,6 +2102,7 @@ void Kart::crashed(const Material *m, const Vec3 &normal) if (m->getCollisionReaction() == Material::RESCUE) { new RescueAnimation(this); + m_last_factor_engine_sound = 0.0f; } else if (m->getCollisionReaction() == Material::PUSH_BACK) { @@ -2285,7 +2294,7 @@ void Kart::updatePhysics(float dt) m_max_speed->update(dt); - updateEngineSFX(); + updateEngineSFX(dt); #ifdef XX Log::info("Kart","angVel %f %f %f heading %f suspension %f %f %f %f" ,m_body->getAngularVelocity().getX() @@ -2304,7 +2313,7 @@ void Kart::updatePhysics(float dt) //----------------------------------------------------------------------------- /** Adjust the engine sound effect depending on the speed of the kart. */ -void Kart::updateEngineSFX() +void Kart::updateEngineSFX(float dt) { // when going faster, use higher pitch for engine if(!m_engine_sound || !SFXManager::get()->sfxAllowed()) @@ -2312,7 +2321,7 @@ void Kart::updateEngineSFX() if(isOnGround()) { - float max_speed = m_max_speed->getCurrentMaxSpeed(); + float max_speed = m_kart_properties->getEngineMaxSpeed(); // Engine noise is based half in total speed, half in fake gears: // With a sawtooth graph like /|/|/| we get 3 even spaced gears, @@ -2320,20 +2329,23 @@ void Kart::updateEngineSFX() // good enough brrrBRRRbrrrBRRR sound effect. Speed factor makes // it a "staired sawtooth", so more acoustically rich. float f = max_speed > 0 ? m_speed/max_speed : 1.0f; - // Speed at this stage is not yet capped, so it can be > 1, which - // results in odd engine sfx. - if (f>1.0f) f=1.0f; + // Speed at this stage is not yet capped, reduce the amount beyond 1 + if (f> 1.0f) f = 1.0f + (1.0f-1.0f/f); - float gears = 3.0f * fmod(f, 0.333334f); + float fc = f; + if (fc>1.0f) fc = 1.0f; + float gears = 3.0f * fmod(fc, 0.333334f); assert(!std::isnan(f)); - m_engine_sound->setSpeedPosition(0.6f + (f + gears) * 0.35f, getXYZ()); + m_last_factor_engine_sound = (0.9*f + gears) * 0.35f; + m_engine_sound->setSpeedPosition(0.6f + m_last_factor_engine_sound, getXYZ()); } else - { - // When flying, fixed value but not too high pitch - // This gives some variation (vs previous "on wheels" one) - m_engine_sound->setSpeedPosition(0.9f, getXYZ()); - } + { + // When flying, reduce progressively the sound engine (since we can't accelerate) + m_last_factor_engine_sound *= (1.0f-0.1*dt); + m_engine_sound->setSpeedPosition(0.6f + m_last_factor_engine_sound, getXYZ()); + if (m_speed < 0.1f) m_last_factor_engine_sound = 0.0f; + } } // updateEngineSFX //----------------------------------------------------------------------------- diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index aa3a7a87f..63c1bf6ed 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -201,6 +201,9 @@ protected: /** For camera handling an exponentially smoothened value is used, which * reduces stuttering of the camera. */ float m_smoothed_speed; + + /** For smoothing engine sound**/ + float m_last_factor_engine_sound; std::vector m_custom_sounds; SFXBase *m_beep_sound; @@ -226,7 +229,7 @@ protected: void updateFlying(); void updateSliding(); void updateEnginePowerAndBrakes(float dt); - void updateEngineSFX(); + void updateEngineSFX(float dt); void updateSpeed(); void updateNitro(float dt); float getActualWheelForce(); From b8e3de0161de0c370a95bf2b75bec97a7a13e930 Mon Sep 17 00:00:00 2001 From: Deve Date: Sun, 12 Mar 2017 11:34:43 +0100 Subject: [PATCH 14/17] Allow to use debug menu on android --- src/utils/debug.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/utils/debug.cpp b/src/utils/debug.cpp index 714a21892..2f39ccf5d 100644 --- a/src/utils/debug.cpp +++ b/src/utils/debug.cpp @@ -723,11 +723,15 @@ bool onEvent(const SEvent &event) if(!UserConfigParams::m_artist_debug_mode) return true; // keep handling the events - if(event.EventType == EET_MOUSE_INPUT_EVENT) + if (event.EventType == EET_MOUSE_INPUT_EVENT) { // Create the menu (only one menu at a time) - if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN && - !g_debug_menu_visible) + #ifdef ANDROID + if (event.MouseInput.X < 30 && event.MouseInput.Y < 30 && + #else + if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN && + #endif + !g_debug_menu_visible) { irr_driver->getDevice()->getCursorControl()->setVisible(true); From 97670ed23cdf097cfd955f63927f033caef5d444 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Mar 2017 21:35:48 +0100 Subject: [PATCH 15/17] Improved wind effect for vegetation --- data/shaders/grass_pass.vert | 7 +++++-- data/shaders/instanced_grass.vert | 6 ++++-- data/shaders/instanced_grassshadow.vert | 7 +++++-- data/shaders/shadow_grass.vert | 8 ++++++-- src/graphics/stk_mesh.cpp | 2 +- 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/data/shaders/grass_pass.vert b/data/shaders/grass_pass.vert index 23daa553c..e6903c19e 100644 --- a/data/shaders/grass_pass.vert +++ b/data/shaders/grass_pass.vert @@ -20,13 +20,16 @@ out vec2 uv; void main() { + vec3 test = sin(windDir * (Position.y* 0.5)) * 0.5; + test += cos(windDir) * 0.7; + mat4 new_model_matrix = ModelMatrix; mat4 new_inverse_model_matrix = InverseModelMatrix; - new_model_matrix[3].xyz += windDir * Color.r; + new_model_matrix[3].xyz += test * Color.r; // FIXME doesn't seem to make too much difference in pass 2, because this // affects "nor" which is later only * 0.1 by scattering - new_inverse_model_matrix[3].xyz -= windDir * Color.r; + new_inverse_model_matrix[3].xyz -= test * Color.r; mat4 ModelViewProjectionMatrix = ProjectionViewMatrix * new_model_matrix; mat4 TransposeInverseModelView = transpose(InverseViewMatrix * new_inverse_model_matrix); diff --git a/data/shaders/instanced_grass.vert b/data/shaders/instanced_grass.vert index 57adee847..0127a8346 100644 --- a/data/shaders/instanced_grass.vert +++ b/data/shaders/instanced_grass.vert @@ -41,8 +41,10 @@ flat out sampler2D thirdhandle; void main() { - mat4 ModelMatrix = getWorldMatrix(Origin + windDir * Color.r, Orientation, Scale); - mat4 TransposeInverseModelView = transpose(getInverseWorldMatrix(Origin + windDir * Color.r, Orientation, Scale) * InverseViewMatrix); + vec3 test = sin(windDir * (Position.y* 0.5)) * 0.5; + test += cos(windDir) * 0.7; + mat4 ModelMatrix = getWorldMatrix(Origin + test * Color.r, Orientation, Scale); + mat4 TransposeInverseModelView = transpose(getInverseWorldMatrix(Origin + test * Color.r, Orientation, Scale) * InverseViewMatrix); gl_Position = ProjectionViewMatrix * ModelMatrix * vec4(Position, 1.); nor = (TransposeInverseModelView * vec4(Normal, 0.)).xyz; uv = Texcoord; diff --git a/data/shaders/instanced_grassshadow.vert b/data/shaders/instanced_grassshadow.vert index 5d5e8e6e2..a31d49d92 100644 --- a/data/shaders/instanced_grassshadow.vert +++ b/data/shaders/instanced_grassshadow.vert @@ -40,17 +40,20 @@ flat out uvec2 hdle; void main(void) { + + vec3 test = sin(windDir * (Position.y* 0.5)) * 0.5; + test += cos(windDir) * 0.7; mat4 ModelMatrix = getWorldMatrix(Origin, Orientation, Scale); #ifdef VSLayer gl_Layer = layer; - gl_Position = ShadowViewProjMatrixes[gl_Layer] * ModelMatrix * vec4(Position + windDir * Color.r, 1.); + gl_Position = ShadowViewProjMatrixes[gl_Layer] * ModelMatrix * vec4(Position + test * Color.r, 1.); uv = Texcoord; #ifdef Use_Bindless_Texture handle = Handle; #endif #else layerId = layer; - gl_Position = ShadowViewProjMatrixes[layerId] * ModelMatrix * vec4(Position + windDir * Color.r, 1.); + gl_Position = ShadowViewProjMatrixes[layerId] * ModelMatrix * vec4(Position + test * Color.r, 1.); tc = Texcoord; #ifdef Use_Bindless_Texture hdle = Handle; diff --git a/data/shaders/shadow_grass.vert b/data/shaders/shadow_grass.vert index 95827b872..bb978f921 100644 --- a/data/shaders/shadow_grass.vert +++ b/data/shaders/shadow_grass.vert @@ -21,13 +21,17 @@ out int layerId; void main(void) { + + vec3 test = sin(windDir * (Position.y* 0.5)) * 0.5; + test += cos(windDir) * 0.7; + #ifdef VSLayer gl_Layer = layer; uv = Texcoord; - gl_Position = ShadowViewProjMatrixes[gl_Layer] * ModelMatrix * vec4(Position + windDir * Color.r, 1.); + gl_Position = ShadowViewProjMatrixes[gl_Layer] * ModelMatrix * vec4(Position + test * Color.r, 1.); #else layerId = layer; tc = Texcoord; - gl_Position = ShadowViewProjMatrixes[layerId] * ModelMatrix * vec4(Position + windDir * Color.r, 1.); + gl_Position = ShadowViewProjMatrixes[layerId] * ModelMatrix * vec4(Position + test * Color.r, 1.); #endif } diff --git a/src/graphics/stk_mesh.cpp b/src/graphics/stk_mesh.cpp index 397dd8b77..87e2e2b29 100644 --- a/src/graphics/stk_mesh.cpp +++ b/src/graphics/stk_mesh.cpp @@ -337,7 +337,7 @@ core::vector3df getWindDir() const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; GrassShaderProvider *gsp = (GrassShaderProvider *)Shaders::getCallback(ES_GRASS); - return (gsp->getSpeed() * cos(time)) * vector3df(1., 0., 0.); + return (gsp->getSpeed() * time * vector3df(1., 0., 0.)); } // getWindDir // ---------------------------------------------------------------------------- From 6ea06e05de4a562b618856a6463d5ae1da79533f Mon Sep 17 00:00:00 2001 From: Deve Date: Mon, 13 Mar 2017 01:17:31 +0100 Subject: [PATCH 16/17] Use kart lights only in shader-based pipeline. It causes a huge slowdown in legacy pipeline, even if the radius is set to 0. --- src/graphics/particle_emitter.hpp | 1 + src/karts/kart_gfx.cpp | 197 +++++++++++++++--------------- src/karts/kart_gfx.hpp | 3 + 3 files changed, 105 insertions(+), 96 deletions(-) diff --git a/src/graphics/particle_emitter.hpp b/src/graphics/particle_emitter.hpp index 576d4a3d2..a5395467d 100644 --- a/src/graphics/particle_emitter.hpp +++ b/src/graphics/particle_emitter.hpp @@ -92,6 +92,7 @@ public: void setCreationRateAbsolute(float fraction); void setCreationRateRelative(float f); int getCreationRate(); + float getCreationRateFloat() {return m_min_rate;} void setPosition(const Vec3 &pos); void setRotation(const Vec3 &rot); diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index 8ab878025..5d44de431 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -38,6 +38,14 @@ KartGFX::KartGFX(const AbstractKart *kart, RaceManager::KartType type, bool is_day) { + m_nitro_light = NULL; + m_skidding_light_1 = NULL; + m_skidding_light_2 = NULL; + m_head_light = NULL; + m_kart = kart; + m_wheel_toggle = 0; + m_skid_level = 0; + //if(!UserConfigParams::m_graphical_effects) //{ // for(unsigned int i=0; igetKartModel(); const float length = km->getLength(); scene::ISceneNode *node = m_kart->getNode(); // Create nitro light core::vector3df location(0.0f, 0.5f, -0.5f*length - 0.05f); -#ifndef SERVER_ONLY - m_nitro_light = irr_driver->addLight(location, /*force*/ 0.4f, - /*radius*/CVS->isGLSL() ? 5.0f : 1.0f, - 0.0f, 0.4f, 1.0f, - false, node); - m_nitro_light->setVisible(false); -#ifdef DEBUG - m_nitro_light->setName( ("nitro emitter (" + m_kart->getIdent() - + ")").c_str() ); -#endif - - // Create skidding lights - // For the first skidding level - m_skidding_light_1 = - irr_driver->addLight(core::vector3df(0.0f, 0.1f, -0.5f*length - 0.05f), - /* force */ 0.3f, - /*radius*/CVS->isGLSL() ? 3.0f : 1.0f, - 1.0f, 0.6f, 0.0f, false, node); - m_skidding_light_1->setVisible(false); - m_skidding_light_1->setName( ("skidding emitter 1 (" + m_kart->getIdent() - + ")").c_str() ); - - // For the second skidding level - m_skidding_light_2 = - irr_driver->addLight(core::vector3df(0.0f, 0.1f, -0.5f*length - 0.05f), - /* force */0.4f, - /*radius*/CVS->isGLSL() ? 4.0f : 1.0f, - 1.0f, 0.0f, 0.0f, false, node); - m_skidding_light_2->setVisible(false); - m_skidding_light_2->setName( ("skidding emitter 2 (" + m_kart->getIdent() - + ")").c_str() ); - - m_head_light = - irr_driver->addLight(core::vector3df(0.0f, 0.2f, 1.5f*length), - /* force */ 0.5f, - /*radius*/CVS->isGLSL() ? 5.0f : 1.0f, - 1.0f, 1.0f, 1.0f, false, node); - m_head_light->setName( ("head light " + m_kart->getIdent() - + ")").c_str() ); - - if (type == RaceManager::KT_PLAYER && !is_day) - { - m_head_light->setVisible(true); - } - else - { - m_head_light->setVisible(false); - } - - -#endif - #ifndef SERVER_ONLY if (CVS->isGLSL()) { + m_nitro_light = irr_driver->addLight(location, /*force*/ 0.4f, + /*radius*/ 5.0f, 0.0f, 0.4f, 1.0f, + false, node); + m_nitro_light->setVisible(false); + #ifdef DEBUG + m_nitro_light->setName( ("nitro emitter (" + m_kart->getIdent() + + ")").c_str() ); + #endif + + // Create skidding lights + // For the first skidding level + m_skidding_light_1 = + irr_driver->addLight(core::vector3df(0.0f, 0.1f, -0.5f * length - + 0.05f), /* force */ 0.3f, /*radius*/ 3.0f, + 1.0f, 0.6f, 0.0f, false, node); + m_skidding_light_1->setVisible(false); + m_skidding_light_1->setName(("skidding emitter 1 (" + m_kart->getIdent() + + ")").c_str() ); + + // For the second skidding level + m_skidding_light_2 = + irr_driver->addLight(core::vector3df(0.0f, 0.1f, -0.5f * length - + 0.05f), /* force */0.4f, /*radius*/4.0f, + 1.0f, 0.0f, 0.0f, false, node); + m_skidding_light_2->setVisible(false); + m_skidding_light_2->setName(("skidding emitter 2 (" + m_kart->getIdent() + + ")").c_str() ); + + m_head_light = + irr_driver->addLight(core::vector3df(0.0f, 0.2f, 1.5f * length), + /* force */ 0.5f, /*radius*/5.0f, 1.0f, 1.0f, + 1.0f, false, node); + m_head_light->setName( ("head light " + m_kart->getIdent() + + ")").c_str() ); + + m_head_light->setVisible(type == RaceManager::KT_PLAYER && !is_day); + m_nitro_light->grab(); m_skidding_light_1->grab(); m_skidding_light_2->grab(); @@ -182,7 +173,7 @@ void KartGFX::addEffect(KartGFXType type, const std::string &file_name, const Vec3 &position, bool important) { #ifndef SERVER_ONLY - if (!UserConfigParams::m_graphical_effects && + if ((!UserConfigParams::m_graphical_effects || !CVS->isGLSL()) && (!important || m_kart->getType() == RaceManager::KT_AI || m_kart->getType() == RaceManager::KT_SPARE_TIRE)) { @@ -257,6 +248,7 @@ void KartGFX::setSkidLevel(const unsigned int level) { assert(level >= 1); assert(level <= 2); + m_skid_level = level; const ParticleKind *pk = level==1 ? m_skid_kind1 : m_skid_kind2; #ifndef SERVER_ONLY if(m_all_emitters[KGFX_SKID1L]) @@ -309,8 +301,13 @@ void KartGFX::setXYZ(const KartGFXType type, const Vec3 &xyz) void KartGFX::setCreationRateAbsolute(KartGFXType type, float f) { #ifndef SERVER_ONLY - if(m_all_emitters[type]) - m_all_emitters[type]->setCreationRateAbsolute(f); + if (!m_all_emitters[type]) + return; + + if (m_all_emitters[type]->getCreationRateFloat() == f) + return; + + m_all_emitters[type]->setCreationRateAbsolute(f); #endif } // setCreationRateAbsolute @@ -428,7 +425,9 @@ void KartGFX::updateNitroGraphics(float nitro_frac) setCreationRateRelative(KartGFX::KGFX_NITRO2, nitro_frac); setCreationRateRelative(KartGFX::KGFX_NITROSMOKE1, nitro_frac); setCreationRateRelative(KartGFX::KGFX_NITROSMOKE2, nitro_frac); - m_nitro_light->setVisible(true); + + if (CVS->isGLSL()) + m_nitro_light->setVisible(true); } else { @@ -436,15 +435,14 @@ void KartGFX::updateNitroGraphics(float nitro_frac) setCreationRateAbsolute(KartGFX::KGFX_NITRO2, 0); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE1, 0); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE2, 0); - m_nitro_light->setVisible(false); + + if (CVS->isGLSL()) + m_nitro_light->setVisible(false); } - if (CVS->isGLSL()) - { - // Exhaust is always emitting - setCreationRateRelative(KartGFX::KGFX_EXHAUST1, 1.0); - setCreationRateRelative(KartGFX::KGFX_EXHAUST2, 1.0); - } + // Exhaust is always emitting + setCreationRateRelative(KartGFX::KGFX_EXHAUST1, 1.0); + setCreationRateRelative(KartGFX::KGFX_EXHAUST2, 1.0); #endif } // updateGraphics @@ -456,8 +454,11 @@ void KartGFX::updateNitroGraphics(float nitro_frac) void KartGFX::updateSkidLight(unsigned int level) { #ifndef SERVER_ONLY - m_skidding_light_1->setVisible(level == 1); - m_skidding_light_2->setVisible(level > 1); + if (CVS->isGLSL()) + { + m_skidding_light_1->setVisible(level == 1); + m_skidding_light_2->setVisible(level > 1); + } #endif } // updateSkidLight @@ -484,7 +485,7 @@ void KartGFX::getGFXStatus(int* nitro, bool* zipper, if (m_all_emitters[KGFX_SKIDL]) { s = m_all_emitters[KGFX_SKIDL]->getCreationRate(); - r = m_skidding_light_2->isVisible(); + r = m_skid_level == 2; } *nitro = n; @@ -505,15 +506,19 @@ void KartGFX::setGFXFromReplay(int nitro, bool zipper, setCreationRateAbsolute(KartGFX::KGFX_NITRO2, (float)nitro); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE1, (float)nitro); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE2, (float)nitro); - m_nitro_light->setVisible(true); + + if (CVS->isGLSL()) + m_nitro_light->setVisible(true); } - else if (m_nitro_light->isVisible() && nitro == 0) + else { setCreationRateAbsolute(KartGFX::KGFX_NITRO1, 0.0f); setCreationRateAbsolute(KartGFX::KGFX_NITRO2, 0.0f); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE1, 0.0f); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE2, 0.0f); - m_nitro_light->setVisible(false); + + if (CVS->isGLSL()) + m_nitro_light->setVisible(false); } if (zipper) @@ -521,36 +526,33 @@ void KartGFX::setGFXFromReplay(int nitro, bool zipper, if (skidding > 0) { - if (!m_skidding_light_1->isVisible() && !red_skidding) - { - if (m_all_emitters[KGFX_SKID1L]) - m_all_emitters[KGFX_SKID1L]->setParticleType(m_skid_kind1); - if (m_all_emitters[KGFX_SKID1R]) - m_all_emitters[KGFX_SKID1R]->setParticleType(m_skid_kind1); + const ParticleKind* skid_kind = red_skidding ? m_skid_kind2 + : m_skid_kind1; - m_skidding_light_1->setVisible(true); - m_skidding_light_2->setVisible(false); - } - if (!m_skidding_light_2->isVisible() && red_skidding) - { - if (m_all_emitters[KGFX_SKID1L]) - m_all_emitters[KGFX_SKID1L]->setParticleType(m_skid_kind2); - if (m_all_emitters[KGFX_SKID1R]) - m_all_emitters[KGFX_SKID1R]->setParticleType(m_skid_kind2); + if (m_all_emitters[KGFX_SKID1L]) + m_all_emitters[KGFX_SKID1L]->setParticleType(skid_kind); + if (m_all_emitters[KGFX_SKID1R]) + m_all_emitters[KGFX_SKID1R]->setParticleType(skid_kind); - m_skidding_light_1->setVisible(false); - m_skidding_light_2->setVisible(true); + if (CVS->isGLSL()) + { + m_skidding_light_1->setVisible(!red_skidding); + m_skidding_light_2->setVisible(red_skidding); } + setCreationRateAbsolute(KartGFX::KGFX_SKIDL, (float)skidding); setCreationRateAbsolute(KartGFX::KGFX_SKIDR, (float)skidding); } - else if ((m_skidding_light_1->isVisible() || - m_skidding_light_2->isVisible()) && skidding == 0) + else { setCreationRateAbsolute(KartGFX::KGFX_SKIDL, 0.0f); setCreationRateAbsolute(KartGFX::KGFX_SKIDR, 0.0f); - m_skidding_light_1->setVisible(false); - m_skidding_light_2->setVisible(false); + + if (CVS->isGLSL()) + { + m_skidding_light_1->setVisible(false); + m_skidding_light_2->setVisible(false); + } } #endif } // setGFXFromReplay @@ -559,9 +561,12 @@ void KartGFX::setGFXFromReplay(int nitro, bool zipper, void KartGFX::setGFXInvisible() { #ifndef SERVER_ONLY - m_nitro_light->setVisible(false); - m_skidding_light_1->setVisible(false); - m_skidding_light_2->setVisible(false); - m_head_light->setVisible(false); + if (CVS->isGLSL()) + { + m_nitro_light->setVisible(false); + m_skidding_light_1->setVisible(false); + m_skidding_light_2->setVisible(false); + m_head_light->setVisible(false); + } #endif } // setGFXInvisible diff --git a/src/karts/kart_gfx.hpp b/src/karts/kart_gfx.hpp index 68f92d336..d7c2929a6 100644 --- a/src/karts/kart_gfx.hpp +++ b/src/karts/kart_gfx.hpp @@ -77,6 +77,9 @@ private: /** Used to alternate particle effects from the rear wheels. */ int m_wheel_toggle; + + /** A skid level that is currently in use */ + int m_skid_level; /** A light that's shown when the kart uses nitro. */ irr::scene::ISceneNode* m_nitro_light; From e5eb40b773e8bd6954f9871715017434e2872e6f Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 14 Mar 2017 12:51:10 +1100 Subject: [PATCH 17/17] Smooth skidding rotation when entering a cannon. --- src/karts/cannon_animation.cpp | 20 ++++++++++++++++---- src/karts/cannon_animation.hpp | 3 ++- src/tracks/check_cannon.cpp | 7 ++++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index 4fc3e62b6..6fe26ead9 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -27,9 +27,22 @@ #include "LinearMath/btTransform.h" +/** The constructor for the cannon animation. + * \param kart The kart to be animated. + * \param ipo The IPO (blender interpolation curve) which the kart + * should follow. + * \param start_left, start_right: Left and right end points of the line + * that the kart just crossed. + * \param end_left, end_right: Left and right end points of the line at + * which the kart finishes. + * \param skid_rot Visual rotation of the kart due to skidding (while this + * value can be queried, the AbstractkartAnimation constructor + * resets the value to 0, so it needs to be passed in. + */ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, const Vec3 &start_left, const Vec3 &start_right, - const Vec3 &end_left, const Vec3 &end_right ) + const Vec3 &end_left, const Vec3 &end_right, + float skid_rot) : AbstractKartAnimation(kart, "CannonAnimation") { m_curve = new AnimationBase(ipo); @@ -126,10 +139,10 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, m_curve->getDerivativeAt(0, &tangent); // Get the current kart orientation Vec3 forward = m_kart->getTrans().getBasis().getColumn(2); - forward.normalize(); Vec3 v1(tangent), v2(forward); v1.setY(0); v2.setY(0); - m_delta_heading = shortestArcQuatNormalize2(v1, v2); + m_delta_heading = shortestArcQuatNormalize2(v1, v2) + * btQuaternion(Vec3(0,1,0), skid_rot); // The previous call to m_curve->update will set the internal timer @@ -171,7 +184,6 @@ void CannonAnimation::update(float dt) &tangent); // Get the current kart orientation Vec3 forward = m_kart->getTrans().getBasis().getColumn(2); - forward.normalize(); // Heading // ------- diff --git a/src/karts/cannon_animation.hpp b/src/karts/cannon_animation.hpp index aba4e8653..f36098d32 100644 --- a/src/karts/cannon_animation.hpp +++ b/src/karts/cannon_animation.hpp @@ -64,7 +64,8 @@ protected: public: CannonAnimation(AbstractKart *kart, Ipo *ipo, const Vec3 &start_left, const Vec3 &start_right, - const Vec3 &end_left, const Vec3 &end_right); + const Vec3 &end_left, const Vec3 &end_right, + float skid_rot); virtual ~CannonAnimation(); virtual void update(float dt); diff --git a/src/tracks/check_cannon.cpp b/src/tracks/check_cannon.cpp index cad7e9820..9daa715b9 100644 --- a/src/tracks/check_cannon.cpp +++ b/src/tracks/check_cannon.cpp @@ -27,6 +27,7 @@ #include "io/xml_node.hpp" #include "karts/abstract_kart.hpp" #include "karts/cannon_animation.hpp" +#include "karts/skidding.hpp" #include "modes/world.hpp" @@ -142,6 +143,10 @@ void CheckCannon::trigger(unsigned int kart_index) AbstractKart *kart = World::getWorld()->getKart(kart_index); if(kart->getKartAnimation()) return; + // The constructor AbstractKartAnimation resets the skidding to 0. So in + // order to smooth rotate the kart, we need to keep the current visual + // rotation and pass it to the CannonAnimation. + float skid_rot = kart->getSkidding()->getVisualSkidRotation(); new CannonAnimation(kart, m_curve->clone(), getLeftPoint(), getRightPoint(), - m_target_left, m_target_right); + m_target_left, m_target_right, skid_rot); } // CheckCannon