From 317ff370cbea430434c0956bed309ca12b64b883 Mon Sep 17 00:00:00 2001 From: hikerstk Date: Wed, 4 Apr 2012 00:48:05 +0000 Subject: [PATCH] 1) Control the time for an IPO animation in AnimationBase instead of individually on each IPO (besides removing redundancy it keeps the IPOs in synch, since it is now guaranteed that they cycle all at the same time). 2) Started to add length computations for IPOs, but this code is not yet used. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@11082 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/animations/animation_base.cpp | 85 ++++++++++++++----- src/animations/animation_base.hpp | 17 ++-- src/animations/ipo.cpp | 130 +++++++++++++++++++----------- src/animations/ipo.hpp | 44 +++++----- src/karts/cannon_animation.cpp | 31 +------ src/karts/cannon_animation.hpp | 23 ++---- src/tracks/check_cannon.cpp | 27 ++++++- src/tracks/check_cannon.hpp | 24 +++++- 8 files changed, 235 insertions(+), 146 deletions(-) diff --git a/src/animations/animation_base.cpp b/src/animations/animation_base.cpp index 2cadf604d..6d995592e 100644 --- a/src/animations/animation_base.cpp +++ b/src/animations/animation_base.cpp @@ -36,32 +36,34 @@ AnimationBase::AnimationBase(const XMLNode &node) Ipo *ipo = new Ipo(*node.getNode(i), fps); m_all_ipos.push_back(ipo); } + if(m_all_ipos.size()==0) + { + printf("Warning: empty animation curve.\n"); + exit(-1); + } - - // extend all IPOs to add at the same point - float last_x = -1; + // Determine start and end X values for this curve. + m_start_time = m_all_ipos[0].getPoints()[0].X; + m_end_time = m_start_time; Ipo* curr; for_in (curr, m_all_ipos) { const std::vector& points = curr->getPoints(); - last_x = std::max(last_x, points[points.size() - 1].X); + m_start_time = std::min(m_start_time, points[0].X ); + m_end_time = std::max(m_end_time, points[points.size() - 1].X); } - if (last_x > -1) + // Make sure all individual IPOs start and end at the same X value. + for_in (curr, m_all_ipos) { - for_in (curr, m_all_ipos) - { - const std::vector& points = curr->getPoints(); - if (points[points.size() - 1].X < last_x) - { - curr->extendTo(last_x); - } - } + const std::vector& points = curr->getPoints(); + if (points[points.size() - 1].X < m_end_time) + curr->extendEnd(m_end_time); + if(points[0].X > m_start_time) + curr->extendStart(m_start_time); } - - - m_playing = true; + m_playing = true; } // AnimationBase // ---------------------------------------------------------------------------- @@ -90,6 +92,7 @@ void AnimationBase::setInitialTransform(const core::vector3df &xyz, */ void AnimationBase::reset() { + m_current_time = m_start_time; Ipo* curr; for_in (curr, m_all_ipos) { @@ -106,12 +109,58 @@ void AnimationBase::reset() void AnimationBase::update(float dt, core::vector3df *xyz, core::vector3df *hpr, core::vector3df *scale) { + m_current_time += dt; + + while(m_current_time > m_end_time) + m_current_time -= (m_end_time - m_start_time); if ( UserConfigParams::m_graphical_effects ) { Ipo* curr; for_in (curr, m_all_ipos) { - curr->update(dt, xyz, hpr, scale); + curr->update(m_current_time, xyz, hpr, scale); } } -} // float dt +} // update + +// ---------------------------------------------------------------------------- +/** Approximates the overall length of each segment (curve between two points) + * and the overall length of the curve. + */ +void AnimationBase::computeLengths() +{ + Ipo* curr; + // First determine the maximum number of points among all IPOs + unsigned int max_points=0; + for_in (curr, m_all_ipos) + { + const std::vector& points = curr->getPoints(); + max_points = std::max(max_points, points.size()); + } + + // Divide (on average) each segment into STEPS points, and use + // a simple linear approximation for this part of the curve + const float STEPS = 100.0f * (max_points-1); + float x = m_start_time; + float dx = (m_end_time - m_start_time) / STEPS ; + float distance = 0; + + core::vector3df xyz_old(0,0,0), hpr, scale; + for_in(curr, m_all_ipos) + { + curr->update(m_start_time, &xyz_old, &hpr, &scale); + } + + for(unsigned int i=1; i<(unsigned int)STEPS; i++) + { + float x = m_start_time + dx * i; + core::vector3df xyz(0,0,0); + for_in(curr, m_all_ipos) + { + // hpr is not needed, so just reuse old variable + curr->update(x, &xyz, &hpr, &scale); + } // for curr in m_all_ipos + distance += (xyz-xyz_old).getLength(); + xyz_old = xyz; + } // for i in m_points +} // computeLengths \ No newline at end of file diff --git a/src/animations/animation_base.hpp b/src/animations/animation_base.hpp index 55ca4eb07..ea0820912 100644 --- a/src/animations/animation_base.hpp +++ b/src/animations/animation_base.hpp @@ -26,7 +26,6 @@ */ #include - #include using namespace irr; @@ -50,20 +49,26 @@ private: /** True if the animation is currently playing. */ bool m_playing; - /** For one time animations: start time. */ - float m_start; + /** The smallest time for which this animation is defined - usually 0.*/ + float m_start_time; + + /** The end time for this animation. */ + float m_end_time; + + /** The current time used in the IPOs. */ + float m_current_time; /** For cyclic animations: duration of the cycle. */ float m_cycle_length; - /** The current time in the cycle of a cyclic animation. */ - float m_current_time; - /** The inital position of this object. */ core::vector3df m_initial_xyz; /** The initial rotation of this object. */ core::vector3df m_initial_hpr; + + void computeLengths(); + protected: /** All IPOs for this animation. */ PtrVector m_all_ipos; diff --git a/src/animations/ipo.cpp b/src/animations/ipo.cpp index 341b6929a..57510453b 100644 --- a/src/animations/ipo.cpp +++ b/src/animations/ipo.cpp @@ -52,8 +52,6 @@ Ipo::Ipo(const XMLNode &curve, float fps) else if(interp=="linear") m_interpolation = IP_LINEAR; else m_interpolation = IP_BEZIER; - m_min_time = 999999; - m_max_time = -1; for(unsigned int i=0; im_max_time) m_max_time = t; xy.X = t; m_points.push_back(xy); if(m_interpolation==IP_BEZIER) @@ -102,33 +98,30 @@ void Ipo::setInitialTransform(const core::vector3df &xyz, */ void Ipo::reset() { - m_time = 0; + m_next_n = 1; } // reset // ---------------------------------------------------------------------------- /** Updates the time of this ipo and interpolates the new position and * rotation (taking the cycle length etc. into account). - * \param dt Time since last call. + * \param time Current time for which to determine the interpolation. * \param xyz The position that needs to be updated. * \param hpr The rotation that needs to be updated. */ -void Ipo::update(float dt, core::vector3df *xyz, core::vector3df *hpr, +void Ipo::update(float time, core::vector3df *xyz, core::vector3df *hpr, core::vector3df *scale) -{ - m_time += dt; - if(m_extend!=ET_CONST && m_time>m_max_time) m_time = 0; - +{ switch(m_channel) { - case Ipo::IPO_LOCX : xyz->X = get(); break; - case Ipo::IPO_LOCY : xyz->Y = get(); break; - case Ipo::IPO_LOCZ : xyz->Z = get(); break; - case Ipo::IPO_ROTX : hpr->X = get(); break; - case Ipo::IPO_ROTY : hpr->Y = get(); break; - case Ipo::IPO_ROTZ : hpr->Z = get(); break; - case Ipo::IPO_SCALEX : scale->X = get(); break; - case Ipo::IPO_SCALEY : scale->Y = get(); break; - case Ipo::IPO_SCALEZ : scale->Z = get(); break; + case Ipo::IPO_LOCX : xyz->X = get(time); break; + case Ipo::IPO_LOCY : xyz->Y = get(time); break; + case Ipo::IPO_LOCZ : xyz->Z = get(time); break; + case Ipo::IPO_ROTX : hpr->X = get(time); break; + case Ipo::IPO_ROTY : hpr->Y = get(time); break; + case Ipo::IPO_ROTZ : hpr->Z = get(time); break; + case Ipo::IPO_SCALEX : scale->X = get(time); break; + case Ipo::IPO_SCALEY : scale->Y = get(time); break; + case Ipo::IPO_SCALEZ : scale->Z = get(time); break; default: assert(false); // shut up compiler warning } // switch @@ -137,29 +130,28 @@ void Ipo::update(float dt, core::vector3df *xyz, core::vector3df *hpr, // ---------------------------------------------------------------------------- /** Returns the interpolated value at the current time (which this objects * keeps track of). + * \param time The time for which the interpolated value should be computed. */ -float Ipo::get() const +float Ipo::get(float time) const { - if(m_time=m_points[n].X) - n++; - // Avoid crash in case that only one point is given for this IPO. - if(n==0) - return m_points[0].Y; - n--; + while(m_next_n=m_points[m_next_n].X) + m_next_n++; + int n = m_next_n - 1; switch(m_interpolation) { case IP_CONST : return m_points[n].Y; case IP_LINEAR : { - float t = m_time-m_points[n].X; + float t = time-m_points[n].X; return m_points[n].Y + t*(m_points[n+1].Y-m_points[n].Y) / (m_points[n+1].X-m_points[n].X); } @@ -172,7 +164,7 @@ float Ipo::get() const core::vector2df c = 3.0f*(m_handle2[n]-m_points[n]); core::vector2df b = 3.0f*(m_handle1[n+1]-m_handle2[n])-c; core::vector2df a = m_points[n+1] - m_points[n] - c - b; - float t = (m_time-m_points[n].X)/(m_points[n+1].X-m_points[n].X); + float t = (time-m_points[n].X)/(m_points[n+1].X-m_points[n].X); core::vector2df r = ((a*t+b)*t+c)*t+m_points[n]; return r.Y; } @@ -182,31 +174,77 @@ float Ipo::get() const } // get // ---------------------------------------------------------------------------- +/** Inserts a new start point at the beginning of the IPO to make sure that + * this IPO starts with X. + * \param x The minimum value for which this IPO should be defined. + */ +void Ipo::extendStart(float x) +{ + assert(m_points[0].X > x); + extend(x, 0); +} // extendStart +// ---------------------------------------------------------------------------- +/** Inserts an additional point at the end of the IPO to make sure that this + * IPO ends with X. + * \param x The maximum value for which this IPO should be defined. + */ +void Ipo::extendEnd(float x) +{ + assert(m_points[m_points.size()-1].X < x); + extend(x, m_points.size()-1); +} // extendEnd -/** Extends the IPO to end at the given x time coordinate */ -void Ipo::extendTo(float x) +// ---------------------------------------------------------------------------- +/** Extends the IPO either at the beginning (n=0) or at the end (n=size()-1). + * This is used by AnimationBase to make sure all IPOs of one curve have the + * same cycle. + * \param x The X value to which the IPO must be extended. + * \param n The index at (before/after) which to extend. + */ +void Ipo::extend(float x, unsigned int n) { switch (m_interpolation) { case IP_CONST: { - m_points.push_back( core::vector2df(x, m_points[m_points.size()-1].Y) ); + core::vector2df new_point(x, m_points[n].Y); + if(n==0) + m_points.insert(m_points.begin(), new_point); + else + m_points.push_back( new_point); break; } case IP_LINEAR: { - m_points.push_back( core::vector2df(x, m_points[m_points.size()-1].Y) ); + core::vector2df new_point(x, m_points[n].Y); + if(n=0) + m_points.insert(m_points.begin(), new_point); + else + m_points.push_back(new_point); break; } case IP_BEZIER: { // FIXME: I'm somewhat dubious this is the correct way to extend handles - m_handle1.push_back( m_handle1[m_handle1.size() - 1] + core::vector2df(x - m_points[m_points.size()-1].X ,0) ); - m_handle2.push_back( m_handle2[m_handle2.size() - 1] + core::vector2df(x - m_points[m_points.size()-1].X ,0) ); - - m_points.push_back( core::vector2df(x, m_points[m_points.size()-1].Y) ); + core::vector2df new_h1 = m_handle1[n] + + core::vector2df(x - m_points[n].X ,0); + core::vector2df new_h2 = m_handle2[n] + + core::vector2df(x - m_points[n].X ,0); + core::vector2df new_p(x, m_points[n].Y); + if(n==0) + { + m_handle1.insert(m_handle1.begin(), new_h1); + m_handle2.insert(m_handle2.begin(), new_h2); + m_points.insert(m_points.begin(), new_p); + } + else + { + m_handle1.push_back(new_h1); + m_handle2.push_back(new_h2); + m_points.push_back(new_p); + } break; } } - m_max_time = x; -} +} // extend + diff --git a/src/animations/ipo.hpp b/src/animations/ipo.hpp index 9c8a57a9e..e1f3b97bc 100644 --- a/src/animations/ipo.hpp +++ b/src/animations/ipo.hpp @@ -54,39 +54,39 @@ private: /** The actual control points. */ std::vector m_points; + /** Only used for bezier curves: the two handles. */ std::vector m_handle1, m_handle2; - /** Current time in cycle. */ - float m_time; - - /** Minium time when this animations starts, usually 0. */ - float m_min_time; - - /** Time this animation finishes (or cycles). */ - float m_max_time; - /** Frames per second for this animation. */ float m_fps; + /** Which control points will be the next one (so m_next_n-1 and + * m_next_n are the control points to use now). This just reduces + * lookup time in get(t). To allow modifying this in get() const, + * it is declared mutable). */ + mutable unsigned int m_next_n; + /** Stores the inital position of the object. */ core::vector3df m_initial_xyz; + /** Stores the inital rotation of the object. */ core::vector3df m_initial_hpr; -public: - Ipo(const XMLNode &curve, float fps); - void update(float dt, core::vector3df *xyz, core::vector3df *hpr, - core::vector3df *scale); - float get() const; - void setInitialTransform(const core::vector3df &xyz, - const core::vector3df &hpr); - void reset(); - - void extendTo(float x); - - const std::vector& getPoints() const { return m_points; } - + void extend(float x, unsigned int n); +public: + Ipo(const XMLNode &curve, float fps); + void update(float time, core::vector3df *xyz, core::vector3df *hpr, + core::vector3df *scale); + float get(float time) const; + void setInitialTransform(const core::vector3df &xyz, + const core::vector3df &hpr); + void reset(); + + void extendStart(float x); + void extendEnd(float x); + + const std::vector& getPoints() const { return m_points; } }; // Ipo #endif diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index fa23c90ba..357ca2c5d 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -18,40 +18,20 @@ #include "karts/cannon_animation.hpp" +#include "animations/animation_base.hpp" #include "karts/abstract_kart.hpp" #include "modes/world.hpp" #include "LinearMath/btTransform.h" -CannonAnimation::CannonAnimation(AbstractKart *kart, const Vec3 &target, - float speed) +CannonAnimation::CannonAnimation(AbstractKart *kart, AnimationBase *ab) : AbstractKartAnimation(kart) { - m_xyz = m_kart->getXYZ(); - assert(speed>0); - Vec3 delta = target-m_kart->getXYZ(); - m_timer = delta.length()/speed; - m_velocity = delta/m_timer; - - World::getWorld()->getPhysics()->removeKart(m_kart); - - m_curr_rotation.setHeading(m_kart->getHeading()); - m_curr_rotation.setPitch(m_kart->getPitch()); - m_curr_rotation.setRoll(m_kart->getRoll()); - - m_add_rotation.setHeading(0); - m_add_rotation.setPitch( 0); - m_add_rotation.setRoll( 0); } // CannonAnimation // ---------------------------------------------------------------------------- CannonAnimation::~CannonAnimation() { - btTransform trans = m_kart->getTrans(); - trans.setOrigin(m_xyz); - m_kart->setTrans(trans); - m_kart->getBody()->setCenterOfMassTransform(trans); - World::getWorld()->getPhysics()->addKart(m_kart); } // ~CannonAnimation // ---------------------------------------------------------------------------- @@ -61,12 +41,5 @@ CannonAnimation::~CannonAnimation() */ void CannonAnimation::update(float dt) { - m_xyz += dt*m_velocity; - m_kart->setXYZ(m_xyz); - m_curr_rotation += dt*m_add_rotation; - btQuaternion q(m_curr_rotation.getHeading(), m_curr_rotation.getPitch(), - m_curr_rotation.getRoll()); - m_kart->setRotation(q); - AbstractKartAnimation::update(dt); } // update diff --git a/src/karts/cannon_animation.hpp b/src/karts/cannon_animation.hpp index 69988e915..a413485a0 100644 --- a/src/karts/cannon_animation.hpp +++ b/src/karts/cannon_animation.hpp @@ -28,31 +28,18 @@ */ class AbstractKart; +class AnimationBase; class CannonAnimation: public AbstractKartAnimation { protected: - /** The coordinates where the kart was hit originally. */ - Vec3 m_xyz; + /** The offset between the point where the check line was originially + * crossed and the origin of the curve. */ + Vec3 delta; - /** The kart's current rotation. */ - Vec3 m_curr_rotation; - - /** The artificial rotation to toss the kart around. It's in units - * of rotation per second. */ - Vec3 m_add_rotation; - - /** The velocity with which the kart is moved. */ - Vec3 m_velocity; - - /** Duration for this explosion. This can potentially be set - * with different values for different karts, or depending - * on difficulty (so that on easy you can drive again earlier. */ - float m_duration; public: - CannonAnimation(AbstractKart *kart, const Vec3 &target, - float speed); + CannonAnimation(AbstractKart *kart, AnimationBase *ab); virtual ~CannonAnimation(); virtual void update(float dt); virtual const std::string getName() const {return "Cannon";} diff --git a/src/tracks/check_cannon.cpp b/src/tracks/check_cannon.cpp index 02b2af232..8da866bad 100644 --- a/src/tracks/check_cannon.cpp +++ b/src/tracks/check_cannon.cpp @@ -18,11 +18,27 @@ #include "tracks/check_cannon.hpp" +#include "animations/animation_base.hpp" #include "io/xml_node.hpp" #include "karts/abstract_kart.hpp" #include "karts/cannon_animation.hpp" #include "modes/world.hpp" + +CheckCannon::CannonCurve::CannonCurve(const XMLNode &node) + : AnimationBase(node) +{ + m_speed = -1; + node.get("speed", &m_speed); +} // CannonCurve + +// ------------------------------------------------------------------------ +void CheckCannon::CannonCurve::update(float dt) +{ +} // update + +// ============================================================================ + /** Constructor for a check cannon. * \param node XML node containing the parameters for this checkline. * \param index Index of this check structure in the check manager. @@ -38,14 +54,19 @@ CheckCannon::CheckCannon(const XMLNode &node, unsigned int index) exit(-1); } m_target.setLine(p1, p2); - m_speed = -1; - node.get("speed", &m_speed); + m_curve = new CannonCurve(node); } // CheckCannon +// ---------------------------------------------------------------------------- +CheckCannon::~CheckCannon() +{ + delete m_curve; +} // ~CheckCannon + // ---------------------------------------------------------------------------- void CheckCannon::trigger(unsigned int kart_index) { Vec3 target(m_target.getMiddle()); AbstractKart *kart = World::getWorld()->getKart(kart_index); - new CannonAnimation(kart, target, m_speed); + new CannonAnimation(kart, m_curve); } // CheckCannon diff --git a/src/tracks/check_cannon.hpp b/src/tracks/check_cannon.hpp index 2db209932..eecc60b81 100644 --- a/src/tracks/check_cannon.hpp +++ b/src/tracks/check_cannon.hpp @@ -19,10 +19,11 @@ #ifndef HEADER_CHECK_CANNON_HPP #define HEADER_CHECK_CANNON_HPP +#include "animations/animation_base.hpp" #include "tracks/check_line.hpp" -class XMLNode; class CheckManager; +class XMLNode; /** * \brief Implements a simple checkline that will cause a kart to be @@ -36,11 +37,26 @@ private: /** The target point the kart will fly to. */ core::line3df m_target; - /** The speed with which the kart moves. */ - float m_speed; + + // ------------------------------------------------------------------------ +protected: + class CannonCurve : public AnimationBase + { + private: + /** The speed with which the kart moves. */ + float m_speed; + public: + CannonCurve(const XMLNode &node); + void update(float dt); + }; // CannonCurve + + // ------------------------------------------------------------------------ + /** Stores the cannon curve data. */ + CannonCurve *m_curve; public: - CheckCannon(const XMLNode &node, unsigned int index); + CheckCannon(const XMLNode &node, unsigned int index); + virtual ~CheckCannon(); virtual void trigger(unsigned int kart_index); }; // CheckLine