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
This commit is contained in:
hikerstk 2012-04-04 00:48:05 +00:00
parent ae5b3b923e
commit 317ff370cb
8 changed files with 235 additions and 146 deletions

View File

@ -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<core::vector2df>& 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<core::vector2df>& points = curr->getPoints();
if (points[points.size() - 1].X < last_x)
{
curr->extendTo(last_x);
}
}
const std::vector<core::vector2df>& 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<core::vector2df>& 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

View File

@ -26,7 +26,6 @@
*/
#include <vector>
#include <vector3d.h>
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<Ipo> m_all_ipos;

View File

@ -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; i<curve.getNumNodes(); i++)
{
const XMLNode *node = curve.getNode(i);
@ -62,8 +60,6 @@ Ipo::Ipo(const XMLNode &curve, float fps)
// Convert blender's frame number (1 ...) into time (0 ...)
float t = (xy.X-1)/fps;
if(t<m_min_time) m_min_time = t;
if(t>m_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_min_time)
{
// FIXME: should take extend into account!
return 0;
}
unsigned int n=0;
// Avoid crash in case that only one point is given for this IPO.
if(m_next_n==0)
return m_points[0].Y;
// Time was reset since the last cached value for n,
// reset n to start from the beginning again.
if(time < m_points[m_next_n-1].X)
m_next_n = 1;
// Search for the first point in the (sorted) array which is greater or equal
// to the current time.
// FIXME: we should store the last point to speed this up!
while(n<m_points.size()-1 && 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.size()-1 && time >=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

View File

@ -54,39 +54,39 @@ private:
/** The actual control points. */
std::vector<core::vector2df> m_points;
/** Only used for bezier curves: the two handles. */
std::vector<core::vector2df> 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<core::vector2df>& 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<core::vector2df>& getPoints() const { return m_points; }
}; // Ipo
#endif

View File

@ -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

View File

@ -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";}

View File

@ -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

View File

@ -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