Allow the player to go to the left and right when in a cannon.
This commit is contained in:
parent
a7d1a312ad
commit
ae375c50b6
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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_n<m_ipo_data->m_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
|
||||
|
||||
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
|
||||
|
||||
|
@ -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<Vec3>& getPoints() const { return m_ipo_data->m_points; }
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user