A first version of the cannon that uses linear interpolation for the
bezier curves to allow smooth (i.e. constant speed) travel along the curve. This is WIP. Known bug: the kart jumps at the beginning and end of the cannon animation. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@11122 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
parent
22e0f0f9c0
commit
d0fb8aa78a
@ -36,7 +36,8 @@ AnimationBase::AnimationBase(const XMLNode &node)
|
||||
Ipo *ipo = new Ipo(*node.getNode(i), fps);
|
||||
m_all_ipos.push_back(ipo);
|
||||
}
|
||||
m_playing = true;
|
||||
m_playing = true;
|
||||
m_anim_type = ATT_CYCLIC;
|
||||
#ifdef DEBUG
|
||||
if(m_all_ipos.size()==0)
|
||||
{
|
||||
@ -47,10 +48,15 @@ AnimationBase::AnimationBase(const XMLNode &node)
|
||||
|
||||
} // AnimationBase
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
AnimationBase::~AnimationBase()
|
||||
/** Special constructor which takes one IPO (or curve). This is used by the
|
||||
*/
|
||||
AnimationBase::AnimationBase(Ipo *ipo) : TrackObject()
|
||||
{
|
||||
} // ~AnimationBase
|
||||
m_anim_type = ATT_CYCLIC_ONCE;
|
||||
m_playing = true;
|
||||
m_all_ipos.push_back(ipo);
|
||||
reset();
|
||||
} // AnimationBase(Ipo)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Stores the initial transform (in the IPOs actually). This is necessary
|
||||
@ -93,57 +99,10 @@ void AnimationBase::update(float dt, Vec3 *xyz, Vec3 *hpr, Vec3 *scale)
|
||||
if(!m_playing) return;
|
||||
m_current_time += dt;
|
||||
|
||||
if ( UserConfigParams::m_graphical_effects )
|
||||
Ipo* curr;
|
||||
for_in (curr, m_all_ipos)
|
||||
{
|
||||
Ipo* curr;
|
||||
for_in (curr, m_all_ipos)
|
||||
{
|
||||
curr->update(m_current_time, xyz, hpr, scale);
|
||||
}
|
||||
curr->update(m_current_time, xyz, hpr, scale);
|
||||
}
|
||||
} // 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;
|
||||
float max_time = 0;
|
||||
for_in (curr, m_all_ipos)
|
||||
{
|
||||
const std::vector<Vec3>& points = curr->getPoints();
|
||||
max_points = std::max(max_points, (unsigned int) points.size());
|
||||
max_time = std::max(max_time, curr->getEndTime());
|
||||
}
|
||||
|
||||
// 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 dx = max_time / STEPS ;
|
||||
float distance = 0;
|
||||
|
||||
// Initialise xyz_old with the point at time 0
|
||||
Vec3 xyz_old(0,0,0), hpr, scale;
|
||||
for_in(curr, m_all_ipos)
|
||||
{
|
||||
curr->update(/*time*/0, &xyz_old, &hpr, &scale);
|
||||
}
|
||||
|
||||
for(unsigned int i=1; i<(unsigned int)STEPS; i++)
|
||||
{
|
||||
// Interpolations always start at time 0
|
||||
float x = dx * i;
|
||||
Vec3 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).length();
|
||||
xyz_old = xyz;
|
||||
} // for i in m_points
|
||||
} // computeLengths
|
||||
|
@ -27,11 +27,13 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
// Note that ipo.hpp is included here in order that PtrVector<Ipo> can call
|
||||
// the proper destructor!
|
||||
#include "animations/ipo.hpp"
|
||||
#include "tracks/track_object.hpp"
|
||||
#include "utils/ptr_vector.hpp"
|
||||
|
||||
class XMLNode;
|
||||
class Ipo;
|
||||
|
||||
/**
|
||||
* \brief A base class for all animations.
|
||||
@ -59,19 +61,19 @@ private:
|
||||
/** The initial rotation of this object. */
|
||||
Vec3 m_initial_hpr;
|
||||
|
||||
void computeLengths();
|
||||
|
||||
protected:
|
||||
/** All IPOs for this animation. */
|
||||
PtrVector<Ipo> m_all_ipos;
|
||||
|
||||
public:
|
||||
AnimationBase(const XMLNode &node);
|
||||
virtual ~AnimationBase();
|
||||
AnimationBase(Ipo *ipo);
|
||||
virtual void update(float dt, Vec3 *xyz, Vec3 *hpr, Vec3 *scale);
|
||||
/** This needs to be implemented by the inheriting classes. It is called
|
||||
* once per frame from the track. */
|
||||
virtual void update(float dt) = 0;
|
||||
* once per frame from the track. It has a dummy implementation that
|
||||
* just asserts so that this class can be instantiated in
|
||||
* CannonAnimation. */
|
||||
virtual void update(float dt) {assert(false); };
|
||||
void setInitialTransform(const Vec3 &xyz,
|
||||
const Vec3 &hpr);
|
||||
void reset();
|
||||
|
@ -20,12 +20,20 @@
|
||||
|
||||
#include "io/xml_node.hpp"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
const std::string Ipo::m_all_channel_names[IPO_MAX] =
|
||||
{"LocX", "LocY", "LocZ", "LocXYZ",
|
||||
"RotX", "RotY", "RotZ",
|
||||
"ScaleX", "ScaleY", "ScaleZ" };
|
||||
|
||||
Ipo::Ipo(const XMLNode &curve, float fps)
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Initialise the Ipo from the specifications in the XML file.
|
||||
* \param curve The XML node with the IPO data.
|
||||
* \param fps Frames per second value, necessary to convert frame values
|
||||
* into time.
|
||||
*/
|
||||
Ipo::IpoData::IpoData(const XMLNode &curve, float fps)
|
||||
{
|
||||
if(curve.getName()!="curve")
|
||||
{
|
||||
@ -38,11 +46,16 @@ Ipo::Ipo(const XMLNode &curve, float fps)
|
||||
m_channel=IPO_MAX;
|
||||
for(unsigned int i=IPO_LOCX; i<IPO_MAX; i++)
|
||||
{
|
||||
if(m_all_channel_names[i]==channel) m_channel=(IpoChannelType)i;
|
||||
if(m_all_channel_names[i]==channel)
|
||||
{
|
||||
m_channel=(IpoChannelType)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(m_channel==IPO_MAX)
|
||||
{
|
||||
fprintf(stderr, "Unknown animation channel: '%s' - aborting.\n", channel.c_str());
|
||||
fprintf(stderr, "Unknown animation channel: '%s' - aborting.\n",
|
||||
channel.c_str());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@ -52,6 +65,23 @@ Ipo::Ipo(const XMLNode &curve, float fps)
|
||||
else if(interp=="linear") m_interpolation = IP_LINEAR;
|
||||
else m_interpolation = IP_BEZIER;
|
||||
|
||||
if(m_channel==IPO_LOCXYZ)
|
||||
readCurve(curve);
|
||||
else
|
||||
readIPO(curve, fps);
|
||||
|
||||
// ATM no other extends are supported, so hardcode the only one
|
||||
// that works!
|
||||
m_extend = ET_CYCLIC;
|
||||
} // IpoData
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Reads a blender IPO curve, which constists of a frame number and a control
|
||||
* point. This only handles a single axis.
|
||||
* \param node The root node with all curve data points.
|
||||
*/
|
||||
void Ipo::IpoData::readIPO(const XMLNode &curve, float fps)
|
||||
{
|
||||
m_start_time = 999999.9f;
|
||||
m_end_time = -999999.9f;
|
||||
for(unsigned int i=0; i<curve.getNumNodes(); i++)
|
||||
@ -61,30 +91,303 @@ Ipo::Ipo(const XMLNode &curve, float fps)
|
||||
node->get("c", &xy);
|
||||
// Convert blender's frame number (1 ...) into time (0 ...)
|
||||
float t = (xy.X-1)/fps;
|
||||
|
||||
Vec3 point(t, xy.Y, 0);
|
||||
Vec3 point(xy.Y, 0, 0, t);
|
||||
m_points.push_back(point);
|
||||
m_start_time = std::min(m_start_time, t);
|
||||
m_end_time = std::max(m_end_time, t);
|
||||
m_points.push_back(point);
|
||||
if(m_interpolation==IP_BEZIER)
|
||||
{
|
||||
Vec3 handle1, handle2;
|
||||
core::vector2df handle;
|
||||
node->get("h1", &handle);
|
||||
handle.X = (xy.X-1)/fps;
|
||||
m_handle1.push_back(Vec3(handle.X, handle.Y, 0));
|
||||
handle1.setW((xy.X-1)/fps);
|
||||
handle1.setX(handle.Y);
|
||||
node->get("h2", &handle);
|
||||
handle.X = (xy.X-1)/fps;
|
||||
m_handle2.push_back(Vec3(handle.X, handle.Y, 0));
|
||||
handle2.setW((xy.X-1)/fps);
|
||||
handle2.setX(handle.Y);
|
||||
m_handle1.push_back(handle1);
|
||||
m_handle2.push_back(handle2);
|
||||
}
|
||||
} // for i<getNumNodes()
|
||||
} // IpoData::readIPO
|
||||
|
||||
// ATM no other extends are supported, so hardcode the only one
|
||||
// that works!
|
||||
m_extend = ET_CYCLIC;
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Reads in 3 dimensional curve data - i.e. the xml file contains xyz, but no
|
||||
* time. If the curve is using bezier interpolation, the curve is
|
||||
* approximated by piecewise linear functions. Reason is that bezier curves
|
||||
* can not (easily) be used for smooth (i.e. constant speed) driving:
|
||||
* A linear time variation in [0, 1] will result in non-linear distances
|
||||
* for the bezier function, which is a 3rd degree polynomial (--> the speed
|
||||
* which is the deviation of this function is a 2nd degree polynomial, and
|
||||
* therefore not constant!
|
||||
* \param node The root node with all curve data points.
|
||||
*/
|
||||
void Ipo::IpoData::readCurve(const XMLNode &curve)
|
||||
{
|
||||
m_start_time = 0;
|
||||
m_end_time = -999999.9f;
|
||||
float speed = 30.0f;
|
||||
curve.get("speed", &speed);
|
||||
|
||||
for(unsigned int i=0; i<curve.getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *node = curve.getNode(i);
|
||||
Vec3 point;
|
||||
node->get("c", &point);
|
||||
|
||||
if(m_interpolation==IP_BEZIER)
|
||||
{
|
||||
Vec3 handle;
|
||||
node->get("h1", &handle);
|
||||
m_handle1.push_back(handle);
|
||||
node->get("h2", &handle);
|
||||
m_handle2.push_back(handle);
|
||||
if(i>0)
|
||||
{
|
||||
// We have to take a copy of the end point, since otherwise
|
||||
// it can happen that as more points are added to m_points
|
||||
// in the approximateBezier function, the data gets
|
||||
// reallocated and then the reference to the original point
|
||||
// is not correct anymore.
|
||||
Vec3 end_point = m_points[m_points.size()-1];
|
||||
approximateBezier(0.0f, 1.0f, end_point, point,
|
||||
m_handle2[i-1], m_handle1[i]);
|
||||
}
|
||||
}
|
||||
m_points.push_back(point);
|
||||
} // for i<getNumNodes()
|
||||
|
||||
// The handles of a bezier curve are not needed anymore and can be
|
||||
// removed now (since the bezier funciton has been replaced with a
|
||||
// piecewise linear
|
||||
if(m_interpolation==IP_BEZIER)
|
||||
{
|
||||
m_handle1.clear();
|
||||
m_handle2.clear();
|
||||
m_interpolation = IP_LINEAR;
|
||||
}
|
||||
|
||||
if(m_points.size()==0) return;
|
||||
|
||||
// Compute the time for each segment based on the speed and
|
||||
// store it in the W component.
|
||||
m_points[0].setW(0);
|
||||
for(unsigned int i=1; i<m_points.size(); i++)
|
||||
{
|
||||
m_points[i].setW( (m_points[i]-m_points[i-1]).length()/speed
|
||||
+ m_points[i-1].getW() );
|
||||
}
|
||||
m_end_time = m_points.back().getW();
|
||||
} // IpoData::readCurve
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** This function approximates a bezier curve by piecewise linear functions.
|
||||
* It uses quite primitive approximations: if the estimated distance of
|
||||
* the bezier curve at between t=t0 and t=t1 is greater than 2, it
|
||||
* inserts one point at (t0+t1)/2, and recursively splits the two intervals
|
||||
* further. End condition is either a maximum recursion depth of 6 or
|
||||
* an estimated curve length of less than 2. It does not add any points
|
||||
* at t=t0 or t=t1, only between this interval.
|
||||
* \param t0, t1 The interval which is approximated.
|
||||
* \param p0, p1, h0, h1: The bezier parameters.
|
||||
* \param rec_level The recursion level to avoid creating too many points.
|
||||
*/
|
||||
void Ipo::IpoData::approximateBezier(float t0, float t1,
|
||||
const Vec3 &p0, const Vec3 &p1,
|
||||
const Vec3 &h0, const Vec3 &h1,
|
||||
unsigned int rec_level)
|
||||
{
|
||||
// Limit the granularity by limiting the recursion depth
|
||||
if(rec_level>6)
|
||||
return;
|
||||
|
||||
float distance = approximateLength(t0, t1, p0, p1, h0, h1);
|
||||
// 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)
|
||||
return;
|
||||
|
||||
// Insert one point at (t0+t1)/2. First split the left part of
|
||||
// the interval by a recursive call, then insert the point at
|
||||
// (t0+t1)/2, then approximate the right part of the interval.
|
||||
approximateBezier(t0, (t0+t1)*0.5f, p0, p1, h0, h1, rec_level + 1);
|
||||
Vec3 middle;
|
||||
for(unsigned int j=0; j<3; j++)
|
||||
middle[j] = getCubicBezier((t0+t1)*0.5f, p0[j], h0[j], h1[j], p1[j]);
|
||||
m_points.push_back(middle);
|
||||
approximateBezier((t0+t1)*0.5f, t1, p0, p1, h0, h1, rec_level + 1);
|
||||
|
||||
} // approximateBezier
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Approximates the length of a bezier curve using a simple Euler
|
||||
* approximation by dividing the interval [t0, t1] into 10 pieces. Good enough
|
||||
* for our needs in STK.
|
||||
* \param t0, t1 Approximate for t in [t0, t1].
|
||||
* \param p0, p1 The start and end point of the curve.
|
||||
* \param h0, h1 The control points for the corresponding points.
|
||||
*/
|
||||
float Ipo::IpoData::approximateLength(float t0, float t1,
|
||||
const Vec3 &p0, const Vec3 &p1,
|
||||
const Vec3 &h0, const Vec3 &h1)
|
||||
{
|
||||
assert(m_interpolation == IP_BEZIER);
|
||||
|
||||
float distance=0;
|
||||
const unsigned int NUM_STEPS=10;
|
||||
float delta = (t1-t0)/NUM_STEPS;
|
||||
Vec3 prev_point;
|
||||
for(unsigned int j=0; j<3; j++)
|
||||
prev_point[j] = getCubicBezier(t0, p0[j], h0[j], h1[j], p1[j]);
|
||||
for(unsigned int i=1; i<=NUM_STEPS; i++)
|
||||
{
|
||||
float t = t0 + i * delta;
|
||||
Vec3 next_point;
|
||||
// Interpolate all three axis
|
||||
for(unsigned j=0; j<3; j++)
|
||||
{
|
||||
next_point[j] = getCubicBezier(t, p0[j], h0[j], h1[j], p1[j]);
|
||||
}
|
||||
distance += (next_point - prev_point).length();
|
||||
prev_point = next_point;
|
||||
} // for i< NUM_STEPS
|
||||
|
||||
return distance;
|
||||
} // IpoData::approximateLength
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Adjusts the time so that it is between start and end of this Ipo. This
|
||||
* takes the extend type into account, e.g. cyclic animations will just
|
||||
* use a modulo operation, while constant extends will return start or
|
||||
* end time directly.
|
||||
* \param time The time to adjust.
|
||||
*/
|
||||
float Ipo::IpoData::adjustTime(float time)
|
||||
{
|
||||
if(time<m_start_time)
|
||||
{
|
||||
switch(m_extend)
|
||||
{
|
||||
case IpoData::ET_CYCLIC:
|
||||
time = m_start_time + fmodf(time, m_end_time-m_start_time); break;
|
||||
case ET_CONST:
|
||||
time = m_start_time; break;
|
||||
default:
|
||||
// FIXME: ET_CYCLIC_EXTRAP and ET_EXTRAP missing
|
||||
assert(false);
|
||||
} // switch m_extend
|
||||
} // if time < m_start_time
|
||||
|
||||
else if(time > m_end_time)
|
||||
{
|
||||
switch(m_extend)
|
||||
{
|
||||
case ET_CYCLIC:
|
||||
time = m_start_time + fmodf(time, m_end_time-m_start_time); break;
|
||||
case ET_CONST:
|
||||
time = m_end_time; break;
|
||||
default:
|
||||
// FIXME: ET_CYCLIC_EXTRAP and ET_EXTRAP missing
|
||||
assert(false);
|
||||
} // switch m_extend
|
||||
} // if time > m_end_time
|
||||
return time;
|
||||
} // adjustTime
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
float Ipo::IpoData::get(float time, unsigned int index, unsigned int n)
|
||||
{
|
||||
switch(m_interpolation)
|
||||
{
|
||||
case IP_CONST : return m_points[n][index];
|
||||
case IP_LINEAR : {
|
||||
float t = time-m_points[n].getW();
|
||||
return m_points[n][index]
|
||||
+ t*(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)
|
||||
{
|
||||
// FIXME: only const implemented atm.
|
||||
return m_points[n][index];
|
||||
}
|
||||
float t = (time-m_points[n].getW())
|
||||
/ (m_points[n+1].getW()-m_points[n].getW());
|
||||
return getCubicBezier(t,
|
||||
m_points [n ][index],
|
||||
m_handle2[n ][index],
|
||||
m_handle1[n+1][index],
|
||||
m_points [n+1][index]);
|
||||
}
|
||||
} // switch
|
||||
// Keep the compiler happy:
|
||||
return 0;
|
||||
} // get
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Computes a cubic bezier curve for a given t in [0,1] and four control
|
||||
* points. The curve will go through p0 (t=0), p3 (t=1).
|
||||
* \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::getCubicBezier(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;
|
||||
return ((a*t+b)*t+c)*t+p0;
|
||||
} // bezier
|
||||
|
||||
// ============================================================================
|
||||
/** The Ipo constructor. Ipos can share the actual data to interpolate, which
|
||||
* is stored in a separate IpoData object, see Ipo(const Ipo *ipo)
|
||||
* constructor. This is used for cannons: the actual check line stores the
|
||||
* 'master' Ipo, and each actual IPO that animate a kart just use a copy
|
||||
* of this read-only data.
|
||||
* \param curve The XML data for this curve.
|
||||
* \param fps Frames per second, used to convert all frame based value
|
||||
* in the xml file into seconds.
|
||||
*/
|
||||
Ipo::Ipo(const XMLNode &curve, float fps)
|
||||
{
|
||||
m_ipo_data = new IpoData(curve, fps);
|
||||
m_own_ipo_data = true;
|
||||
reset();
|
||||
} // Ipo
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** A copy constructor. It shares the read-only data with the source Ipo
|
||||
* \param ipo The ipo to copy from.
|
||||
*/
|
||||
Ipo::Ipo(const Ipo *ipo)
|
||||
{
|
||||
// Share the read-only data
|
||||
m_ipo_data = ipo->m_ipo_data;
|
||||
m_own_ipo_data = false;
|
||||
reset();
|
||||
} // Ipo(Ipo*)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Creates a copy of this object (the copy constructor is disabled in order
|
||||
* to avoid implicit copies happening).
|
||||
*/
|
||||
Ipo *Ipo::clone()
|
||||
{
|
||||
return new Ipo(this);
|
||||
} // clone
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** The destructor only frees IpoData if it was created by this instance (and
|
||||
* not if this instance was copied, therefore sharing the IpoData).
|
||||
*/
|
||||
Ipo::~Ipo()
|
||||
{
|
||||
if(m_own_ipo_data)
|
||||
delete m_ipo_data;
|
||||
} // ~Ipo
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Stores the initial transform. This is necessary for relative IPOs.
|
||||
* \param xyz Position of the object.
|
||||
@ -93,8 +396,8 @@ Ipo::Ipo(const XMLNode &curve, float fps)
|
||||
void Ipo::setInitialTransform(const Vec3 &xyz,
|
||||
const Vec3 &hpr)
|
||||
{
|
||||
m_initial_xyz = xyz;
|
||||
m_initial_hpr = hpr;
|
||||
m_ipo_data->m_initial_xyz = xyz;
|
||||
m_ipo_data->m_initial_hpr = hpr;
|
||||
} // setInitialTransform
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -114,17 +417,24 @@ void Ipo::reset()
|
||||
*/
|
||||
void Ipo::update(float time, Vec3 *xyz, Vec3 *hpr,Vec3 *scale)
|
||||
{
|
||||
switch(m_channel)
|
||||
switch(m_ipo_data->m_channel)
|
||||
{
|
||||
case Ipo::IPO_LOCX : xyz ->setX(get(time)); break;
|
||||
case Ipo::IPO_LOCY : xyz ->setY(get(time)); break;
|
||||
case Ipo::IPO_LOCZ : xyz ->setZ(get(time)); break;
|
||||
case Ipo::IPO_ROTX : hpr ->setX(get(time)); break;
|
||||
case Ipo::IPO_ROTY : hpr ->setY(get(time)); break;
|
||||
case Ipo::IPO_ROTZ : hpr ->setZ(get(time)); break;
|
||||
case Ipo::IPO_SCALEX : scale->setX(get(time)); break;
|
||||
case Ipo::IPO_SCALEY : scale->setY(get(time)); break;
|
||||
case Ipo::IPO_SCALEZ : scale->setZ(get(time)); break;
|
||||
case Ipo::IPO_LOCX : xyz ->setX(get(time, 0)); break;
|
||||
case Ipo::IPO_LOCY : xyz ->setY(get(time, 0)); break;
|
||||
case Ipo::IPO_LOCZ : xyz ->setZ(get(time, 0)); break;
|
||||
case Ipo::IPO_ROTX : hpr ->setX(get(time, 0)); break;
|
||||
case Ipo::IPO_ROTY : hpr ->setY(get(time, 0)); break;
|
||||
case Ipo::IPO_ROTZ : hpr ->setZ(get(time, 0)); break;
|
||||
case Ipo::IPO_SCALEX : scale->setX(get(time, 0)); break;
|
||||
case Ipo::IPO_SCALEY : scale->setY(get(time, 0)); break;
|
||||
case Ipo::IPO_SCALEZ : scale->setZ(get(time, 0)); break;
|
||||
case Ipo::IPO_LOCXYZ :
|
||||
{
|
||||
for(unsigned int j=0; j<3; j++)
|
||||
(*xyz)[j] = get(time, j);
|
||||
break;
|
||||
}
|
||||
|
||||
default: assert(false); // shut up compiler warning
|
||||
} // switch
|
||||
|
||||
@ -135,162 +445,22 @@ void Ipo::update(float time, Vec3 *xyz, Vec3 *hpr,Vec3 *scale)
|
||||
* keeps track of).
|
||||
* \param time The time for which the interpolated value should be computed.
|
||||
*/
|
||||
float Ipo::get(float time) const
|
||||
float Ipo::get(float time, unsigned int index) const
|
||||
{
|
||||
if(time<m_start_time)
|
||||
{
|
||||
switch(m_extend)
|
||||
{
|
||||
case ET_CYCLIC:
|
||||
time = m_start_time + fmodf(time, m_end_time-m_start_time); break;
|
||||
case ET_CONST:
|
||||
time = m_start_time; break;
|
||||
default:
|
||||
// FIXME: ET_CYCLIC_EXTRAP and ET_EXTRAP missing
|
||||
assert(false);
|
||||
} // switch m_extend
|
||||
} // if time < m_start_time
|
||||
|
||||
if(time > m_end_time)
|
||||
{
|
||||
switch(m_extend)
|
||||
{
|
||||
case ET_CYCLIC:
|
||||
time = m_start_time + fmodf(time, m_end_time-m_start_time); break;
|
||||
case ET_CONST:
|
||||
time = m_end_time; break;
|
||||
default:
|
||||
// FIXME: ET_CYCLIC_EXTRAP and ET_EXTRAP missing
|
||||
assert(false);
|
||||
} // switch m_extend
|
||||
} // if time > m_end_time
|
||||
|
||||
// Avoid crash in case that only one point is given for this IPO.
|
||||
if(m_next_n==0)
|
||||
return m_points[0].getY();
|
||||
return m_ipo_data->m_points[0][index];
|
||||
float orig_t=time;
|
||||
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_points[m_next_n-1].getX())
|
||||
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_points.size()-1 && time >=m_points[m_next_n].getX())
|
||||
while(m_next_n<m_ipo_data->m_points.size()-1 &&
|
||||
time >=m_ipo_data->m_points[m_next_n].getW())
|
||||
m_next_n++;
|
||||
int n = m_next_n - 1;
|
||||
switch(m_interpolation)
|
||||
{
|
||||
case IP_CONST : return m_points[n].getY();
|
||||
case IP_LINEAR : {
|
||||
float t = time-m_points[n].getX();
|
||||
return m_points[n].getY()
|
||||
+ t*(m_points[n+1].getY()-m_points[n].getY()) /
|
||||
(m_points[n+1].getX()-m_points[n].getX());
|
||||
}
|
||||
case IP_BEZIER: {
|
||||
if(n==(int)m_points.size()-1)
|
||||
{
|
||||
// FIXME: only const implemented atm.
|
||||
return m_points[n].getY();
|
||||
}
|
||||
float t = (time-m_points[n].getX())
|
||||
/ (m_points[n+1].getX()-m_points[n].getX());
|
||||
return getCubicBezier(t,
|
||||
m_points[n].getY(),
|
||||
m_handle2[n].getY(),
|
||||
m_handle1[n+1].getY(),
|
||||
m_points[n+1].getY());
|
||||
}
|
||||
} // switch
|
||||
// Keep the compiler happy:
|
||||
return 0;
|
||||
} // get
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Computes a cubic bezier curve for a given t in [0,1] and four control
|
||||
* points. The curve will go through p0 (t=0), p3 (t=1).
|
||||
* \param t The parameter for the bezier curve, must be in [0,1].
|
||||
* \param p0, p1, p2, p3 The four control points.
|
||||
*/
|
||||
float Ipo::getCubicBezier(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;
|
||||
return ((a*t+b)*t+c)*t+p0;
|
||||
} // bezier
|
||||
// ----------------------------------------------------------------------------
|
||||
/** 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].getX() > 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].getX() < x);
|
||||
extend(x, m_points.size()-1);
|
||||
} // extendEnd
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** 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:
|
||||
{
|
||||
Vec3 new_point(x, m_points[n].getY(), 0);
|
||||
if(n==0)
|
||||
m_points.insert(m_points.begin(), new_point);
|
||||
else
|
||||
m_points.push_back( new_point);
|
||||
break;
|
||||
}
|
||||
case IP_LINEAR:
|
||||
{
|
||||
Vec3 new_point(x, m_points[n].getY(), 0);
|
||||
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
|
||||
Vec3 new_h1 = m_handle1[n] + Vec3(x - m_points[n].getX() ,0, 0);
|
||||
Vec3 new_h2 = m_handle2[n] + Vec3(x - m_points[n].getX() ,0, 0);
|
||||
Vec3 new_p(x, m_points[n].getY());
|
||||
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;
|
||||
}
|
||||
}
|
||||
} // extend
|
||||
|
||||
return m_ipo_data->get(time, index, m_next_n-1);
|
||||
} // get
|
@ -47,61 +47,89 @@ public:
|
||||
IPO_MAX};
|
||||
static const std::string m_all_channel_names[IPO_MAX];
|
||||
private:
|
||||
/** The type of this IPO. */
|
||||
IpoChannelType m_channel;
|
||||
/** This object stores the read-only data of an IPO. It might be
|
||||
* shared among several instances of an Ipo (e.g. in a cannon
|
||||
* animation). */
|
||||
class IpoData
|
||||
{
|
||||
public:
|
||||
/** The type of this IPO. */
|
||||
IpoChannelType m_channel;
|
||||
|
||||
/** The three interpolations defined by blender. */
|
||||
enum {IP_CONST, IP_LINEAR, IP_BEZIER} m_interpolation;
|
||||
/** The four extend types. */
|
||||
enum {ET_CONST, ET_EXTRAP, ET_CYCLIC_EXTRAP, ET_CYCLIC} m_extend;
|
||||
/** The three interpolations defined by blender. */
|
||||
enum {IP_CONST, IP_LINEAR, IP_BEZIER} m_interpolation;
|
||||
/** The four extend types. */
|
||||
enum {ET_CONST, ET_EXTRAP, ET_CYCLIC_EXTRAP, ET_CYCLIC} m_extend;
|
||||
|
||||
/** The actual control points. */
|
||||
std::vector<Vec3> m_points;
|
||||
/** The actual control points. */
|
||||
std::vector<Vec3> m_points;
|
||||
|
||||
/** Only used for bezier curves: the two handles. */
|
||||
std::vector<Vec3> m_handle1, m_handle2;
|
||||
/** Only used for bezier curves: the two handles. */
|
||||
std::vector<Vec3> m_handle1, m_handle2;
|
||||
|
||||
/** Frames per second for this animation. */
|
||||
float m_fps;
|
||||
/** Time of the first control point. */
|
||||
float m_start_time;
|
||||
|
||||
/** Time of the first control point. */
|
||||
float m_start_time;
|
||||
/** Time of the last control point. */
|
||||
float m_end_time;
|
||||
|
||||
/** Time of the last control point. */
|
||||
float m_end_time;
|
||||
/** Stores the inital position of the object. */
|
||||
Vec3 m_initial_xyz;
|
||||
|
||||
/** Stores the inital rotation of the object. */
|
||||
Vec3 m_initial_hpr;
|
||||
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);
|
||||
public:
|
||||
IpoData(const XMLNode &curve, float fps);
|
||||
void readCurve(const XMLNode &node);
|
||||
void readIPO(const XMLNode &node, float fps);
|
||||
float approximateLength(float t0, float t1,
|
||||
const Vec3 &p0, const Vec3 &p1,
|
||||
const Vec3 &h1, const Vec3 &h2);
|
||||
float adjustTime(float time);
|
||||
float get(float time, unsigned int index, unsigned int n);
|
||||
|
||||
}; // IpoData
|
||||
// ------------------------------------------------------------------------
|
||||
/** The actual data of the IPO. This can be shared between Ipo (e.g. each
|
||||
* cannon animation will use the same IpoData block, but its own instance
|
||||
* of Ipo, since data like m_next_n should not be shared). */
|
||||
IpoData *m_ipo_data;
|
||||
|
||||
/** True if m_ipo_data is 'owned' by this object and therefore needs to be
|
||||
* freed. If an Ipo is cloned, it will share a reference to m_ipo_data,
|
||||
* and must therefore not free it. */
|
||||
bool m_own_ipo_data;
|
||||
|
||||
/** 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). */
|
||||
* 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. */
|
||||
Vec3 m_initial_xyz;
|
||||
|
||||
/** Stores the inital rotation of the object. */
|
||||
Vec3 m_initial_hpr;
|
||||
|
||||
void extend(float x, unsigned int n);
|
||||
float getCubicBezier(float t, float p0, float p1,
|
||||
float p2, float p3) const;
|
||||
Ipo(const Ipo *ipo);
|
||||
public:
|
||||
Ipo(const XMLNode &curve, float fps);
|
||||
void update(float time, Vec3 *xyz, Vec3 *hpr, Vec3 *scale);
|
||||
float get(float time) const;
|
||||
void setInitialTransform(const Vec3 &xyz, const Vec3 &hpr);
|
||||
void reset();
|
||||
|
||||
void extendStart(float x);
|
||||
void extendEnd(float x);
|
||||
Ipo(const XMLNode &curve, float fps=25);
|
||||
virtual ~Ipo();
|
||||
Ipo *clone();
|
||||
void update(float time, Vec3 *xyz, Vec3 *hpr, Vec3 *scale);
|
||||
float get(float time, unsigned int index) const;
|
||||
void setInitialTransform(const Vec3 &xyz, const Vec3 &hpr);
|
||||
void reset();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the raw data points for this IPO. */
|
||||
const std::vector<Vec3>& getPoints() const { return m_points; }
|
||||
const std::vector<Vec3>& getPoints() const { return m_ipo_data->m_points; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the last specified time (i.e. not considering any extend
|
||||
* types). */
|
||||
float getEndTime() const { return m_end_time; }
|
||||
float getEndTime() const { return m_ipo_data->m_end_time; }
|
||||
}; // Ipo
|
||||
|
||||
#endif
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "animations/ipo.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/material.hpp"
|
||||
#include "graphics/material_manager.hpp"
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "karts/abstract_kart_animation.hpp"
|
||||
#include "modes/world.hpp"
|
||||
|
||||
AbstractKartAnimation::AbstractKartAnimation(AbstractKart *kart,
|
||||
const std::string &name)
|
||||
@ -42,8 +43,24 @@ AbstractKartAnimation::AbstractKartAnimation(AbstractKart *kart,
|
||||
// Register this animation with the kart (which will free it
|
||||
// later).
|
||||
kart->setKartAnimation(this);
|
||||
World::getWorld()->getPhysics()->removeKart(m_kart);
|
||||
} // AbstractKartAnimation
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
AbstractKartAnimation::~AbstractKartAnimation()
|
||||
{
|
||||
// If m_timer >=0, this object is deleted because the kart
|
||||
// is deleted (at the end of a race), which means that
|
||||
// world is in the process of being deleted. In this case
|
||||
// we can't call getPhysics() anymore.
|
||||
if(m_timer < 0)
|
||||
{
|
||||
//m_kart->getBody()->setLinearVelocity(btVector3(0,0,0));
|
||||
m_kart->getBody()->setAngularVelocity(btVector3(0,0,0));
|
||||
World::getWorld()->getPhysics()->addKart(m_kart);
|
||||
}
|
||||
} // ~AbstractKartAnimation
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Updates the timer, and if it expires (<0), the kart animation will be
|
||||
* removed from the kart and this object will be deleted.
|
||||
|
@ -50,7 +50,7 @@ protected:
|
||||
public:
|
||||
AbstractKartAnimation(AbstractKart *kart,
|
||||
const std::string &name);
|
||||
virtual ~AbstractKartAnimation() {}
|
||||
virtual ~AbstractKartAnimation();
|
||||
virtual void update(float dt);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual float getAnimationTimer() const { return m_timer; }
|
||||
|
@ -19,19 +19,42 @@
|
||||
#include "karts/cannon_animation.hpp"
|
||||
|
||||
#include "animations/animation_base.hpp"
|
||||
#include "animations/ipo.hpp"
|
||||
#include "animations/three_d_animation.hpp"
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "modes/world.hpp"
|
||||
|
||||
#include "LinearMath/btTransform.h"
|
||||
|
||||
CannonAnimation::CannonAnimation(AbstractKart *kart, AnimationBase *ab)
|
||||
CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo,
|
||||
const Vec3 &delta)
|
||||
: AbstractKartAnimation(kart, "CannonAnimation")
|
||||
{
|
||||
m_curve = new AnimationBase(ipo);
|
||||
m_timer = ipo->getEndTime();
|
||||
Vec3 xyz = m_kart->getXYZ();
|
||||
Vec3 hpr, scale;
|
||||
// Get the curve position at t=0
|
||||
m_curve->update(0, &xyz, &hpr, &scale);
|
||||
m_offset = m_kart->getXYZ() - xyz-delta;
|
||||
m_delta = delta;
|
||||
} // CannonAnimation
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
CannonAnimation::~CannonAnimation()
|
||||
{
|
||||
delete m_curve;
|
||||
float epsilon = 0.5f * m_kart->getKartHeight();
|
||||
|
||||
btTransform pos;
|
||||
pos.setOrigin(m_kart->getXYZ()+btVector3(0, m_kart->getKartHeight() + epsilon,
|
||||
0));
|
||||
pos.setRotation(btQuaternion(btVector3(0.0f, 1.0f, 0.0f), m_kart->getHeading()));
|
||||
|
||||
m_kart->getBody()->setCenterOfMassTransform(pos);
|
||||
Vec3 v(0, 0, m_kart->getKartProperties()->getMaxSpeed());
|
||||
m_kart->setVelocity(pos.getBasis()*v);
|
||||
} // ~CannonAnimation
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -41,5 +64,23 @@ CannonAnimation::~CannonAnimation()
|
||||
*/
|
||||
void CannonAnimation::update(float dt)
|
||||
{
|
||||
if(m_timer < dt)
|
||||
{
|
||||
AbstractKartAnimation::update(dt);
|
||||
return;
|
||||
}
|
||||
Vec3 xyz = m_kart->getXYZ();
|
||||
core::vector3df old_xyz = xyz.toIrrVector();
|
||||
Vec3 hpr, scale;
|
||||
m_curve->update(dt, &xyz, &hpr, &scale);
|
||||
|
||||
Vec3 rotated_delta = m_kart->getTrans().getBasis()*m_delta;
|
||||
rotated_delta = Vec3(0,0,0);
|
||||
Vec3 new_xyz = xyz+rotated_delta+m_offset;
|
||||
m_kart->setXYZ(new_xyz);
|
||||
|
||||
core::vector3df rot = (new_xyz.toIrrVector()-old_xyz).getHorizontalAngle();
|
||||
btQuaternion q(Vec3(0,1,0),rot.Y*DEGREE_TO_RAD);
|
||||
m_kart->setRotation(q);
|
||||
AbstractKartAnimation::update(dt);
|
||||
} // update
|
||||
|
@ -29,17 +29,26 @@
|
||||
|
||||
class AbstractKart;
|
||||
class AnimationBase;
|
||||
class Ipo;
|
||||
|
||||
class CannonAnimation: public AbstractKartAnimation
|
||||
{
|
||||
protected:
|
||||
/** The offset between the point where the check line was originially
|
||||
* crossed and the origin of the curve. */
|
||||
Vec3 delta;
|
||||
/** The offset between the origin of the curve (relative to which
|
||||
* all points are interpolated) and the position of the kart. */
|
||||
Vec3 m_offset;
|
||||
|
||||
/** An offset that is rotated with the kart and is added to the
|
||||
* interpolated point. This basically shifts the curve (usually)
|
||||
* to the left/right to be aligned with the crossing point of the
|
||||
* kart. */
|
||||
Vec3 m_delta;
|
||||
|
||||
/** Stores the curve interpolation for the cannon. */
|
||||
AnimationBase *m_curve;
|
||||
|
||||
public:
|
||||
CannonAnimation(AbstractKart *kart, AnimationBase *ab);
|
||||
CannonAnimation(AbstractKart *kart, Ipo *ipo, const Vec3 &delta);
|
||||
virtual ~CannonAnimation();
|
||||
virtual void update(float dt);
|
||||
|
||||
|
@ -176,6 +176,10 @@ void DefaultAIController::update(float dt)
|
||||
m_controls->m_look_back = false;
|
||||
m_controls->m_nitro = false;
|
||||
|
||||
// Don't do anything if there is currently a kart animations shown.
|
||||
if(m_kart->getKartAnimation())
|
||||
return;
|
||||
|
||||
// Having a non-moving AI can be useful for debugging, e.g. aiming
|
||||
// or slipstreaming.
|
||||
#undef AI_DOES_NOT_MOVE_FOR_DEBUGGING
|
||||
@ -193,7 +197,7 @@ void DefaultAIController::update(float dt)
|
||||
}
|
||||
|
||||
// If the kart needs to be rescued, do it now (and nothing else)
|
||||
if(isStuck())
|
||||
if(isStuck() && !m_kart->getKartAnimation())
|
||||
{
|
||||
new RescueAnimation(m_kart);
|
||||
AIBaseController::update(dt);
|
||||
|
@ -77,7 +77,6 @@ ExplosionAnimation::ExplosionAnimation(AbstractKart *kart,
|
||||
// the right initial velocity for a kart to land back after
|
||||
// the specified time.
|
||||
m_velocity = 0.5f * m_timer * World::getWorld()->getTrack()->getGravity();
|
||||
World::getWorld()->getPhysics()->removeKart(m_kart);
|
||||
|
||||
m_curr_rotation.setHeading(m_kart->getHeading());
|
||||
m_curr_rotation.setPitch(m_kart->getPitch());
|
||||
@ -114,7 +113,6 @@ ExplosionAnimation::~ExplosionAnimation()
|
||||
{
|
||||
m_kart->getBody()->setLinearVelocity(btVector3(0,0,0));
|
||||
m_kart->getBody()->setAngularVelocity(btVector3(0,0,0));
|
||||
World::getWorld()->getPhysics()->addKart(m_kart);
|
||||
if(m_kart->getCamera() &&
|
||||
m_kart->getCamera()->getMode() != Camera::CM_FINAL)
|
||||
m_kart->getCamera()->setMode(Camera::CM_NORMAL);
|
||||
|
@ -49,8 +49,6 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue)
|
||||
m_add_rotation = -m_curr_rotation/m_timer;
|
||||
m_curr_rotation.setHeading(m_kart->getHeading());
|
||||
|
||||
World::getWorld()->getPhysics()->removeKart(m_kart);
|
||||
|
||||
// Add a hit unless it was auto-rescue
|
||||
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES &&
|
||||
!is_auto_rescue)
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "tracks/check_cannon.hpp"
|
||||
|
||||
#include "animations/animation_base.hpp"
|
||||
#include "animations/ipo.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "karts/cannon_animation.hpp"
|
||||
@ -54,7 +55,7 @@ CheckCannon::CheckCannon(const XMLNode &node, unsigned int index)
|
||||
exit(-1);
|
||||
}
|
||||
m_target.setLine(p1, p2);
|
||||
m_curve = new CannonCurve(node);
|
||||
m_curve = new Ipo(*(node.getNode("curve")));
|
||||
} // CheckCannon
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -68,5 +69,10 @@ void CheckCannon::trigger(unsigned int kart_index)
|
||||
{
|
||||
Vec3 target(m_target.getMiddle());
|
||||
AbstractKart *kart = World::getWorld()->getKart(kart_index);
|
||||
new CannonAnimation(kart, m_curve);
|
||||
if(kart->getKartAnimation()) return;
|
||||
|
||||
const core::vector2df &cross = getCrossPoint();
|
||||
const core::line2df &line = getLine2D();
|
||||
Vec3 delta = Vec3(1,0,0) * (line.start-cross).getLength();
|
||||
new CannonAnimation(kart, m_curve->clone(), delta);
|
||||
} // CheckCannon
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "tracks/check_line.hpp"
|
||||
|
||||
class CheckManager;
|
||||
class Ipo;
|
||||
class XMLNode;
|
||||
|
||||
/**
|
||||
@ -52,7 +53,7 @@ protected:
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Stores the cannon curve data. */
|
||||
CannonCurve *m_curve;
|
||||
Ipo *m_curve;
|
||||
|
||||
public:
|
||||
CheckCannon(const XMLNode &node, unsigned int index);
|
||||
|
@ -132,15 +132,15 @@ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, int indx)
|
||||
bool result=sign!=m_previous_sign[indx];
|
||||
// If the sign has changed, i.e. the infinite line was crossed somewhere,
|
||||
// check if the finite line was actually crossed:
|
||||
core::vector2df out;
|
||||
if(sign!=m_previous_sign[indx] &&
|
||||
m_line.intersectWith(core::line2df(old_pos.toIrrVector2d(),
|
||||
new_pos.toIrrVector2d()), out) )
|
||||
new_pos.toIrrVector2d()),
|
||||
m_cross_point) )
|
||||
{
|
||||
// Now check the minimum height: the kart position must be within a
|
||||
// reasonable distance in the Z axis - 'reasonable' for now to be
|
||||
// between -1 and 4 units (negative numbers are unlikely, but help
|
||||
// in case that there is 'somewhat' inside of the track, or the
|
||||
// in case that the kart is 'somewhat' inside of the track, or the
|
||||
// checklines are a bit off in Z direction.
|
||||
result = new_pos.getY()-m_min_height<m_over_min_height &&
|
||||
new_pos.getY()-m_min_height>-m_under_min_height;
|
||||
|
@ -19,8 +19,9 @@
|
||||
#ifndef HEADER_CHECK_LINE_HPP
|
||||
#define HEADER_CHECK_LINE_HPP
|
||||
|
||||
#include <line2d.h>
|
||||
#include <IMeshSceneNode.h>
|
||||
#include <line2d.h>
|
||||
#include <vector2d.h>
|
||||
using namespace irr;
|
||||
|
||||
#include "tracks/check_structure.hpp"
|
||||
@ -45,6 +46,8 @@ private:
|
||||
/** The line that is tested for being crossed. */
|
||||
core::line2df m_line;
|
||||
|
||||
core::vector2df m_cross_point;
|
||||
|
||||
/** The minimum height of the checkline. */
|
||||
float m_min_height;
|
||||
|
||||
@ -66,11 +69,17 @@ private:
|
||||
public:
|
||||
CheckLine(const XMLNode &node, unsigned int index);
|
||||
virtual ~CheckLine();
|
||||
virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, int indx);
|
||||
virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos,
|
||||
int indx);
|
||||
virtual void reset(const Track &track);
|
||||
virtual void changeDebugColor(bool is_active);
|
||||
/** Returns the actual line data for this checkpoint. */
|
||||
const core::line2df &getLine2D() const {return m_line;}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the 2d point at which the line was crossed. Note that this
|
||||
* value is ONLY valid after isTriggered is called and inside of
|
||||
* trigger(). */
|
||||
const core::vector2df &getCrossPoint() const { return m_cross_point; }
|
||||
}; // CheckLine
|
||||
|
||||
#endif
|
||||
|
@ -48,7 +48,8 @@ CheckStructure::CheckStructure(const XMLNode &node, unsigned int index)
|
||||
m_check_type = CT_TOGGLE;
|
||||
else if(kind=="ambient-light")
|
||||
m_check_type = CT_AMBIENT_SPHERE;
|
||||
else if(kind=="cannon")
|
||||
// Cannons don't have a kind specified, so test for the name in this case
|
||||
else if(node.getName()=="cannon")
|
||||
m_check_type = CT_CANNON;
|
||||
else
|
||||
{
|
||||
|
@ -235,6 +235,14 @@ TrackObject::TrackObject(const core::vector3df& pos, const core::vector3df& hpr,
|
||||
reset();
|
||||
} // TrackObject
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
TrackObject::TrackObject()
|
||||
{
|
||||
m_node = NULL;
|
||||
m_mesh = NULL;
|
||||
m_sound = NULL;
|
||||
} // TrackObject()
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Destructor. Removes the node from the scene graph, and also
|
||||
* drops the textures of the mesh. Sound buffers are also freed.
|
||||
|
@ -95,6 +95,7 @@ protected:
|
||||
|
||||
public:
|
||||
TrackObject(const XMLNode &xml_node);
|
||||
TrackObject();
|
||||
TrackObject(const core::vector3df& pos, const core::vector3df& hpr,
|
||||
const core::vector3df& scale, const std::string& model);
|
||||
~TrackObject();
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "tracks/track_object_manager.hpp"
|
||||
|
||||
#include "animations/ipo.hpp"
|
||||
#include "config/user_config.hpp"
|
||||
#include "animations/billboard_animation.hpp"
|
||||
#include "animations/three_d_animation.hpp"
|
||||
|
@ -56,6 +56,10 @@ public:
|
||||
/** Creates a 3d vector from three scalars. */
|
||||
inline Vec3(float x, float y, float z) : btVector3(x,y,z) {}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Creates a 3d vector from three scalars. */
|
||||
inline Vec3(float x, float y, float z, float w) : btVector3(x,y,z)
|
||||
{ setW(w); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Initialises a 3d vector from one scalar value, which is used to
|
||||
* initialise all components. */
|
||||
inline Vec3(float x) : btVector3(x,x,x) {}
|
||||
|
Loading…
Reference in New Issue
Block a user