diff --git a/data/shaders/grass_pass.vert b/data/shaders/grass_pass.vert index 23daa553c..e6903c19e 100644 --- a/data/shaders/grass_pass.vert +++ b/data/shaders/grass_pass.vert @@ -20,13 +20,16 @@ out vec2 uv; void main() { + vec3 test = sin(windDir * (Position.y* 0.5)) * 0.5; + test += cos(windDir) * 0.7; + mat4 new_model_matrix = ModelMatrix; mat4 new_inverse_model_matrix = InverseModelMatrix; - new_model_matrix[3].xyz += windDir * Color.r; + new_model_matrix[3].xyz += test * Color.r; // FIXME doesn't seem to make too much difference in pass 2, because this // affects "nor" which is later only * 0.1 by scattering - new_inverse_model_matrix[3].xyz -= windDir * Color.r; + new_inverse_model_matrix[3].xyz -= test * Color.r; mat4 ModelViewProjectionMatrix = ProjectionViewMatrix * new_model_matrix; mat4 TransposeInverseModelView = transpose(InverseViewMatrix * new_inverse_model_matrix); diff --git a/data/shaders/instanced_grass.vert b/data/shaders/instanced_grass.vert index 57adee847..0127a8346 100644 --- a/data/shaders/instanced_grass.vert +++ b/data/shaders/instanced_grass.vert @@ -41,8 +41,10 @@ flat out sampler2D thirdhandle; void main() { - mat4 ModelMatrix = getWorldMatrix(Origin + windDir * Color.r, Orientation, Scale); - mat4 TransposeInverseModelView = transpose(getInverseWorldMatrix(Origin + windDir * Color.r, Orientation, Scale) * InverseViewMatrix); + vec3 test = sin(windDir * (Position.y* 0.5)) * 0.5; + test += cos(windDir) * 0.7; + mat4 ModelMatrix = getWorldMatrix(Origin + test * Color.r, Orientation, Scale); + mat4 TransposeInverseModelView = transpose(getInverseWorldMatrix(Origin + test * Color.r, Orientation, Scale) * InverseViewMatrix); gl_Position = ProjectionViewMatrix * ModelMatrix * vec4(Position, 1.); nor = (TransposeInverseModelView * vec4(Normal, 0.)).xyz; uv = Texcoord; diff --git a/data/shaders/instanced_grassshadow.vert b/data/shaders/instanced_grassshadow.vert index 5d5e8e6e2..a31d49d92 100644 --- a/data/shaders/instanced_grassshadow.vert +++ b/data/shaders/instanced_grassshadow.vert @@ -40,17 +40,20 @@ flat out uvec2 hdle; void main(void) { + + vec3 test = sin(windDir * (Position.y* 0.5)) * 0.5; + test += cos(windDir) * 0.7; mat4 ModelMatrix = getWorldMatrix(Origin, Orientation, Scale); #ifdef VSLayer gl_Layer = layer; - gl_Position = ShadowViewProjMatrixes[gl_Layer] * ModelMatrix * vec4(Position + windDir * Color.r, 1.); + gl_Position = ShadowViewProjMatrixes[gl_Layer] * ModelMatrix * vec4(Position + test * Color.r, 1.); uv = Texcoord; #ifdef Use_Bindless_Texture handle = Handle; #endif #else layerId = layer; - gl_Position = ShadowViewProjMatrixes[layerId] * ModelMatrix * vec4(Position + windDir * Color.r, 1.); + gl_Position = ShadowViewProjMatrixes[layerId] * ModelMatrix * vec4(Position + test * Color.r, 1.); tc = Texcoord; #ifdef Use_Bindless_Texture hdle = Handle; diff --git a/data/shaders/shadow_grass.vert b/data/shaders/shadow_grass.vert index 95827b872..bb978f921 100644 --- a/data/shaders/shadow_grass.vert +++ b/data/shaders/shadow_grass.vert @@ -21,13 +21,17 @@ out int layerId; void main(void) { + + vec3 test = sin(windDir * (Position.y* 0.5)) * 0.5; + test += cos(windDir) * 0.7; + #ifdef VSLayer gl_Layer = layer; uv = Texcoord; - gl_Position = ShadowViewProjMatrixes[gl_Layer] * ModelMatrix * vec4(Position + windDir * Color.r, 1.); + gl_Position = ShadowViewProjMatrixes[gl_Layer] * ModelMatrix * vec4(Position + test * Color.r, 1.); #else layerId = layer; tc = Texcoord; - gl_Position = ShadowViewProjMatrixes[layerId] * ModelMatrix * vec4(Position + windDir * Color.r, 1.); + gl_Position = ShadowViewProjMatrixes[layerId] * ModelMatrix * vec4(Position + test * Color.r, 1.); #endif } diff --git a/src/animations/animation_base.cpp b/src/animations/animation_base.cpp index ff10089e0..029273cf7 100644 --- a/src/animations/animation_base.cpp +++ b/src/animations/animation_base.cpp @@ -107,3 +107,36 @@ void AnimationBase::update(float dt, Vec3 *xyz, Vec3 *hpr, Vec3 *scale) } } // update +// ---------------------------------------------------------------------------- +/** Return the time, position and rotation at the specified time. It does not + * update the internal timer as update() does. + * \param dt Time since last call. + * \param xyz Position to be updated. + * \param hpr Rotation to be updated. + */ +void AnimationBase::getAt(float time, Vec3 *xyz, Vec3 *hpr, Vec3 *scale) +{ + assert(!std::isnan(time)); + + // Don't do anything if the animation is disabled + if (!m_playing) return; + + for_var_in(Ipo*, curr, m_all_ipos) + { + curr->update(time, xyz, hpr, scale); + } +} // getAt + +// ---------------------------------------------------------------------------- +/** Returns the derivative at the specified point. + * \param time The time for which to determine the derivative. + * \param xyz Float pointer to store the result. + */ +void AnimationBase::getDerivativeAt(float time, Vec3 *xyz) +{ + for_var_in(Ipo*, curr, m_all_ipos) + { + curr->getDerivative(time, xyz); + } + xyz->normalize(); +} diff --git a/src/animations/animation_base.hpp b/src/animations/animation_base.hpp index 98e30f4ca..8f5430542 100644 --- a/src/animations/animation_base.hpp +++ b/src/animations/animation_base.hpp @@ -67,8 +67,11 @@ public: AnimationBase(const XMLNode &node); AnimationBase(Ipo *ipo); virtual ~AnimationBase() {} - virtual void update(float dt, Vec3 *xyz=NULL, Vec3 *hpr=NULL, - Vec3 *scale=NULL); + virtual void update(float dt, Vec3 *xyz=NULL, Vec3 *hpr=NULL, + Vec3 *scale=NULL); + virtual void getAt(float time, Vec3 *xyz = NULL, Vec3 *hpr = NULL, + Vec3 *scale = NULL); + virtual void getDerivativeAt(float time, Vec3 *xyz); /** This needs to be implemented by the inheriting classes. It is called * once per frame from the track. It has a dummy implementation that * just asserts so that this class can be instantiated in diff --git a/src/animations/ipo.cpp b/src/animations/ipo.cpp index 4499592af..faa765ef0 100644 --- a/src/animations/ipo.cpp +++ b/src/animations/ipo.cpp @@ -228,7 +228,7 @@ void Ipo::IpoData::approximateBezier(float t0, float t1, // A more sophisticated estimation might be useful (e.g. taking the // difference between a linear approximation and the actual bezier // curve into accound. - if(distance<=2.0f) + if(distance<=0.2f) return; // Insert one point at (t0+t1)/2. First split the left part of @@ -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,6 +361,64 @@ 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; +} // getCubicBezier + +// ---------------------------------------------------------------------------- +/** 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. + */ +float Ipo::IpoData::getDerivative(float time, unsigned int index, + unsigned int n) +{ + switch (m_interpolation) + { + 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 + + +// ---------------------------------------------------------------------------- +/** 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 // ============================================================================ @@ -471,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). @@ -484,18 +564,50 @@ float Ipo::get(float time, unsigned int index) const if(m_next_n==0) return m_ipo_data->m_points[0][index]; - time = m_ipo_data->adjustTime(time); + updateNextN(time); - // Time was reset since the last cached value for n, - // reset n to start from the beginning again. - if(time < m_ipo_data->m_points[m_next_n-1].getW()) - m_next_n = 1; - // Search for the first point in the (sorted) array which is greater or equal - // to the current time. - while(m_next_nm_points.size()-1 && - time >=m_ipo_data->m_points[m_next_n].getW()) - m_next_n++; float rval = m_ipo_data->get(time, index, m_next_n-1); assert(!std::isnan(rval)); return rval; } // get + +// ---------------------------------------------------------------------------- +/** 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. + */ +void Ipo::getDerivative(float time, Vec3 *xyz) +{ + // Avoid crash in case that only one point is given for this IPO. + if (m_next_n == 0) + { + // Derivative has no real meaning in case of a single point. + // So just return a dummy value. + xyz->setValue(1, 0, 0); + return; + } + + updateNextN(time); + switch (m_ipo_data->m_channel) + { + case Ipo::IPO_LOCX: xyz->setX(m_ipo_data->getDerivative(time, m_next_n, 0)); break; + case Ipo::IPO_LOCY: xyz->setY(m_ipo_data->getDerivative(time, m_next_n, 0)); break; + case Ipo::IPO_LOCZ: xyz->setZ(m_ipo_data->getDerivative(time, m_next_n, 0)); break; + case Ipo::IPO_LOCXYZ: + { + if (xyz) + { + for (unsigned int j = 0; j < 3; j++) + (*xyz)[j] = m_ipo_data->getDerivative(time, j, m_next_n-1); + } + break; + } + default: Log::warn("IPO", "Unexpected channel %d for derivate.", + m_ipo_data->m_channel ); + xyz->setValue(1, 0, 0); + break; + } // switch + + +} // getDerivative + diff --git a/src/animations/ipo.hpp b/src/animations/ipo.hpp index fadff2028..f1b83a3e9 100644 --- a/src/animations/ipo.hpp +++ b/src/animations/ipo.hpp @@ -81,10 +81,12 @@ private: private: float getCubicBezier(float t, float p0, float p1, float p2, float p3) const; - void approximateBezier(float t0, float t1, - const Vec3 &p0, const Vec3 &p1, - const Vec3 &h0, const Vec3 &h2, - unsigned int rec_level = 0); + 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, + unsigned int rec_level = 0); public: IpoData(const XMLNode &curve, float fps, bool reverse); void readCurve(const XMLNode &node, bool reverse); @@ -94,6 +96,7 @@ private: const Vec3 &h1, const Vec3 &h2); float adjustTime(float time); float get(float time, unsigned int index, unsigned int n); + float getDerivative(float time, unsigned int index, unsigned int n); }; // IpoData // ------------------------------------------------------------------------ @@ -113,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); @@ -120,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(); - // ------------------------------------------------------------------------ /** Returns the raw data points for this IPO. */ const std::vector& getPoints() const { return m_ipo_data->m_points; } diff --git a/src/graphics/particle_emitter.hpp b/src/graphics/particle_emitter.hpp index 576d4a3d2..a5395467d 100644 --- a/src/graphics/particle_emitter.hpp +++ b/src/graphics/particle_emitter.hpp @@ -92,6 +92,7 @@ public: void setCreationRateAbsolute(float fraction); void setCreationRateRelative(float f); int getCreationRate(); + float getCreationRateFloat() {return m_min_rate;} void setPosition(const Vec3 &pos); void setRotation(const Vec3 &rot); diff --git a/src/graphics/stk_mesh.cpp b/src/graphics/stk_mesh.cpp index 397dd8b77..87e2e2b29 100644 --- a/src/graphics/stk_mesh.cpp +++ b/src/graphics/stk_mesh.cpp @@ -337,7 +337,7 @@ core::vector3df getWindDir() const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; GrassShaderProvider *gsp = (GrassShaderProvider *)Shaders::getCallback(ES_GRASS); - return (gsp->getSpeed() * cos(time)) * vector3df(1., 0., 0.); + return (gsp->getSpeed() * time * vector3df(1., 0., 0.)); } // getWindDir // ---------------------------------------------------------------------------- diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index 39ce1a933..6fe26ead9 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -27,30 +27,123 @@ #include "LinearMath/btTransform.h" -CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo) - : AbstractKartAnimation(kart, "CannonAnimation") +/** The constructor for the cannon animation. + * \param kart The kart to be animated. + * \param ipo The IPO (blender interpolation curve) which the kart + * should follow. + * \param start_left, start_right: Left and right end points of the line + * that the kart just crossed. + * \param end_left, end_right: Left and right end points of the line at + * which the kart finishes. + * \param skid_rot Visual rotation of the kart due to skidding (while this + * value can be queried, the AbstractkartAnimation constructor + * resets the value to 0, so it needs to be passed in. + */ +CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, + const Vec3 &start_left, const Vec3 &start_right, + const Vec3 &end_left, const Vec3 &end_right, + float skid_rot) + : AbstractKartAnimation(kart, "CannonAnimation") { m_curve = new AnimationBase(ipo); m_timer = ipo->getEndTime(); + + // First make sure that left and right points are indeed correct + // ------------------------------------------------------------- + Vec3 my_start_left = start_left; + Vec3 my_start_right = start_right; + Vec3 p0, p1; + // Define a plane that goes through the middle of the start line + // (the curve's origin must be in the middle of the line. + m_curve->getAt(0, &p0); + m_curve->getAt(0.1f, &p1); + Vec3 p2 = 0.5f*(p0 + p1) + m_kart->getNormal(); + if (start_left.sideofPlane(p0, p1, p2) < 0) + { + // Left and right start line needs to be swapped + my_start_left = start_right; + my_start_right = start_left; + } + // First adjust start and end points to take on each side half the kart + // width into account: + Vec3 direction = my_start_right - my_start_left; + direction.normalize(); + + float kw = m_kart->getKartModel()->getWidth(); + Vec3 adj_start_left = my_start_left + (0.5f*kw) * direction; + Vec3 adj_start_right = my_start_right - (0.5f*kw) * direction; + + // Store the length of the start and end line, which is used + // during update() to adjust the distance to center + m_start_line_length = (adj_start_left - adj_start_right).length(); + m_end_line_length = (end_left - end_right).length() - kw; + + // The current kart position is divided into three components: + // kart.xyz = curve.xyz + parallel_to_start_line_component + rest + // 1) curve.xyz: The point at the curve at t=0. + // 2) parallel_to_start_line_component: + // A component parallel to the start line. This component is scaled + // depending on time and length of start- and end-line (e.g. if the + // end line is twice as long as the start line, this will make sure + // that a kart starting at the very left of the start line will end + // up at the very left of the end line). This component can also be + // 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) rest: The amoount that the kart is ahead and above the + // start line. This is stored in m_delta and will be added to the + // newly computed curve xyz coordinates. + // // Compute the delta between the kart position and the start of the curve. // This delta is rotated with the kart and added to the interpolated curve // position to get the actual kart position during the animation. - m_curve->update(0, &m_previous_orig_xyz); - m_delta = kart->getXYZ() - m_previous_orig_xyz; + Vec3 curve_xyz; + m_curve->update(0, &curve_xyz); + m_delta = kart->getXYZ() - curve_xyz; + + // Compute on which fraction of the start line the kart is, to get the + // second component of the kart position: distance along start line + Vec3 v = adj_start_left - adj_start_right; + float l = v.length(); + v /= l; + + 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] - 0 in case of left side, 1 if the kart is at the + // very right. Convert this to [-1,1] assuming that the ipo for the + // cannon is in the middle of the start and end line + m_fraction_of_line = 2.0f*f - 1.0f; + + Vec3 delta = 0.5f*m_fraction_of_line * (adj_start_right - adj_start_left); + // Subtract the horizontal difference, to get the constant offset the + // kart has from the curve. + m_delta = m_delta - delta; + + // Compute the original heading of the kart. At the end of the cannon, + // the kart should be parallel to the curve, but at the beginning it + // the kart should be parallel to the curve and facing forwards, but + // at the beginning it might not be. The initial rotation between the + // tangent of the curce and the kart is stored as a m_delta_heading, + // which will be applied to the curve orientation in update, but reduced + // over time till it becomes 0 at the end of the curve. The effect is that + // initially (t=0) the kart will keep its (non-orhtogonal) rotation, + // but smoothly this will adjusted until at the end the kart will be + // facing forwards again. + Vec3 tangent; + m_curve->getDerivativeAt(0, &tangent); + // Get the current kart orientation + Vec3 forward = m_kart->getTrans().getBasis().getColumn(2); + Vec3 v1(tangent), v2(forward); + v1.setY(0); v2.setY(0); + m_delta_heading = shortestArcQuatNormalize2(v1, v2) + * btQuaternion(Vec3(0,1,0), skid_rot); - // 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); - core::vector3df rot = (m_previous_orig_xyz-xyz1).toIrrVector() - .getHorizontalAngle(); - btQuaternion q(Vec3(0,1,0),rot.Y*DEGREE_TO_RAD); - btMatrix3x3 m(q); - m_delta = m * m_delta; // The previous call to m_curve->update will set the internal timer // of the curve to dt. Reset it to 0 to make sure the timer is in @@ -63,11 +156,7 @@ CannonAnimation::~CannonAnimation() { delete m_curve; - btTransform pos; - pos.setOrigin(m_kart->getXYZ()); - pos.setRotation(btQuaternion(btVector3(0.0f, 1.0f, 0.0f), - m_kart->getHeading() )); - + btTransform pos = m_kart->getTrans(); m_kart->getBody()->setCenterOfMassTransform(pos); Vec3 v(0, 0, m_kart->getKartProperties()->getEngineMaxSpeed()); m_kart->setVelocity(pos.getBasis()*v); @@ -86,26 +175,77 @@ void CannonAnimation::update(float dt) return; } - Vec3 xyz; - m_curve->update(dt, &xyz); + // First compute the current rotation + // ================================== + // Get the tangent = derivative at the current point to compute the + // new orientation of the kart + Vec3 tangent; + m_curve->getDerivativeAt(m_curve->getAnimationDuration() - m_timer, + &tangent); + // Get the current kart orientation + Vec3 forward = m_kart->getTrans().getBasis().getColumn(2); + + // Heading + // ------- + // I tried to also adjust pitch at the same time, but that adds a strong + // roll to the kart on some cannons while it is in the air (caused by + // the rotation axis returned shortestArc not being orthogonal to the + // up vector). + Vec3 v1(tangent), v2(forward); + v1.setY(0); v2.setY(0); + btQuaternion heading = shortestArcQuatNormalize2(v2, v1); - // 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; + // Align to up-vector + // ------------------ + // While start and end line have to have the same 'up' vector, karts can + // sometimes be not parallel to them. So slowly adjust this over time + Vec3 up = m_kart->getTrans().getBasis().getColumn(1); + up.normalize(); + Vec3 gravity = -m_kart->getBody()->getGravity(); + gravity.normalize(); + // Adjust only 5% towards the real up vector. This will smoothly + // adjust the kart while the kart is in the air + Vec3 target_up_vector = (gravity*0.05f + up*0.95f).normalize(); + btQuaternion q_up = shortestArcQuat(up, target_up_vector); - Vec3 rotated_delta = m_kart->getTrans().getBasis()*m_delta; - m_kart->setXYZ(xyz + rotated_delta); + // Additional kart rotation + // ------------------------ + // Apply any additional rotation the kart had when crossing the start + // line. This rotation will be reduced the closer the kart gets to + // the end line, with the result that at the start line the kart will + // be not rotated at all (so the visuals from physics to cannon will + // be smoothed), and at the end line the kart will face in the + // forward direction. + + // The timer counts backwards, so the fraction goes from 1 to 0 + float f = m_timer / m_curve->getAnimationDuration(); + + btQuaternion zero(gravity, 0); + btQuaternion current_delta_heading = zero.slerp(m_delta_heading, f); + + btQuaternion all_heading = m_kart->getRotation()*current_delta_heading*heading; + + m_kart->setRotation(q_up * all_heading); + + + // Then compute the new location of the kart + // ----------------------------------------- + float f_current_width = m_start_line_length * f + + m_end_line_length * (1.0f - f); + + // Adjust the horizontal location based on steering + m_fraction_of_line += m_kart->getSteerPercent()*dt*2.0f; + btClamp(m_fraction_of_line, -1.0f, 1.0f); + + // horiz_delta is in kart coordinates, the rotation by q will + // transform it to the global coordinate system + Vec3 horiz_delta = Vec3(0.5f*m_fraction_of_line * f_current_width, 0, 0); + + Vec3 rotated_delta = quatRotate(all_heading, m_delta + horiz_delta); + + Vec3 curve_xyz; + m_curve->update(dt, &curve_xyz); + m_kart->setXYZ(curve_xyz+rotated_delta); AbstractKartAnimation::update(dt); } // update diff --git a/src/karts/cannon_animation.hpp b/src/karts/cannon_animation.hpp index bb8022c25..f36098d32 100644 --- a/src/karts/cannon_animation.hpp +++ b/src/karts/cannon_animation.hpp @@ -22,15 +22,18 @@ #include "karts/abstract_kart_animation.hpp" #include "utils/vec3.hpp" -/** This animation shoots the kart to a specified point on the track. - * - * \ingroup karts - */ +#include "LinearMath/btQuaternion.h" class AbstractKart; class AnimationBase; class Ipo; + +/** This animation shoots the kart to a specified point on the track. + * + * \ingroup karts + */ + class CannonAnimation: public AbstractKartAnimation { protected: @@ -43,12 +46,26 @@ protected: /** 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; + /** Length of the (adjusted, i.e. taking kart width into account) + * start line. */ + float m_start_line_length; + /** Length of the (adjusted, i.e. taking kart width into account) + * end line. */ + float m_end_line_length; + + /** Stores the position of the kart relative to the line width + * at the current location. */ + float m_fraction_of_line; + + /** The initial heading of the kart when crossing the line. This is + * used to smoothly orient the kart towards the normal of the cuve. */ + btQuaternion m_delta_heading; public: - CannonAnimation(AbstractKart *kart, Ipo *ipo); + CannonAnimation(AbstractKart *kart, Ipo *ipo, + const Vec3 &start_left, const Vec3 &start_right, + const Vec3 &end_left, const Vec3 &end_right, + float skid_rot); virtual ~CannonAnimation(); virtual void update(float dt); diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index dc5ae15c7..f8e4972d4 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -153,6 +153,7 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id, m_reset_transform = init_transform; m_speed = 0.0f; m_smoothed_speed = 0.0f; + m_last_factor_engine_sound = 0.0f; m_kart_model->setKart(this); @@ -1225,7 +1226,19 @@ void Kart::update(float dt) } } - // Update the position and other data taken from the physics + // This is to avoid a rescue immediately after an explosion + const bool has_animation_before = m_kart_animation != NULL; + // A kart animation can change the xyz position. This needs to be done + // before updating the graphical position (which is done in + // Moveable::update() ), otherwise 'stuttering' can happen (caused by + // graphical and physical position not being the same). + if (has_animation_before) + { + m_kart_animation->update(dt); + } + // Update the position and other data taken from the physics (or + // an animation which calls setXYZ(), which also updates the kart + // physical position). Moveable::update(dt); Vec3 front(0, 0, getKartLength()*0.5f); @@ -1309,9 +1322,6 @@ void Kart::update(float dt) // Used to prevent creating a rescue animation after an explosion animation // got deleted - const bool has_animation_before = m_kart_animation!= NULL; - if(has_animation_before) - m_kart_animation->update(dt); m_attachment->update(dt); @@ -1375,6 +1385,7 @@ void Kart::update(float dt) fabs(getSpeed()) < 3.0f) { new RescueAnimation(this, /*is_auto_rescue*/true); + m_last_factor_engine_sound = 0.0f; } } @@ -1439,7 +1450,10 @@ void Kart::update(float dt) if((min->getY() - getXYZ().getY() > 17 || dist_to_sector > 25) && !m_flying && !getKartAnimation()) + { new RescueAnimation(this); + m_last_factor_engine_sound = 0.0f; + } } else { @@ -1458,7 +1472,10 @@ void Kart::update(float dt) } // if !flying handleMaterialSFX(material); if (material->isDriveReset() && isOnGround()) + { new RescueAnimation(this); + m_last_factor_engine_sound = 0.0f; + } else if(material->isZipper() && isOnGround()) { handleZipper(material); @@ -2094,6 +2111,7 @@ void Kart::crashed(const Material *m, const Vec3 &normal) if (m->getCollisionReaction() == Material::RESCUE) { new RescueAnimation(this); + m_last_factor_engine_sound = 0.0f; } else if (m->getCollisionReaction() == Material::PUSH_BACK) { @@ -2285,7 +2303,7 @@ void Kart::updatePhysics(float dt) m_max_speed->update(dt); - updateEngineSFX(); + updateEngineSFX(dt); #ifdef XX Log::info("Kart","angVel %f %f %f heading %f suspension %f %f %f %f" ,m_body->getAngularVelocity().getX() @@ -2304,7 +2322,7 @@ void Kart::updatePhysics(float dt) //----------------------------------------------------------------------------- /** Adjust the engine sound effect depending on the speed of the kart. */ -void Kart::updateEngineSFX() +void Kart::updateEngineSFX(float dt) { // when going faster, use higher pitch for engine if(!m_engine_sound || !SFXManager::get()->sfxAllowed()) @@ -2312,7 +2330,7 @@ void Kart::updateEngineSFX() if(isOnGround()) { - float max_speed = m_max_speed->getCurrentMaxSpeed(); + float max_speed = m_kart_properties->getEngineMaxSpeed(); // Engine noise is based half in total speed, half in fake gears: // With a sawtooth graph like /|/|/| we get 3 even spaced gears, @@ -2320,20 +2338,23 @@ void Kart::updateEngineSFX() // good enough brrrBRRRbrrrBRRR sound effect. Speed factor makes // it a "staired sawtooth", so more acoustically rich. float f = max_speed > 0 ? m_speed/max_speed : 1.0f; - // Speed at this stage is not yet capped, so it can be > 1, which - // results in odd engine sfx. - if (f>1.0f) f=1.0f; + // Speed at this stage is not yet capped, reduce the amount beyond 1 + if (f> 1.0f) f = 1.0f + (1.0f-1.0f/f); - float gears = 3.0f * fmod(f, 0.333334f); + float fc = f; + if (fc>1.0f) fc = 1.0f; + float gears = 3.0f * fmod(fc, 0.333334f); assert(!std::isnan(f)); - m_engine_sound->setSpeedPosition(0.6f + (f + gears) * 0.35f, getXYZ()); + m_last_factor_engine_sound = (0.9*f + gears) * 0.35f; + m_engine_sound->setSpeedPosition(0.6f + m_last_factor_engine_sound, getXYZ()); } else - { - // When flying, fixed value but not too high pitch - // This gives some variation (vs previous "on wheels" one) - m_engine_sound->setSpeedPosition(0.9f, getXYZ()); - } + { + // When flying, reduce progressively the sound engine (since we can't accelerate) + m_last_factor_engine_sound *= (1.0f-0.1*dt); + m_engine_sound->setSpeedPosition(0.6f + m_last_factor_engine_sound, getXYZ()); + if (m_speed < 0.1f) m_last_factor_engine_sound = 0.0f; + } } // updateEngineSFX //----------------------------------------------------------------------------- diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index aa3a7a87f..63c1bf6ed 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -201,6 +201,9 @@ protected: /** For camera handling an exponentially smoothened value is used, which * reduces stuttering of the camera. */ float m_smoothed_speed; + + /** For smoothing engine sound**/ + float m_last_factor_engine_sound; std::vector m_custom_sounds; SFXBase *m_beep_sound; @@ -226,7 +229,7 @@ protected: void updateFlying(); void updateSliding(); void updateEnginePowerAndBrakes(float dt); - void updateEngineSFX(); + void updateEngineSFX(float dt); void updateSpeed(); void updateNitro(float dt); float getActualWheelForce(); diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index 8ab878025..5d44de431 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -38,6 +38,14 @@ KartGFX::KartGFX(const AbstractKart *kart, RaceManager::KartType type, bool is_day) { + m_nitro_light = NULL; + m_skidding_light_1 = NULL; + m_skidding_light_2 = NULL; + m_head_light = NULL; + m_kart = kart; + m_wheel_toggle = 0; + m_skid_level = 0; + //if(!UserConfigParams::m_graphical_effects) //{ // for(unsigned int i=0; igetKartModel(); const float length = km->getLength(); scene::ISceneNode *node = m_kart->getNode(); // Create nitro light core::vector3df location(0.0f, 0.5f, -0.5f*length - 0.05f); -#ifndef SERVER_ONLY - m_nitro_light = irr_driver->addLight(location, /*force*/ 0.4f, - /*radius*/CVS->isGLSL() ? 5.0f : 1.0f, - 0.0f, 0.4f, 1.0f, - false, node); - m_nitro_light->setVisible(false); -#ifdef DEBUG - m_nitro_light->setName( ("nitro emitter (" + m_kart->getIdent() - + ")").c_str() ); -#endif - - // Create skidding lights - // For the first skidding level - m_skidding_light_1 = - irr_driver->addLight(core::vector3df(0.0f, 0.1f, -0.5f*length - 0.05f), - /* force */ 0.3f, - /*radius*/CVS->isGLSL() ? 3.0f : 1.0f, - 1.0f, 0.6f, 0.0f, false, node); - m_skidding_light_1->setVisible(false); - m_skidding_light_1->setName( ("skidding emitter 1 (" + m_kart->getIdent() - + ")").c_str() ); - - // For the second skidding level - m_skidding_light_2 = - irr_driver->addLight(core::vector3df(0.0f, 0.1f, -0.5f*length - 0.05f), - /* force */0.4f, - /*radius*/CVS->isGLSL() ? 4.0f : 1.0f, - 1.0f, 0.0f, 0.0f, false, node); - m_skidding_light_2->setVisible(false); - m_skidding_light_2->setName( ("skidding emitter 2 (" + m_kart->getIdent() - + ")").c_str() ); - - m_head_light = - irr_driver->addLight(core::vector3df(0.0f, 0.2f, 1.5f*length), - /* force */ 0.5f, - /*radius*/CVS->isGLSL() ? 5.0f : 1.0f, - 1.0f, 1.0f, 1.0f, false, node); - m_head_light->setName( ("head light " + m_kart->getIdent() - + ")").c_str() ); - - if (type == RaceManager::KT_PLAYER && !is_day) - { - m_head_light->setVisible(true); - } - else - { - m_head_light->setVisible(false); - } - - -#endif - #ifndef SERVER_ONLY if (CVS->isGLSL()) { + m_nitro_light = irr_driver->addLight(location, /*force*/ 0.4f, + /*radius*/ 5.0f, 0.0f, 0.4f, 1.0f, + false, node); + m_nitro_light->setVisible(false); + #ifdef DEBUG + m_nitro_light->setName( ("nitro emitter (" + m_kart->getIdent() + + ")").c_str() ); + #endif + + // Create skidding lights + // For the first skidding level + m_skidding_light_1 = + irr_driver->addLight(core::vector3df(0.0f, 0.1f, -0.5f * length - + 0.05f), /* force */ 0.3f, /*radius*/ 3.0f, + 1.0f, 0.6f, 0.0f, false, node); + m_skidding_light_1->setVisible(false); + m_skidding_light_1->setName(("skidding emitter 1 (" + m_kart->getIdent() + + ")").c_str() ); + + // For the second skidding level + m_skidding_light_2 = + irr_driver->addLight(core::vector3df(0.0f, 0.1f, -0.5f * length - + 0.05f), /* force */0.4f, /*radius*/4.0f, + 1.0f, 0.0f, 0.0f, false, node); + m_skidding_light_2->setVisible(false); + m_skidding_light_2->setName(("skidding emitter 2 (" + m_kart->getIdent() + + ")").c_str() ); + + m_head_light = + irr_driver->addLight(core::vector3df(0.0f, 0.2f, 1.5f * length), + /* force */ 0.5f, /*radius*/5.0f, 1.0f, 1.0f, + 1.0f, false, node); + m_head_light->setName( ("head light " + m_kart->getIdent() + + ")").c_str() ); + + m_head_light->setVisible(type == RaceManager::KT_PLAYER && !is_day); + m_nitro_light->grab(); m_skidding_light_1->grab(); m_skidding_light_2->grab(); @@ -182,7 +173,7 @@ void KartGFX::addEffect(KartGFXType type, const std::string &file_name, const Vec3 &position, bool important) { #ifndef SERVER_ONLY - if (!UserConfigParams::m_graphical_effects && + if ((!UserConfigParams::m_graphical_effects || !CVS->isGLSL()) && (!important || m_kart->getType() == RaceManager::KT_AI || m_kart->getType() == RaceManager::KT_SPARE_TIRE)) { @@ -257,6 +248,7 @@ void KartGFX::setSkidLevel(const unsigned int level) { assert(level >= 1); assert(level <= 2); + m_skid_level = level; const ParticleKind *pk = level==1 ? m_skid_kind1 : m_skid_kind2; #ifndef SERVER_ONLY if(m_all_emitters[KGFX_SKID1L]) @@ -309,8 +301,13 @@ void KartGFX::setXYZ(const KartGFXType type, const Vec3 &xyz) void KartGFX::setCreationRateAbsolute(KartGFXType type, float f) { #ifndef SERVER_ONLY - if(m_all_emitters[type]) - m_all_emitters[type]->setCreationRateAbsolute(f); + if (!m_all_emitters[type]) + return; + + if (m_all_emitters[type]->getCreationRateFloat() == f) + return; + + m_all_emitters[type]->setCreationRateAbsolute(f); #endif } // setCreationRateAbsolute @@ -428,7 +425,9 @@ void KartGFX::updateNitroGraphics(float nitro_frac) setCreationRateRelative(KartGFX::KGFX_NITRO2, nitro_frac); setCreationRateRelative(KartGFX::KGFX_NITROSMOKE1, nitro_frac); setCreationRateRelative(KartGFX::KGFX_NITROSMOKE2, nitro_frac); - m_nitro_light->setVisible(true); + + if (CVS->isGLSL()) + m_nitro_light->setVisible(true); } else { @@ -436,15 +435,14 @@ void KartGFX::updateNitroGraphics(float nitro_frac) setCreationRateAbsolute(KartGFX::KGFX_NITRO2, 0); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE1, 0); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE2, 0); - m_nitro_light->setVisible(false); + + if (CVS->isGLSL()) + m_nitro_light->setVisible(false); } - if (CVS->isGLSL()) - { - // Exhaust is always emitting - setCreationRateRelative(KartGFX::KGFX_EXHAUST1, 1.0); - setCreationRateRelative(KartGFX::KGFX_EXHAUST2, 1.0); - } + // Exhaust is always emitting + setCreationRateRelative(KartGFX::KGFX_EXHAUST1, 1.0); + setCreationRateRelative(KartGFX::KGFX_EXHAUST2, 1.0); #endif } // updateGraphics @@ -456,8 +454,11 @@ void KartGFX::updateNitroGraphics(float nitro_frac) void KartGFX::updateSkidLight(unsigned int level) { #ifndef SERVER_ONLY - m_skidding_light_1->setVisible(level == 1); - m_skidding_light_2->setVisible(level > 1); + if (CVS->isGLSL()) + { + m_skidding_light_1->setVisible(level == 1); + m_skidding_light_2->setVisible(level > 1); + } #endif } // updateSkidLight @@ -484,7 +485,7 @@ void KartGFX::getGFXStatus(int* nitro, bool* zipper, if (m_all_emitters[KGFX_SKIDL]) { s = m_all_emitters[KGFX_SKIDL]->getCreationRate(); - r = m_skidding_light_2->isVisible(); + r = m_skid_level == 2; } *nitro = n; @@ -505,15 +506,19 @@ void KartGFX::setGFXFromReplay(int nitro, bool zipper, setCreationRateAbsolute(KartGFX::KGFX_NITRO2, (float)nitro); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE1, (float)nitro); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE2, (float)nitro); - m_nitro_light->setVisible(true); + + if (CVS->isGLSL()) + m_nitro_light->setVisible(true); } - else if (m_nitro_light->isVisible() && nitro == 0) + else { setCreationRateAbsolute(KartGFX::KGFX_NITRO1, 0.0f); setCreationRateAbsolute(KartGFX::KGFX_NITRO2, 0.0f); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE1, 0.0f); setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE2, 0.0f); - m_nitro_light->setVisible(false); + + if (CVS->isGLSL()) + m_nitro_light->setVisible(false); } if (zipper) @@ -521,36 +526,33 @@ void KartGFX::setGFXFromReplay(int nitro, bool zipper, if (skidding > 0) { - if (!m_skidding_light_1->isVisible() && !red_skidding) - { - if (m_all_emitters[KGFX_SKID1L]) - m_all_emitters[KGFX_SKID1L]->setParticleType(m_skid_kind1); - if (m_all_emitters[KGFX_SKID1R]) - m_all_emitters[KGFX_SKID1R]->setParticleType(m_skid_kind1); + const ParticleKind* skid_kind = red_skidding ? m_skid_kind2 + : m_skid_kind1; - m_skidding_light_1->setVisible(true); - m_skidding_light_2->setVisible(false); - } - if (!m_skidding_light_2->isVisible() && red_skidding) - { - if (m_all_emitters[KGFX_SKID1L]) - m_all_emitters[KGFX_SKID1L]->setParticleType(m_skid_kind2); - if (m_all_emitters[KGFX_SKID1R]) - m_all_emitters[KGFX_SKID1R]->setParticleType(m_skid_kind2); + if (m_all_emitters[KGFX_SKID1L]) + m_all_emitters[KGFX_SKID1L]->setParticleType(skid_kind); + if (m_all_emitters[KGFX_SKID1R]) + m_all_emitters[KGFX_SKID1R]->setParticleType(skid_kind); - m_skidding_light_1->setVisible(false); - m_skidding_light_2->setVisible(true); + if (CVS->isGLSL()) + { + m_skidding_light_1->setVisible(!red_skidding); + m_skidding_light_2->setVisible(red_skidding); } + setCreationRateAbsolute(KartGFX::KGFX_SKIDL, (float)skidding); setCreationRateAbsolute(KartGFX::KGFX_SKIDR, (float)skidding); } - else if ((m_skidding_light_1->isVisible() || - m_skidding_light_2->isVisible()) && skidding == 0) + else { setCreationRateAbsolute(KartGFX::KGFX_SKIDL, 0.0f); setCreationRateAbsolute(KartGFX::KGFX_SKIDR, 0.0f); - m_skidding_light_1->setVisible(false); - m_skidding_light_2->setVisible(false); + + if (CVS->isGLSL()) + { + m_skidding_light_1->setVisible(false); + m_skidding_light_2->setVisible(false); + } } #endif } // setGFXFromReplay @@ -559,9 +561,12 @@ void KartGFX::setGFXFromReplay(int nitro, bool zipper, void KartGFX::setGFXInvisible() { #ifndef SERVER_ONLY - m_nitro_light->setVisible(false); - m_skidding_light_1->setVisible(false); - m_skidding_light_2->setVisible(false); - m_head_light->setVisible(false); + if (CVS->isGLSL()) + { + m_nitro_light->setVisible(false); + m_skidding_light_1->setVisible(false); + m_skidding_light_2->setVisible(false); + m_head_light->setVisible(false); + } #endif } // setGFXInvisible diff --git a/src/karts/kart_gfx.hpp b/src/karts/kart_gfx.hpp index 68f92d336..d7c2929a6 100644 --- a/src/karts/kart_gfx.hpp +++ b/src/karts/kart_gfx.hpp @@ -77,6 +77,9 @@ private: /** Used to alternate particle effects from the rear wheels. */ int m_wheel_toggle; + + /** A skid level that is currently in use */ + int m_skid_level; /** A light that's shown when the kart uses nitro. */ irr::scene::ISceneNode* m_nitro_light; diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index bd3e92f55..06926a3cd 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -152,7 +152,7 @@ void Physics::update(float dt) // Maximum of three substeps. This will work for framerate down to // 20 FPS (bullet default frequency is 60 HZ). - m_dynamics_world->stepSimulation(dt, 3); + m_dynamics_world->stepSimulation(dt, 6, 1.0f/120.0f); // Now handle the actual collision. Note: flyables can not be removed // inside of this loop, since the same flyables might hit more than one diff --git a/src/tracks/check_cannon.cpp b/src/tracks/check_cannon.cpp index 2fb3000c8..9daa715b9 100644 --- a/src/tracks/check_cannon.cpp +++ b/src/tracks/check_cannon.cpp @@ -21,10 +21,13 @@ #include "animations/animation_base.hpp" #include "animations/ipo.hpp" #include "config/user_config.hpp" +#include "graphics/irr_driver.hpp" #include "graphics/show_curve.hpp" +#include "graphics/stk_tex_manager.hpp" #include "io/xml_node.hpp" #include "karts/abstract_kart.hpp" #include "karts/cannon_animation.hpp" +#include "karts/skidding.hpp" #include "modes/world.hpp" @@ -35,13 +38,23 @@ CheckCannon::CheckCannon(const XMLNode &node, unsigned int index) : CheckLine(node, index) { - core::vector3df p1, p2; - if(!node.get("target-p1", &p1) || !node.get("target-p2", &p2)) + std::string p1("target-p1"); + std::string p2("target-p2"); + + if (race_manager->getReverseTrack()) + { + p1 = "p1"; + p2 = "p2"; + } + + if( !node.get(p1, &m_target_left ) || + !node.get(p2, &m_target_right) ) Log::fatal("CheckCannon", "No target line specified."); - m_target.setLine(p1, p2); + m_curve = new Ipo(*(node.getNode("curve")), /*fps*/25, /*reverse*/race_manager->getReverseTrack()); + #if defined(DEBUG) && !defined(SERVER_ONLY) if(UserConfigParams::m_track_debug) { @@ -50,7 +63,41 @@ CheckCannon::CheckCannon(const XMLNode &node, unsigned int index) for(unsigned int i=0; iaddPoint(p[i]); } + if (UserConfigParams::m_check_debug) + { + video::SMaterial material; + material.setFlag(video::EMF_BACK_FACE_CULLING, false); + material.setFlag(video::EMF_LIGHTING, false); + material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; + scene::IMesh *mesh = irr_driver->createQuadMesh(&material, + /*create mesh*/true); + scene::IMeshBuffer *buffer = mesh->getMeshBuffer(0); + + assert(buffer->getVertexType() == video::EVT_STANDARD); + irr::video::S3DVertex* vertices + = (video::S3DVertex*)buffer->getVertices(); + Vec3 height(0, 3, 0); + vertices[0].Pos = m_target_left.toIrrVector(); + vertices[1].Pos = m_target_right.toIrrVector(); + vertices[2].Pos = Vec3(m_target_right + height).toIrrVector(); + vertices[3].Pos = Vec3(m_target_left + height).toIrrVector(); + for (unsigned int i = 0; i<4; i++) + { + vertices[i].Color = m_active_at_reset + ? video::SColor(128, 255, 0, 0) + : video::SColor(128, 128, 128, 128); + } + buffer->recalculateBoundingBox(); + buffer->getMaterial().setTexture(0, STKTexManager::getInstance()->getUnicolorTexture(video::SColor(128, 255, 105, 180))); + buffer->getMaterial().setTexture(1, STKTexManager::getInstance()->getUnicolorTexture(video::SColor(0, 0, 0, 0))); + buffer->getMaterial().setTexture(2, STKTexManager::getInstance()->getUnicolorTexture(video::SColor(0, 0, 0, 0))); + buffer->getMaterial().BackfaceCulling = false; + //mesh->setBoundingBox(buffer->getBoundingBox()); + m_debug_target_node = irr_driver->addMesh(mesh, "checkdebug"); + mesh->drop(); + } #endif // DEBUG AND !SERVER_ONLY + } // CheckCannon // ---------------------------------------------------------------------------- @@ -66,6 +113,26 @@ CheckCannon::~CheckCannon() #endif } // ~CheckCannon +// ---------------------------------------------------------------------------- +void CheckCannon::changeDebugColor(bool is_active) +{ +#if defined(DEBUG) && !defined(SERVER_ONLY) + CheckLine::changeDebugColor(is_active); + + scene::IMesh *mesh = m_debug_target_node->getMesh(); + scene::IMeshBuffer *buffer = mesh->getMeshBuffer(0); + irr::video::S3DVertex* vertices + = (video::S3DVertex*)buffer->getVertices(); + video::SColor color = is_active ? video::SColor(192, 255, 0, 0) + : video::SColor(192, 128, 128, 128); + for (unsigned int i = 0; i<4; i++) + { + vertices[i].Color = color; + } + buffer->getMaterial().setTexture(0, STKTexManager::getInstance()->getUnicolorTexture(color)); +#endif +} // changeDebugColor + // ---------------------------------------------------------------------------- /** Called when the check line is triggered. This function creates a cannon * animation object and attaches it to the kart. @@ -73,9 +140,13 @@ CheckCannon::~CheckCannon() */ void CheckCannon::trigger(unsigned int kart_index) { - Vec3 target(m_target.getMiddle()); AbstractKart *kart = World::getWorld()->getKart(kart_index); if(kart->getKartAnimation()) return; - new CannonAnimation(kart, m_curve->clone()); + // The constructor AbstractKartAnimation resets the skidding to 0. So in + // order to smooth rotate the kart, we need to keep the current visual + // rotation and pass it to the CannonAnimation. + float skid_rot = kart->getSkidding()->getVisualSkidRotation(); + new CannonAnimation(kart, m_curve->clone(), getLeftPoint(), getRightPoint(), + m_target_left, m_target_right, skid_rot); } // CheckCannon diff --git a/src/tracks/check_cannon.hpp b/src/tracks/check_cannon.hpp index 3507195d3..0708d944a 100644 --- a/src/tracks/check_cannon.hpp +++ b/src/tracks/check_cannon.hpp @@ -37,7 +37,8 @@ class CheckCannon : public CheckLine { private: /** The target point the kart will fly to. */ - core::line3df m_target; + Vec3 m_target_left; + Vec3 m_target_right; /** Stores the cannon curve data. */ Ipo *m_curve; @@ -46,12 +47,16 @@ private: /** If track debugging is enabled, this will show the the curve of * the cannon in the race. */ ShowCurve * m_show_curve; + + /** Used to display debug information about checklines. */ + scene::IMeshSceneNode *m_debug_target_node; #endif public: CheckCannon(const XMLNode &node, unsigned int index); virtual ~CheckCannon(); virtual void trigger(unsigned int kart_index); + virtual void changeDebugColor(bool is_active); }; // CheckLine #endif diff --git a/src/tracks/check_line.hpp b/src/tracks/check_line.hpp index 1da3ec34c..3b06e2ca8 100644 --- a/src/tracks/check_line.hpp +++ b/src/tracks/check_line.hpp @@ -71,6 +71,10 @@ private: /** How much a kart is allowed to be over the minimum height of a * quad and still considered to be able to cross it. */ static const int m_over_min_height = 4; +protected: + const Vec3 &getLeftPoint() const { return m_left_point; } + const Vec3 &getRightPoint() const { return m_right_point; } + public: CheckLine(const XMLNode &node, unsigned int index); virtual ~CheckLine(); diff --git a/src/utils/debug.cpp b/src/utils/debug.cpp index 714a21892..2f39ccf5d 100644 --- a/src/utils/debug.cpp +++ b/src/utils/debug.cpp @@ -723,11 +723,15 @@ bool onEvent(const SEvent &event) if(!UserConfigParams::m_artist_debug_mode) return true; // keep handling the events - if(event.EventType == EET_MOUSE_INPUT_EVENT) + if (event.EventType == EET_MOUSE_INPUT_EVENT) { // Create the menu (only one menu at a time) - if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN && - !g_debug_menu_visible) + #ifdef ANDROID + if (event.MouseInput.X < 30 && event.MouseInput.Y < 30 && + #else + if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN && + #endif + !g_debug_menu_visible) { irr_driver->getDevice()->getCursorControl()->setVisible(true);