diff --git a/src/graphics/slip_stream.cpp b/src/graphics/slip_stream.cpp index a9997db4d..856704521 100644 --- a/src/graphics/slip_stream.cpp +++ b/src/graphics/slip_stream.cpp @@ -36,17 +36,21 @@ #include "utils/constants.hpp" #include "utils/mini_glm.hpp" -/** Creates the slip stream object using a moving texture. +/** Creates the slip stream object * \param kart Pointer to the kart to which the slip stream * belongs to. */ -SlipStream::SlipStream(AbstractKart* kart) : MovingTexture(0, 0), m_kart(kart) +SlipStream::SlipStream(AbstractKart* kart) { m_node = NULL; + m_kart = kart; #ifndef SERVER_ONLY if (CVS->isGLSL()) { + m_moving = new MovingTexture(0.0f, 0.0f); + m_moving_fast = new MovingTexture(0.0f, 0.0f); + Material* material = material_manager->getMaterialSPM("slipstream.png", ""); SP::SPMesh* mesh = createMesh(material); @@ -59,23 +63,42 @@ SlipStream::SlipStream(AbstractKart* kart) : MovingTexture(0, 0), m_kart(kart) m_node->setVisible(false); SP::SPMeshNode* spmn = dynamic_cast(m_node); assert(spmn); - setSPTM(spmn->getTextureMatrix(0).data()); + m_moving->setSPTM(spmn->getTextureMatrix(0).data()); + + material = material_manager->getMaterialSPM("slipstream2.png", ""); + mesh = createMesh(material); + m_node_fast = irr_driver->addMesh(mesh, "slipstream2"); + mesh->drop(); + debug_name = m_kart->getIdent()+" (slip-stream2)"; + m_node_fast->setName(debug_name.c_str()); + m_node_fast->setPosition(core::vector3df(0, 0 * 0.25f + 2.5f, + m_kart->getKartLength())); + m_node_fast->setVisible(false); + spmn = dynamic_cast(m_node_fast); + assert(spmn); + m_moving_fast->setSPTM(spmn->getTextureMatrix(0).data()); } #endif m_slipstream_time = 0.0f; - float length = m_kart->getKartProperties()->getSlipstreamLength(); + //The kart starts at 0 speed anyway + float length = 0.0f; float kw = m_kart->getKartWidth(); - float ew = m_kart->getKartProperties()->getSlipstreamWidth(); + float ew = 0.0f; float kl = m_kart->getKartLength(); + //making the slipstream quad start at the kart front + //allows better results when the kart turns Vec3 p[4]; - p[0]=Vec3(-kw*0.5f, 0, -kl*0.5f ); + p[0]=Vec3(-kw*0.5f, 0, kl*0.5f ); p[1]=Vec3(-ew*0.5f, 0, -kl*0.5f-length); p[2]=Vec3( ew*0.5f, 0, -kl*0.5f-length); - p[3]=Vec3( kw*0.5f, 0, -kl*0.5f ); + p[3]=Vec3( kw*0.5f, 0, kl*0.5f ); + m_slipstream_quad = new Quad(p[0], p[1], p[2], p[3]); + //The position will be corrected in the update anyway + m_slipstream_inner_quad = new Quad(p[0], p[1], p[2], p[3]); #ifndef SERVER_ONLY if (UserConfigParams::m_slipstream_debug) { @@ -97,6 +120,22 @@ SlipStream::SlipStream(AbstractKart* kart) : MovingTexture(0, 0), m_kart(kart) m_debug_dc->recalculateBoundingBox(); m_debug_dc->setParent(m_kart->getNode()); SP::addDynamicDrawCall(m_debug_dc); + + m_debug_dc2 = std::make_shared + (scene::EPT_TRIANGLE_STRIP, + SP::SPShaderManager::get()->getSPShader("additive"), + material_manager->getDefaultSPMaterial("additive")); + m_debug_dc2->getVerticesVector().resize(4); + v = m_debug_dc2->getVerticesVector().data(); + for (unsigned i = 0; i < 4; i++) + { + v[i].m_position = p[idx[i]].toIrrVector(); + v[i].m_normal = 0x1FF << 10; + v[i].m_color = red; + } + m_debug_dc2->recalculateBoundingBox(); + m_debug_dc2->setParent(m_kart->getNode()); + SP::addDynamicDrawCall(m_debug_dc2); } #endif } // SlipStream @@ -110,11 +149,22 @@ SlipStream::~SlipStream() { irr_driver->removeNode(m_node); } + if (m_node_fast) + { + irr_driver->removeNode(m_node_fast); + } if (m_debug_dc) { m_debug_dc->removeFromSP(); } + if (m_debug_dc2) + { + m_debug_dc2->removeFromSP(); + } delete m_slipstream_quad; + delete m_slipstream_inner_quad; + delete m_moving; + delete m_moving_fast; } // ~SlipStream @@ -246,41 +296,56 @@ SP::SPMesh* SlipStream::createMesh(Material* material) /** Sets the animation intensity (or speed). * \param f Intensity: 0 = no slip stream, * 1 = collecting - * 2 = using slip stream bonus + * 2 = bonus can be used */ void SlipStream::setIntensity(float f, const AbstractKart *kart) { - if (!kart || !m_node) + if (!kart || !m_node || !m_node_fast) { if (m_node) { m_node->setVisible(false); } + if (m_node_fast) + { + m_node_fast->setVisible(false); + } return; } - m_node->setVisible(true); + float ktf = m_kart->getKartProperties()->getSlipstreamCollectTime(); + const float above_terrain = 0.2f; core::vector3df my_pos = m_kart->getNode()->getPosition(); my_pos.Y = m_kart->getHoT()+above_terrain; - m_node->setPosition(my_pos); - core::vector3df other_pos = kart->getNode()->getPosition(); other_pos.Y = kart->getHoT()+above_terrain; core::vector3df diff = other_pos - my_pos; core::vector3df rotation = diff.getHorizontalAngle(); - m_node->setRotation(rotation); float fs = diff.getLength()/m_length; + + m_node->setPosition(my_pos); + m_node->setRotation(rotation); m_node->setScale(core::vector3df(1, 1, fs)); - // For real testing in game: this needs some tuning! - m_node->setVisible(f!=0); - MovingTexture::setSpeed(f, 0); + m_node_fast->setPosition(my_pos); + m_node_fast->setRotation(rotation); + m_node_fast->setScale(core::vector3df(1, 1, fs)); + + m_node->setVisible(f>0.0f && fsetVisible(f>=ktf); + + //specify the texture speed movement + if (f > 8.0f) f = 8.0f; + f = f/2; + + m_moving->setSpeed(f, 0); + m_moving_fast->setSpeed(f, 0); return; // For debugging: make the slip stream effect visible all the time m_node->setVisible(true); - MovingTexture::setSpeed(1.0f, 0.0f); + m_moving->setSpeed(1.0f, 0.0f); } // setIntensity //----------------------------------------------------------------------------- @@ -302,8 +367,7 @@ void SlipStream::updateSlipstreamPower() // See if we are currently using accumulated slipstream credits: // ------------------------------------------------------------- if(m_slipstream_mode==SS_USE) - { - setIntensity(2.0f, NULL); + { const KartProperties *kp = m_kart->getKartProperties(); m_kart->increaseMaxSpeed(MaxSpeed::MS_INCREASE_SLIPSTREAM, kp->getSlipstreamMaxSpeedIncrease(), @@ -320,23 +384,160 @@ void SlipStream::updateSlipstreamPower() * black: kart too slow * red: not inside of slipstream area * green: slipstream is being accumulated. + * \param inner : bool to know if we apply the color to the inner quad or to the main one */ -void SlipStream::setDebugColor(const video::SColor &color) +void SlipStream::setDebugColor(const video::SColor &color, bool inner) { - if (!m_debug_dc) + if (!inner) { - return; - } + if (!m_debug_dc) + { + return; + } - video::S3DVertexSkinnedMesh* v = m_debug_dc->getVerticesVector().data(); - for (unsigned i = 0; i < 4; i++) + video::S3DVertexSkinnedMesh* v = m_debug_dc->getVerticesVector().data(); + for (unsigned i = 0; i < 4; i++) + { + v[i].m_color = color; + } + m_debug_dc->setUpdateOffset(0); + } + else { - v[i].m_color = color; - } - m_debug_dc->setUpdateOffset(0); + if (!m_debug_dc2) + { + return; + } + video::S3DVertexSkinnedMesh* v = m_debug_dc2->getVerticesVector().data(); + for (unsigned i = 0; i < 4; i++) + { + v[i].m_color = color; + } + m_debug_dc2->setUpdateOffset(0); + } } // setDebugColor +//----------------------------------------------------------------------------- +/** UpdateQuad + */ +void SlipStream::updateQuad() +{ + + //Change the quad form to counteract the mismatch between + //kart orientation and real direction + + //Computations are contrieved by the fact we have several + //different 3D vector, one for each library. + Vec3 moving_xyz = m_kart->getPreviousXYZ() - m_kart->getXYZ(); + + //retrieve a vector rotated to kart direction + btScalar bx,by,bz;//a btScalar is a float or a double + bx = 1.0f; + by = bz = 0.0f; + btVector3 rotated_base; + rotated_base.setValue(bx,by,bz); + btQuaternion rotation = m_kart->getRotation(); + rotated_base = quatRotate(rotation,rotated_base); + Vec3 direction_vector; + //Z and X need to be inverted and X multiplied by -1 to match moving_xyz + direction_vector = Vec3(rotated_base.getZ(), rotated_base.getY(), -rotated_base.getX()); + + //normalize the moving vector + float vec_length = moving_xyz.x()*moving_xyz.x() + + moving_xyz.y()*moving_xyz.y() + + moving_xyz.z()*moving_xyz.z(); + if (vec_length != 0) + { + vec_length = core::reciprocal_squareroot(vec_length); + float x,y,z; + x = moving_xyz.x() * vec_length; + y = moving_xyz.y() * vec_length; + z = moving_xyz.z() * vec_length; + moving_xyz = Vec3(x,y,z); + } + + //This vector gives us the change to apply in absolute coordinates + Vec3 noffset = moving_xyz - direction_vector; + + //But the quad position is in the kart coordinate space + //So we rotate it back + rotated_base.setValue(-noffset.z(),noffset.y(),noffset.x()); + rotation = rotation.inverse(); + rotated_base = quatRotate(rotation,rotated_base); + noffset = Vec3(rotated_base.getZ(), rotated_base.getY(), -rotated_base.getX()); + + float speed_factor = m_kart->getSpeed()/m_kart->getKartProperties()->getSlipstreamBaseSpeed(); + float length = m_kart->getKartProperties()->getSlipstreamLength()*speed_factor; + float kw = m_kart->getKartWidth(); + float ew = m_kart->getKartProperties()->getSlipstreamWidth()*speed_factor; + float kl = m_kart->getKartLength(); + float offx = (kl*0.5f+length)*noffset.x(); + float offz = (kl*0.5f+length)*noffset.z(); + + //making the slipstream quad start at the kart front + //allows better results when the kart turns + Vec3 p[4]; + p[0]=Vec3(-kw*0.5f, 0, kl*0.5f ); + p[1]=Vec3(-ew*0.5f+offx, 0, -kl*0.5f-length+offz); + p[2]=Vec3( ew*0.5f+offx, 0, -kl*0.5f-length+offz); + p[3]=Vec3( kw*0.5f, 0, kl*0.5f ); + + //Update the slipstreaming quad + m_slipstream_quad->setQuad(p[0], p[1], p[2], p[3]); + +#ifndef SERVER_ONLY + //recalculate quad position for debug drawing + if (UserConfigParams::m_slipstream_debug) + { + video::S3DVertexSkinnedMesh* v = + m_debug_dc->getVerticesVector().data(); + unsigned idx[] = { 0, 3, 1, 2 }; + for (unsigned i = 0; i < 4; i++) + { + v[i].m_position = p[idx[i]].toIrrVector(); + } + m_debug_dc->recalculateBoundingBox(); + m_debug_dc->setParent(m_kart->getNode()); + SP::addDynamicDrawCall(m_debug_dc); + } +#endif + float inner_factor = m_kart->getKartProperties()->getSlipstreamInnerFactor()*sqrt(speed_factor); + length = length*inner_factor; + ew = ew*inner_factor; + if (ew > 0.5f) ew -= 0.5f; + else ew = 0; + + offx = (kl*0.5f+length)*noffset.x(); + offz = (kl*0.5f+length)*noffset.z(); + + p[0]=Vec3(-kw*0.5f, 0, kl*0.5f ); + p[1]=Vec3(-ew*0.5f+offx, 0, -kl*0.5f-length+offz); + p[2]=Vec3( ew*0.5f+offx, 0, -kl*0.5f-length+offz); + p[3]=Vec3( kw*0.5f, 0, kl*0.5f ); + + //Update the slipstreaming inner quad + m_slipstream_inner_quad->setQuad(p[0], p[1], p[2], p[3]); + +#ifndef SERVER_ONLY + //recalculate inner quad position for debug drawing + if (UserConfigParams::m_slipstream_debug) + { + video::S3DVertexSkinnedMesh* v = + m_debug_dc2->getVerticesVector().data(); + unsigned idx[] = { 0, 3, 1, 2 }; + for (unsigned i = 0; i < 4; i++) + { + v[i].m_position = p[idx[i]].toIrrVector(); + } + m_debug_dc2->recalculateBoundingBox(); + m_debug_dc2->setParent(m_kart->getNode()); + SP::addDynamicDrawCall(m_debug_dc2); + } +#endif + +} //updateQuad + //----------------------------------------------------------------------------- /** Update, called once per timestep. * \param dt Time step size. @@ -350,38 +551,53 @@ void SlipStream::update(float dt) || m_kart->isGhostKart()) return; - MovingTexture::update(dt); + //there is no slipstreaming at low speed + //and the quad may do weird things if going in reverse + if(m_kart->getSpeed() > 1.0f) + { + updateQuad(); + } + + m_moving->update(dt); + m_moving_fast->update(dt); if(m_slipstream_mode==SS_USE) { - m_slipstream_time -= dt; + m_slipstream_time -= dt;//This makes slipstream_time decrease twice faster if(m_slipstream_time<0) m_slipstream_mode=SS_NONE; } updateSlipstreamPower(); // If this kart is too slow for slipstreaming taking effect, do nothing + // Use a margin because what really matters is the target's speed + // If this kart is much slower than the minSpeed, then either its + // targets are slower too, or it won't stay long enough behind them // -------------------------------------------------------------------- // Define this to get slipstream effect shown even when the karts are // not moving. This is useful for debugging the graphics of SS-ing. //#define DISPLAY_SLIPSTREAM_WITH_0_SPEED_FOR_DEBUGGING #ifndef DISPLAY_SLIPSTREAM_WITH_0_SPEED_FOR_DEBUGGING - if(m_kart->getSpeed() < kp->getSlipstreamMinSpeed()) + if(m_kart->getSpeed() < kp->getSlipstreamMinSpeed() - 2.0f) { setIntensity(0, NULL); m_slipstream_mode = SS_NONE; if(UserConfigParams::m_slipstream_debug) - setDebugColor(video::SColor(255, 0, 0, 0)); + { + setDebugColor(video::SColor(255, 0, 0, 0),false); + setDebugColor(video::SColor(255, 0, 0, 0),true); + } return; } #endif // Then test if this kart is in the slipstream range of another kart: // ------------------------------------------------------------------ - World *world = World::getWorld(); - unsigned int num_karts = world->getNumKarts(); - bool is_sstreaming = false; - m_target_kart = NULL; + World *world = World::getWorld(); + unsigned int num_karts = world->getNumKarts(); + bool is_sstreaming = false; + bool is_inner_sstreaming = false; + m_target_kart = NULL; // Note that this loop can not be simply replaced with a shorter loop // using only the karts with a better position - since a kart might @@ -389,6 +605,7 @@ void SlipStream::update(float dt) for(unsigned int i=0; igetKart(i); + // Don't test for slipstream with itself, a kart that is being // rescued or exploding, a ghost kart or an eliminated kart if(m_target_kart==m_kart || @@ -396,6 +613,8 @@ void SlipStream::update(float dt) m_target_kart->isGhostKart() || m_target_kart->isEliminated() ) continue; + const KartProperties *kp_target = m_target_kart->getKartProperties(); + // Transform this kart location into target kart point of view Vec3 lc = m_target_kart->getTrans().inverse()(m_kart->getXYZ()); @@ -404,37 +623,51 @@ void SlipStream::update(float dt) if (fabsf(lc.y()) > 6.0f) continue; // If the kart we are testing against is too slow, no need to test - // slipstreaming. Note: We compare the speed of the other kart - // against the minimum slipstream speed kart of this kart - not - // entirely sure if this makes sense, but it makes it easier to - // give karts different slipstream properties. + // slipstreaming. #ifndef DISPLAY_SLIPSTREAM_WITH_0_SPEED_FOR_DEBUGGING - if (m_target_kart->getSpeed() < kp->getSlipstreamMinSpeed()) + if (m_target_kart->getSpeed() < kp_target->getSlipstreamMinSpeed()) { if(UserConfigParams::m_slipstream_debug && m_kart->getController()->isLocalPlayerController()) + { m_target_kart->getSlipstream() - ->setDebugColor(video::SColor(255, 0, 0, 0)); + ->setDebugColor(video::SColor(255, 0, 0, 0), false); + m_target_kart->getSlipstream() + ->setDebugColor(video::SColor(255, 0, 0, 0), true); + } continue; } #endif // Quick test: the kart must be not more than - // slipstream length+0.5*kart_length()+0.5*target_kart_length + // slipstream length+0.5*kart_length()+target_kart_length // away from the other kart + // (additional target_kart_length because that kart's center + // is not the center of rotation of the slipstreaming quad) Vec3 delta = m_kart->getXYZ() - m_target_kart->getXYZ(); - float l = kp->getSlipstreamLength() - + 0.5f*( m_target_kart->getKartLength() - +m_kart->getKartLength() ); + float l = kp_target->getSlipstreamLength(); + float speed_factor = m_target_kart->getSpeed() + /kp_target->getSlipstreamBaseSpeed(); + l = l*speed_factor + m_target_kart->getKartLength() + + 0.5f*m_kart->getKartLength(); if(delta.length2() > l*l) { - if(UserConfigParams::m_slipstream_debug && - m_kart->getController()->isLocalPlayerController()) - m_target_kart->getSlipstream() - ->setDebugColor(video::SColor(255, 0, 0, 128)); continue; } - // Real test: if in slipstream quad of other kart + // Real test 1: if in inner slipstream quad of other kart + if(m_target_kart->getSlipstream()->m_slipstream_inner_quad + ->pointInside(lc)) + { + is_inner_sstreaming = true; + is_sstreaming = true; + break; + } + if(UserConfigParams::m_slipstream_debug && + m_kart->getController()->isLocalPlayerController()) + m_target_kart->getSlipstream() + ->setDebugColor(video::SColor(255, 0, 0, 255),true); + + // Real test2: if in slipstream quad of other kart if(m_target_kart->getSlipstream()->m_slipstream_quad ->pointInside(lc)) { @@ -444,26 +677,36 @@ void SlipStream::update(float dt) if(UserConfigParams::m_slipstream_debug && m_kart->getController()->isLocalPlayerController()) m_target_kart->getSlipstream() - ->setDebugColor(video::SColor(255, 0, 0, 255)); + ->setDebugColor(video::SColor(255, 0, 0, 255),false); } // for i < num_karts if(!is_sstreaming) { if(UserConfigParams::m_slipstream_debug && m_kart->getController()->isLocalPlayerController()) + { m_target_kart->getSlipstream() - ->setDebugColor(video::SColor(255, 255, 0, 0)); + ->setDebugColor(video::SColor(255, 255, 0, 0),false); + + m_target_kart->getSlipstream() + ->setDebugColor(video::SColor(255, 0, 255, 0),true); + + } if(isSlipstreamReady()) { // The first time slipstream is ready after collecting - // and you are leaving the slipstream area, you get a - // zipper bonus. + // and you are leaving the slipstream area, you get an + // instant speed_increase if(m_slipstream_mode==SS_COLLECT) { m_slipstream_mode = SS_USE; - m_kart->handleZipper(); - m_slipstream_time = kp->getSlipstreamCollectTime(); + m_kart->instantSpeedIncrease(MaxSpeed::MS_INCREASE_SLIPSTREAM, + kp->getSlipstreamMaxSpeedIncrease(), + kp->getSlipstreamMaxSpeedIncrease(), + kp->getSlipstreamAddPower(), + kp->getSlipstreamDuration(), + kp->getSlipstreamFadeOutTime()); return; } } @@ -475,17 +718,30 @@ void SlipStream::update(float dt) if(UserConfigParams::m_slipstream_debug && m_kart->getController()->isLocalPlayerController()) - m_target_kart->getSlipstream()->setDebugColor(video::SColor(255, 0, 255, 0)); + m_target_kart->getSlipstream()->setDebugColor(video::SColor(255, 128, 255, 0),false); + // Accumulate slipstream credits now - m_slipstream_time = m_slipstream_mode==SS_NONE ? dt - : m_slipstream_time+dt; + //Twice as fast in the inner quad + if (is_inner_sstreaming) + { + m_slipstream_time = m_slipstream_mode==SS_NONE ? 2*dt : m_slipstream_time+2*dt; + + if(UserConfigParams::m_slipstream_debug && + m_kart->getController()->isLocalPlayerController()) + m_target_kart->getSlipstream()->setDebugColor(video::SColor(255, 0, 255, 128),true); + } + else + { + m_slipstream_time = m_slipstream_mode==SS_NONE ? dt : m_slipstream_time+dt; + } + + //Cap the possible credits. Keep this similar to the animation speed cap. + if (m_slipstream_time > 8.0f) + m_slipstream_time = 8.0f; + if(isSlipstreamReady()) m_kart->setSlipstreamEffect(9.0f); setIntensity(m_slipstream_time, m_target_kart); m_slipstream_mode = SS_COLLECT; - if (m_slipstream_time > kp->getSlipstreamCollectTime()) - { - setIntensity(1.0f, m_target_kart); - } } // update