diff --git a/src/graphics/material.cpp b/src/graphics/material.cpp index c2f0972a8..f44f61eb4 100644 --- a/src/graphics/material.cpp +++ b/src/graphics/material.cpp @@ -26,6 +26,7 @@ #include "config/user_config.hpp" #include "config/stk_config.hpp" #include "graphics/irr_driver.hpp" +#include "graphics/particle_kind_manager.hpp" #include "io/file_manager.hpp" #include "io/xml_node.hpp" #include "utils/string_utils.hpp" @@ -70,8 +71,6 @@ Material::Material(const XMLNode *node, int index) node->get("graphical-effect", &s ); if(s=="water") m_graphical_effect = GE_WATER; - else if(s=="smoke") - m_graphical_effect = GE_SMOKE; else if (s!="") fprintf(stderr, "Invalid graphical effect specification: '%s' - ignored.\n", @@ -79,11 +78,13 @@ Material::Material(const XMLNode *node, int index) else m_graphical_effect = GE_NONE; - node->get("compositing", &s); - if (s == "blend") m_alpha_blending = true; - else if (s == "test") m_alpha_testing = true; - else if (s == "additive") m_add = true; - else if (s != "none") fprintf(stderr, "[Material] WARNING: Unknown alpha mode '%s'\n", s.c_str()); + if (node->get("compositing", &s)) + { + if (s == "blend") m_alpha_blending = true; + else if (s == "test") m_alpha_testing = true; + else if (s == "additive") m_add = true; + else if (s != "none") fprintf(stderr, "[Material] WARNING: Unknown compositing mode '%s'\n", s.c_str()); + } node->get("zipper", &m_zipper ); node->get("zipper-duration", &m_zipper_duration ); @@ -92,22 +93,26 @@ Material::Material(const XMLNode *node, int index) node->get("zipper-speed-gain", &m_zipper_speed_gain ); // Terrain-specifc sound effect - for(unsigned int i=0; igetNumNodes(); i++) + const unsigned int children_count = node->getNumNodes(); + for (unsigned int i=0; igetNode(i); - if(sfx->getName()!="sfx") + const XMLNode *child_node = node->getNode(i); + + if (child_node->getName() == "sfx") { - printf("Warning: unknown node type '%s' for texture '%s' - ignored.\n", - sfx->getName().c_str(), m_texname.c_str()); - continue; + + initCustomSFX(child_node); } - if(m_sfx_name!="") + else if (child_node->getName() == "particles") { - printf("Warning: more than one sfx specified for texture '%s' - ignored.\n", - m_texname.c_str()); - continue; + initParticlesEffect(child_node); } - initCustomSFX(sfx); + else + { + fprintf(stderr, "[Material] WARNING: unknown node type '%s' for texture '%s' - ignored.\n", + child_node->getName().c_str(), m_texname.c_str()); + } + } // for i getNumNodes() install(/*is_full_path*/false); } // Material @@ -157,6 +162,11 @@ void Material::init(unsigned int index) m_zipper_fade_out_time = -1.0f; m_zipper_max_speed_increase = -1.0f; m_zipper_speed_gain = -1.0f; + + for (int n=0; nget("base", &base); + if (base.size() < 1) + { + fprintf(stderr, "[Material::initParticlesEffect] WARNING: Invalid particle settings for material '%s'\n", + m_texname.c_str()); + return; + } + + ParticleKind* particles = NULL; + try + { + particles = pkm->getParticles(base.c_str()); + } + catch (...) + { + fprintf(stderr, "[Material::initParticlesEffect] WARNING: Cannot find particles '%s' for material '%s'\n", + base.c_str(), m_texname.c_str()); + return; + } + + std::vector conditions; + node->get("condition", &conditions); + + const int count = conditions.size(); + + if (count == 0) + { + fprintf(stderr, "[Material::initParticlesEffect] WARNING: Particles '%s' for material '%s' are declared but not used\n", + base.c_str(), m_texname.c_str()); + } + + for (int c=0; caddParticleNode(); - m_particle_type = type; - - Material* material = type->getMaterial(); - const float minSize = type->getMinSize(); - const float maxSize = type->getMaxSize(); - const int lifeTimeMin = type->getMinLifetime(); - const int lifeTimeMax = type->getMaxLifetime(); - - assert(material->getTexture() != NULL); - assert(maxSize >= minSize); - assert(lifeTimeMax >= lifeTimeMin); - -#ifdef DEBUG - std::string debug_name = std::string("particles(") + material->getTexture()->getName().getPath().c_str() + ")"; - m_node->setName(debug_name.c_str()); -#endif - - - if (parent != NULL) - { - m_node->setParent(parent); - } - - m_node->setPosition(position); - material->setMaterialProperties(&(m_node->getMaterial(0))); - m_node->setMaterialTexture(0, material->getTexture()); - - //m_node->getMaterial(0).MaterialType = video::EMT_ONETEXTURE_BLEND ; - //m_node->getMaterial(0).MaterialTypeParam = pack_texureBlendFunc(video::EBF_SRC_ALPHA, video::EBF_ONE_MINUS_SRC_ALPHA, - // video::EMFN_MODULATE_1X, video::EAS_TEXTURE | video::EAS_VERTEX_COLOR); - m_node->getMaterial(0).ZWriteEnable = false; // disable z-buffer writes - - switch (type->getShape()) - { - case EMITTER_POINT: - { - m_emitter = m_node->createPointEmitter(core::vector3df(m_particle_type->getVelocityX(), - m_particle_type->getVelocityY(), - m_particle_type->getVelocityZ()), // velocity in m/ms - type->getMinRate(), type->getMaxRate(), - type->getMinColor(), type->getMaxColor(), - lifeTimeMin, lifeTimeMax, - m_particle_type->getAngleSpread() /* angle */ - ); - break; - } - - case EMITTER_BOX: - { - const float box_size_x = type->getBoxSizeX()/2.0f; - const float box_size_y = type->getBoxSizeY()/2.0f; - const float box_size_z = type->getBoxSizeZ()/2.0f; - m_emitter = m_node->createBoxEmitter(core::aabbox3df(-box_size_x, -box_size_y, -box_size_z, - box_size_x, box_size_y, box_size_z), - core::vector3df(m_particle_type->getVelocityX(), - m_particle_type->getVelocityY(), - m_particle_type->getVelocityZ()), // velocity in m/ms - type->getMinRate(), type->getMaxRate(), - type->getMinColor(), type->getMaxColor(), - lifeTimeMin, lifeTimeMax, - m_particle_type->getAngleSpread() /* angle */ - ); - - //irr_driver->getSceneManager()->addCubeSceneNode(2.0f, parent, -1, position, core::vector3df(0, 0, 0) /* rotation */, - // core::vector3df(box_size_x, box_size_y, box_size_z)); - - break; - } - - default: - { - fprintf(stderr, "[ParticleEmitter] Unknown shape\n"); - return; - } - } - - m_emitter->setMinStartSize(core::dimension2df(minSize, minSize)); - m_emitter->setMaxStartSize(core::dimension2df(maxSize, maxSize)); - m_node->setEmitter(m_emitter); // this grabs the emitter - m_emitter->drop(); // so we can drop our references - - // FIXME: this is ridiculous, the fadeout time should be equal to the lifetime, except that the - // lifetime is random... - scene::IParticleFadeOutAffector *af = m_node->createFadeOutParticleAffector(video::SColor(0, 255, 255, 255), - type->getFadeoutTime()); - m_node->addAffector(af); - af->drop(); + m_node = NULL; + m_parent = parent; + setParticleType(type); } // KartParticleSystem @@ -162,3 +77,98 @@ void ParticleEmitter::setPosition(core::vector3df pos) { m_node->setPosition(pos); } + +//----------------------------------------------------------------------------- + +void ParticleEmitter::setParticleType(const ParticleKind* type) +{ + if (m_particle_type == type) return; // already the right type + + if (m_node != NULL) m_node->remove(); + + m_node = irr_driver->addParticleNode(); + m_particle_type = type; + + Material* material = type->getMaterial(); + const float minSize = type->getMinSize(); + const float maxSize = type->getMaxSize(); + const int lifeTimeMin = type->getMinLifetime(); + const int lifeTimeMax = type->getMaxLifetime(); + + assert(material->getTexture() != NULL); + assert(maxSize >= minSize); + assert(lifeTimeMax >= lifeTimeMin); + +#ifdef DEBUG + std::string debug_name = std::string("particles(") + material->getTexture()->getName().getPath().c_str() + ")"; + m_node->setName(debug_name.c_str()); +#endif + + + if (m_parent != NULL) + { + m_node->setParent(m_parent); + } + + m_node->setPosition(m_position); + material->setMaterialProperties(&(m_node->getMaterial(0))); + m_node->setMaterialTexture(0, material->getTexture()); + + m_node->getMaterial(0).ZWriteEnable = false; // disable z-buffer writes + + switch (type->getShape()) + { + case EMITTER_POINT: + { + m_emitter = m_node->createPointEmitter(core::vector3df(m_particle_type->getVelocityX(), + m_particle_type->getVelocityY(), + m_particle_type->getVelocityZ()), // velocity in m/ms + type->getMinRate(), type->getMaxRate(), + type->getMinColor(), type->getMaxColor(), + lifeTimeMin, lifeTimeMax, + m_particle_type->getAngleSpread() /* angle */ + ); + break; + } + + case EMITTER_BOX: + { + const float box_size_x = type->getBoxSizeX()/2.0f; + const float box_size_y = type->getBoxSizeY()/2.0f; + const float box_size_z = type->getBoxSizeZ()/2.0f; + m_emitter = m_node->createBoxEmitter(core::aabbox3df(-box_size_x, -box_size_y, -box_size_z, + box_size_x, box_size_y, box_size_z), + core::vector3df(m_particle_type->getVelocityX(), + m_particle_type->getVelocityY(), + m_particle_type->getVelocityZ()), // velocity in m/ms + type->getMinRate(), type->getMaxRate(), + type->getMinColor(), type->getMaxColor(), + lifeTimeMin, lifeTimeMax, + m_particle_type->getAngleSpread() /* angle */ + ); + + //irr_driver->getSceneManager()->addCubeSceneNode(2.0f, parent, -1, position, core::vector3df(0, 0, 0) /* rotation */, + // core::vector3df(box_size_x, box_size_y, box_size_z)); + + break; + } + + default: + { + fprintf(stderr, "[ParticleEmitter] Unknown shape\n"); + return; + } + } + + m_emitter->setMinStartSize(core::dimension2df(minSize, minSize)); + m_emitter->setMaxStartSize(core::dimension2df(maxSize, maxSize)); + m_node->setEmitter(m_emitter); // this grabs the emitter + m_emitter->drop(); // so we can drop our references + + // FIXME: this is ridiculous, the fadeout time should be equal to the lifetime, except that the + // lifetime is random... + scene::IParticleFadeOutAffector *af = m_node->createFadeOutParticleAffector(video::SColor(0, 255, 255, 255), + type->getFadeoutTime()); + m_node->addAffector(af); + af->drop(); +} diff --git a/src/graphics/particle_emitter.hpp b/src/graphics/particle_emitter.hpp index 4e65ba910..34694be66 100644 --- a/src/graphics/particle_emitter.hpp +++ b/src/graphics/particle_emitter.hpp @@ -39,15 +39,19 @@ private: /** Irrlicht's particle systems. */ scene::IParticleSystemSceneNode *m_node; /* left wheel */ + core::vector3df m_position; + + scene::ISceneNode* m_parent; + /** The emitters. Access to these is needed to adjust the number of * particles per second. */ scene::IParticleEmitter *m_emitter; - ParticleKind *m_particle_type; + const ParticleKind *m_particle_type; public: - ParticleEmitter (ParticleKind* type, core::vector3df position, + ParticleEmitter (const ParticleKind* type, core::vector3df position, scene::ISceneNode* parent = NULL); virtual ~ParticleEmitter(); virtual void update (); @@ -55,7 +59,9 @@ public: void setPosition(core::vector3df pos); - ParticleKind* getParticlesInfo() { return m_particle_type; } + const ParticleKind* getParticlesInfo() const { return m_particle_type; } + + void setParticleType(const ParticleKind* p); }; #endif diff --git a/src/graphics/particle_kind.hpp b/src/graphics/particle_kind.hpp index 8d92d2dd8..0fc022303 100644 --- a/src/graphics/particle_kind.hpp +++ b/src/graphics/particle_kind.hpp @@ -76,11 +76,15 @@ private: public: + /** + * @brief Load a XML file describing a type of particles + * @param file Name of the file to load (no full path) + * @throw std::runtime_error If the file cannot be found or is heavily malformed + */ ParticleKind(const std::string file); virtual ~ParticleKind() {} - //float getParticleSize() const { return m_particle_size; } float getMaxSize () const { return m_max_size; } float getMinSize () const { return m_min_size; } diff --git a/src/graphics/particle_kind_manager.cpp b/src/graphics/particle_kind_manager.cpp new file mode 100644 index 000000000..fc7a6e98d --- /dev/null +++ b/src/graphics/particle_kind_manager.cpp @@ -0,0 +1,63 @@ +// $Id$ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2011 Joerg Henrichs, Marianne Gagnon +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "graphics/particle_kind_manager.hpp" +#include "io/file_manager.hpp" + +// ---------------------------------------------------------------------------- + +ParticleKindManager* ParticleKindManager::singleton = NULL; + +ParticleKindManager* ParticleKindManager::get() +{ + if (singleton == NULL) singleton = new ParticleKindManager(); + return singleton; +} + +// ---------------------------------------------------------------------------- + +ParticleKindManager::ParticleKindManager() +{ +} + +// ---------------------------------------------------------------------------- + +ParticleKindManager::~ParticleKindManager() +{ + // TODO: free items in map +} + +// ---------------------------------------------------------------------------- + +ParticleKind* ParticleKindManager::getParticles(const char* name) +{ + std::map::iterator i = m_kinds.find(name); + if (i == m_kinds.end()) + { + ParticleKind* newkind = new ParticleKind(file_manager->getDataFile(name)); + m_kinds[name] = newkind; + return newkind; + } + else + { + return i->second; + } +} + +// ---------------------------------------------------------------------------- diff --git a/src/graphics/particle_kind_manager.hpp b/src/graphics/particle_kind_manager.hpp new file mode 100644 index 000000000..88dac56b5 --- /dev/null +++ b/src/graphics/particle_kind_manager.hpp @@ -0,0 +1,51 @@ +// $Id$ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2011 Marianne Gagnon +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_PARTICLE_KIND_MANAGER_HPP +#define HEADER_PARTICLE_KIND_MANAGER_HPP + +#include "utils/no_copy.hpp" +#include "graphics/particle_kind.hpp" +#include +#include + +/** + * \brief Holds and manages a list of all types of particles + * \ingroup graphics + */ +class ParticleKindManager : public NoCopy +{ +private: + + std::map m_kinds; + static ParticleKindManager* singleton; + + ParticleKindManager(); + +public: + + virtual ~ParticleKindManager(); + + ParticleKind* getParticles(const char* name); + + static ParticleKindManager* get(); +}; +#endif + + diff --git a/src/ide/Xcode/STK_XCode.xcodeproj/project.pbxproj b/src/ide/Xcode/STK_XCode.xcodeproj/project.pbxproj index 50068a8a8..baeed77f9 100644 --- a/src/ide/Xcode/STK_XCode.xcodeproj/project.pbxproj +++ b/src/ide/Xcode/STK_XCode.xcodeproj/project.pbxproj @@ -35,6 +35,7 @@ 95263DEF0FD7471900CF5F92 /* highscores.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95263DE60FD7471900CF5F92 /* highscores.cpp */; }; 95263DF00FD7471900CF5F92 /* history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95263DE80FD7471900CF5F92 /* history.cpp */; }; 95263DF10FD7471900CF5F92 /* race_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95263DEA0FD7471900CF5F92 /* race_manager.cpp */; }; + 9528C71612D69494006E9167 /* particle_kind_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9528C71412D69494006E9167 /* particle_kind_manager.cpp */; }; 9528CC241291E7A10078A5EF /* binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9528CC221291E7A10078A5EF /* binding.cpp */; }; 952A1545103F66D600B1895D /* camera.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952A152D103F66D600B1895D /* camera.cpp */; }; 952A1546103F66D600B1895D /* explosion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952A152F103F66D600B1895D /* explosion.cpp */; }; @@ -388,6 +389,8 @@ 95263DE90FD7471900CF5F92 /* history.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = history.hpp; path = ../../race/history.hpp; sourceTree = SOURCE_ROOT; }; 95263DEA0FD7471900CF5F92 /* race_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = race_manager.cpp; path = ../../race/race_manager.cpp; sourceTree = SOURCE_ROOT; }; 95263DEB0FD7471900CF5F92 /* race_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = race_manager.hpp; path = ../../race/race_manager.hpp; sourceTree = SOURCE_ROOT; }; + 9528C71412D69494006E9167 /* particle_kind_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = particle_kind_manager.cpp; path = ../../graphics/particle_kind_manager.cpp; sourceTree = SOURCE_ROOT; }; + 9528C71512D69494006E9167 /* particle_kind_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = particle_kind_manager.hpp; path = ../../graphics/particle_kind_manager.hpp; sourceTree = SOURCE_ROOT; }; 9528CC221291E7A10078A5EF /* binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = binding.cpp; path = ../../input/binding.cpp; sourceTree = SOURCE_ROOT; }; 9528CC231291E7A10078A5EF /* binding.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = binding.hpp; path = ../../input/binding.hpp; sourceTree = SOURCE_ROOT; }; 952A152D103F66D600B1895D /* camera.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = camera.cpp; path = ../../graphics/camera.cpp; sourceTree = SOURCE_ROOT; }; @@ -1253,6 +1256,8 @@ 9542FC7612D3BDB000C00366 /* particle_emitter.hpp */, 9542FD4A12D3E0D700C00366 /* particle_kind.cpp */, 9542FD4B12D3E0D700C00366 /* particle_kind.hpp */, + 9528C71412D69494006E9167 /* particle_kind_manager.cpp */, + 9528C71512D69494006E9167 /* particle_kind_manager.hpp */, 952A153D103F66D600B1895D /* shadow.cpp */, 952A153E103F66D600B1895D /* shadow.hpp */, 952A153F103F66D600B1895D /* skid_marks.cpp */, @@ -2726,6 +2731,7 @@ 9538A56012CD094200CE3220 /* addon.cpp in Sources */, 9542FC7712D3BDB000C00366 /* particle_emitter.cpp in Sources */, 9542FD4C12D3E0D700C00366 /* particle_kind.cpp in Sources */, + 9528C71612D69494006E9167 /* particle_kind_manager.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 97ebcbdc2..791be7ec9 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -32,6 +32,7 @@ #include "graphics/material_manager.hpp" #include "graphics/particle_emitter.hpp" #include "graphics/particle_kind.hpp" +#include "graphics/particle_kind_manager.hpp" #include "graphics/shadow.hpp" #include "graphics/skid_marks.hpp" #include "graphics/slip_stream.hpp" @@ -89,7 +90,7 @@ Kart::Kart (const std::string& ident, int position, m_finish_time = 0.0f; m_shadow_enabled = false; m_shadow = NULL; - m_smoke_system = NULL; + m_terrain_particles = NULL; m_water_splash_system = NULL; m_nitro = NULL; m_slipstream = NULL; @@ -317,7 +318,7 @@ Kart::~Kart() sfx_manager->deleteSFX(m_beep_sound ); if(m_terrain_sound) sfx_manager->deleteSFX(m_terrain_sound); if(m_previous_terrain_sound) sfx_manager->deleteSFX(m_previous_terrain_sound); - if(m_smoke_system) delete m_smoke_system; + if(m_terrain_particles) delete m_terrain_particles; if(m_water_splash_system) delete m_water_splash_system; if(m_nitro) delete m_nitro; if(m_slipstream) delete m_slipstream; @@ -729,9 +730,9 @@ void Kart::update(float dt) m_attachment->update(dt); //smoke drawing control point - if ( UserConfigParams::m_graphical_effects ) + if (UserConfigParams::m_graphical_effects && m_terrain_particles) { - m_smoke_system->update(); + m_terrain_particles->update(); m_water_splash_system->update(dt); } // UserConfigParams::m_graphical_effects @@ -1449,9 +1450,8 @@ void Kart::loadData() // So it's easier not to move the particle system with the kart, and set // the position directly from the wheel coordinates. core::vector3df position(-getKartWidth()*0.35f, 0.06f, -getKartLength()*0.5f); - m_smoke_system = new ParticleEmitter( - new ParticleKind(file_manager->getDataFile("smoke.xml")), - position); + m_terrain_particles = new ParticleEmitter(ParticleKindManager::get()->getParticles("smoke.xml"), + position); } catch (std::runtime_error& e) { @@ -1553,24 +1553,51 @@ void Kart::updateGraphics(const Vec3& offset_xyz, -m_kart_model->getWheelGraphicsPosition(0).getY() ); center_shift.setY(y); - if(m_smoke_system) + if (m_terrain_particles) { - float f=0.0f; - if(getMaterial() && getMaterial()->hasSmoke() && - fabsf(m_controls.m_steer) > 0.8 && - isOnGround() ) - f=250.0f; - m_smoke_system->setCreationRate((m_skidding-1)*f); - - m_wheel_toggle = 1 - m_wheel_toggle; - const btWheelInfo &wi = getVehicle()->getWheelInfo(2 + m_wheel_toggle); - Vec3 c = wi.m_raycastInfo.m_contactPointWS; - - // FIXME: the X position is not yet always accurate. - m_smoke_system->setPosition(core::vector3df(c.getX() + 0.06f * (m_wheel_toggle ? +1 : -1), - c.getY(), - c.getZ() + 0.06f)); + float f = 0.0f; + const Material* material = getMaterial(); + if (material != NULL) + { + if (m_skidding) + { + const ParticleKind* pk = material->getParticlesWhen(Material::EMIT_ON_SKID); + if (pk != NULL) + { + m_terrain_particles->setParticleType(pk); + + if (fabsf(m_controls.m_steer) > 0.8 && isOnGround()) f = 250.0f; + + m_terrain_particles->setCreationRate((m_skidding-1)*f); + + m_wheel_toggle = 1 - m_wheel_toggle; + const btWheelInfo &wi = getVehicle()->getWheelInfo(2 + m_wheel_toggle); + Vec3 c = wi.m_raycastInfo.m_contactPointWS; + + // FIXME: the X position is not yet always accurate. + m_terrain_particles->setPosition(core::vector3df(c.getX() + 0.06f * (m_wheel_toggle ? +1 : -1), + c.getY(), + c.getZ() + 0.06f)); + } + else + { + m_terrain_particles->setCreationRate(0); + } + } + else // not skidding + { + const ParticleKind* pk = material->getParticlesWhen(Material::EMIT_ON_DRIVE); + if (pk != NULL) + { + m_terrain_particles->setParticleType(pk); + } + else + { + m_terrain_particles->setCreationRate(0); + } + } + } } if(m_water_splash_system) { diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 26f545e31..1da60d1c7 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -135,8 +135,8 @@ private: * stuck to the kart, i.e. the shadow would be flying, too). */ bool m_shadow_enabled; - /** Smoke from skidding. */ - ParticleEmitter *m_smoke_system; + /** Particle emitter used for terrain-specific effects (including but not limited too skidding). */ + ParticleEmitter *m_terrain_particles; /** Water splash when driving in water. */ WaterSplash *m_water_splash_system;