From 0dcb1fa792f834b8a2034b6d78ff9e811f902915 Mon Sep 17 00:00:00 2001 From: auria Date: Sun, 28 Aug 2011 16:15:42 +0000 Subject: [PATCH] Add support for particles on crash-reset, use them in XR591 as example; on the way add support in the particle emitter for decaying emission rates, and use this feature for nitro fire instead of manually applying the decay git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@9641 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- data/gfx/sparks.xml | 31 ++++ data/gfx/zipper_fire.xml | 3 +- src/graphics/material.cpp | 5 + src/graphics/material.hpp | 7 + src/graphics/particle_emitter.cpp | 232 +++++++++++++++++------------- src/graphics/particle_emitter.hpp | 9 +- src/graphics/particle_kind.cpp | 2 + src/graphics/particle_kind.hpp | 4 + src/karts/kart.cpp | 37 ++++- src/karts/kart.hpp | 3 + 10 files changed, 225 insertions(+), 108 deletions(-) create mode 100644 data/gfx/sparks.xml diff --git a/data/gfx/sparks.xml b/data/gfx/sparks.xml new file mode 100644 index 000000000..1ba130e8d --- /dev/null +++ b/data/gfx/sparks.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/gfx/zipper_fire.xml b/data/gfx/zipper_fire.xml index 33ae7ccc5..b45ddf99b 100644 --- a/data/gfx/zipper_fire.xml +++ b/data/gfx/zipper_fire.xml @@ -11,7 +11,8 @@ + max="1000" + decay_rate="800" /> get("mask", &m_mask); + if (m_crash_reset) + { + node->get("crash-reset-particles", &m_crash_reset_particles); + } + if (node->get("normal-map", &m_normal_map_tex)) { m_normal_map = true; diff --git a/src/graphics/material.hpp b/src/graphics/material.hpp index b643a0a89..c7f21e248 100644 --- a/src/graphics/material.hpp +++ b/src/graphics/material.hpp @@ -80,6 +80,10 @@ private: bool m_drive_reset; /** If a kart is rescued when crashing into this surface. */ bool m_crash_reset; + + /** Particles to show on crash-reset */ + std::string m_crash_reset_particles; + /** If the property should be ignored in the physics. Example would be * plants that a kart can just drive through. */ bool m_ignore; @@ -176,6 +180,9 @@ public: /** Returns if this material should trigger a rescue if a kart * crashes against it. */ bool isCrashReset () const { return m_crash_reset; } + + std::string getCrashResetParticles() const { return m_crash_reset_particles; } + bool highTireAdhesion () const { return m_high_tire_adhesion; } const std::string& getTexFname () const { return m_texname; } diff --git a/src/graphics/particle_emitter.cpp b/src/graphics/particle_emitter.cpp index 4a3a506d6..f1b81adc4 100644 --- a/src/graphics/particle_emitter.cpp +++ b/src/graphics/particle_emitter.cpp @@ -187,6 +187,8 @@ ParticleEmitter::ParticleEmitter(const ParticleKind* type, m_node = NULL; m_particle_type = NULL; m_parent = parent; + m_emission_decay_rate = 0; + setParticleType(type); assert(m_node != NULL); @@ -205,7 +207,8 @@ ParticleEmitter::~ParticleEmitter() } // ~ParticleEmitter //----------------------------------------------------------------------------- -void ParticleEmitter::update() + +void ParticleEmitter::update(float dt) { assert(m_magic_number == 0x58781325); @@ -222,6 +225,12 @@ void ParticleEmitter::update() transform.rotateVect(velocity); m_emitter->setDirection(velocity); + if (m_emission_decay_rate > 0) + { + m_max_rate = m_min_rate = std::max(0.0f, (m_min_rate - m_emission_decay_rate*dt)); + setCreationRate(m_min_rate); + } + // There seems to be no way to randomise the velocity for particles, // so we have to do this manually, by changing the default velocity. // Irrlicht expects velocity (called 'direction') in m/ms!! @@ -243,6 +252,9 @@ void ParticleEmitter::setCreationRate(float f) m_emitter->setMinParticlesPerSecond(int(f)); m_emitter->setMaxParticlesPerSecond(int(f)); + m_min_rate = f; + m_max_rate = f; + // FIXME: to work around irrlicht bug, when an emitter is paused by setting the rate // to 0 results in a massive emission when enabling it back. In irrlicht 1.8 // the node has a method called "clearParticles" that should be cleaner than this @@ -280,32 +292,35 @@ void ParticleEmitter::setPosition(const Vec3 &pos) void ParticleEmitter::setParticleType(const ParticleKind* type) { assert(m_magic_number == 0x58781325); - if (m_particle_type == type) return; // already the right type - - if (m_node != NULL) + bool isNewType = (m_particle_type != type); + if (isNewType) { - m_node->removeAll(); - m_node->removeAllAffectors(); - } - else - { - m_node = irr_driver->addParticleNode(); + if (m_node != NULL) + { + m_node->removeAll(); + m_node->removeAllAffectors(); + } + else + { + m_node = irr_driver->addParticleNode(); + } + + if (m_parent != NULL) + { + m_node->setParent(m_parent); + } + + m_particle_type = type; } - if (m_parent != NULL) - { - m_node->setParent(m_parent); - } - - - m_particle_type = type; + m_emission_decay_rate = type->getEmissionDecayRate(); 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(maxSize >= minSize); assert(lifeTimeMax >= lifeTimeMin); @@ -321,109 +336,124 @@ void ParticleEmitter::setParticleType(const ParticleKind* type) m_node->setName(debug_name.c_str()); } #endif + m_min_rate = type->getMinRate(); + m_max_rate = type->getMaxRate(); - video::SMaterial& mat0 = m_node->getMaterial(0); - - m_node->setPosition(m_position.toIrrVector()); - - if (material != NULL) + if (isNewType) { - assert(material->getTexture() != NULL); - material->setMaterialProperties(&mat0); - m_node->setMaterialTexture(0, material->getTexture()); - - mat0.ZWriteEnable = !material->isTransparent(); // disable z-buffer writes if material is transparent - } - else - { - m_node->setMaterialTexture(0, irr_driver->getTexture((file_manager->getDataDir() + "/gui/main_help.png").c_str())); - } - - switch (type->getShape()) - { - case EMITTER_POINT: + video::SMaterial& mat0 = m_node->getMaterial(0); + + m_node->setPosition(m_position.toIrrVector()); + + if (material != NULL) { - 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; + assert(material->getTexture() != NULL); + material->setMaterialProperties(&mat0); + m_node->setMaterialTexture(0, material->getTexture()); + + mat0.ZWriteEnable = !material->isTransparent(); // disable z-buffer writes if material is transparent + } + else + { + m_node->setMaterialTexture(0, irr_driver->getTexture((file_manager->getDataDir() + "/gui/main_help.png").c_str())); } - case EMITTER_BOX: + + switch (type->getShape()) { - const float box_size_x = type->getBoxSizeX()/2.0f; - const float box_size_y = type->getBoxSizeY()/2.0f; - - m_emitter = m_node->createBoxEmitter(core::aabbox3df(-box_size_x, -box_size_y, -0.6f, - box_size_x, box_size_y, -0.6f - type->getBoxSizeZ()), - 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 */ - ); - -#if VISUALIZE_BOX_EMITTER - if (m_parent != NULL) + case EMITTER_POINT: { - for (int x=0; x<2; x++) + 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; + + m_emitter = m_node->createBoxEmitter(core::aabbox3df(-box_size_x, -box_size_y, -0.6f, + box_size_x, box_size_y, -0.6f - type->getBoxSizeZ()), + 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 */ + ); + + #if VISUALIZE_BOX_EMITTER + if (m_parent != NULL) { - for (int y=0; y<2; y++) + for (int x=0; x<2; x++) { - for (int z=0; z<2; z++) + for (int y=0; y<2; y++) { - m_visualisation.push_back( - irr_driver->getSceneManager()->addSphereSceneNode(0.05f, 16, m_parent, -1, - core::vector3df((x ? box_size_x : -box_size_x), - (y ? box_size_y : -box_size_y), - -0.6 - (z ? 0 : type->getBoxSizeZ()))) - ); + for (int z=0; z<2; z++) + { + m_visualisation.push_back( + irr_driver->getSceneManager()->addSphereSceneNode(0.05f, 16, m_parent, -1, + core::vector3df((x ? box_size_x : -box_size_x), + (y ? box_size_y : -box_size_y), + -0.6 - (z ? 0 : type->getBoxSizeZ()))) + ); + } } } } + #endif + break; + } + default: + { + fprintf(stderr, "[ParticleEmitter] Unknown shape\n"); + return; } -#endif - break; - } - default: - { - fprintf(stderr, "[ParticleEmitter] Unknown shape\n"); - return; } } + else + { + m_emitter->setMinParticlesPerSecond(int(m_min_rate)); + m_emitter->setMaxParticlesPerSecond(int(m_max_rate)); + } 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 - scene::IParticleFadeOutAffector *af = m_node->createFadeOutParticleAffector(video::SColor(0, 255, 255, 255), - type->getFadeoutTime()); - m_node->addAffector(af); - af->drop(); - - if (type->getGravityStrength() != 0) + if (isNewType) { - scene::IParticleGravityAffector *gaf = m_node->createGravityAffector(core::vector3df(00.0f, type->getGravityStrength(), 0.0f), - type->getForceLostToGravityTime()); - m_node->addAffector(gaf); - gaf->drop(); - } - - const float fas = type->getFadeAwayStart(); - const float fae = type->getFadeAwayEnd(); - if (fas > 0.0f && fae > 0.0f) - { - FadeAwayAffector* faa = new FadeAwayAffector(fas*fas, fae*fae); - m_node->addAffector(faa); - faa->drop(); + m_node->setEmitter(m_emitter); // this grabs the emitter + m_emitter->drop(); // so we can drop our references + + scene::IParticleFadeOutAffector *af = m_node->createFadeOutParticleAffector(video::SColor(0, 255, 255, 255), + type->getFadeoutTime()); + m_node->addAffector(af); + af->drop(); + + if (type->getGravityStrength() != 0) + { + scene::IParticleGravityAffector *gaf = m_node->createGravityAffector(core::vector3df(00.0f, type->getGravityStrength(), 0.0f), + type->getForceLostToGravityTime()); + m_node->addAffector(gaf); + gaf->drop(); + } + + const float fas = type->getFadeAwayStart(); + const float fae = type->getFadeAwayEnd(); + if (fas > 0.0f && fae > 0.0f) + { + FadeAwayAffector* faa = new FadeAwayAffector(fas*fas, fae*fae); + m_node->addAffector(faa); + faa->drop(); + } } } // setParticleType @@ -436,6 +466,8 @@ void ParticleEmitter::addHeightMapAffector(Track* t) hmca->drop(); } +//----------------------------------------------------------------------------- + void ParticleEmitter::resizeBox(float size) { scene::IParticleBoxEmitter* emitter = (scene::IParticleBoxEmitter*)m_emitter; diff --git a/src/graphics/particle_emitter.hpp b/src/graphics/particle_emitter.hpp index 7950d1dee..2809cbd6d 100644 --- a/src/graphics/particle_emitter.hpp +++ b/src/graphics/particle_emitter.hpp @@ -66,13 +66,20 @@ private: unsigned int m_magic_number; + /** Decay of emission rate, in particles per second */ + int m_emission_decay_rate; + + /** The irrlicht emitter contains this info, but as an int. We want it as a float */ + float m_min_rate, m_max_rate; + + public: ParticleEmitter (const ParticleKind* type, const Vec3 &position, scene::ISceneNode* parent = NULL); virtual ~ParticleEmitter(); - virtual void update (); + virtual void update (float dt); void setCreationRate(float f); int getCreationRate(); diff --git a/src/graphics/particle_kind.cpp b/src/graphics/particle_kind.cpp index 98a6405c2..3f71313b1 100644 --- a/src/graphics/particle_kind.cpp +++ b/src/graphics/particle_kind.cpp @@ -51,6 +51,7 @@ ParticleKind::ParticleKind(const std::string file) : m_min_start_color(255,255,2 m_fade_away_start = -1.0f; m_fade_away_end = -1.0f; m_force_lost_to_gravity_time = 1000; + m_emission_decay_rate = 0; // ----- Read XML file @@ -131,6 +132,7 @@ ParticleKind::ParticleKind(const std::string file) : m_min_start_color(255,255,2 { rate->get("min", &m_min_rate); rate->get("max", &m_max_rate); + rate->get("decay_rate", &m_emission_decay_rate); } //std::cout << "m_min_rate = " << m_min_rate << "\n"; diff --git a/src/graphics/particle_kind.hpp b/src/graphics/particle_kind.hpp index 9940b79bc..7605ee0d1 100644 --- a/src/graphics/particle_kind.hpp +++ b/src/graphics/particle_kind.hpp @@ -81,6 +81,8 @@ private: /** Distance from camera at which particles start fading out, or negative if disabled */ float m_fade_away_start, m_fade_away_end; + int m_emission_decay_rate; + std::string m_name; std::string m_material_file; @@ -137,6 +139,8 @@ public: void setBoxSizeY (float newVal) { m_box_y = newVal; } void setBoxSizeZ (float newVal) { m_box_z = newVal; } + int getEmissionDecayRate() const { return m_emission_decay_rate; } + std::string getName() const { return m_name; } }; diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 9e9c4e964..227e010bb 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -104,6 +104,7 @@ Kart::Kart (const std::string& ident, Track* track, int position, bool is_first_ m_nitro_kind = NULL; m_zipper_fire = NULL; m_zipper_fire_kind = NULL; + m_collision_particles = NULL; m_slipstream = NULL; m_skidmarks = NULL; m_camera = NULL; @@ -390,6 +391,7 @@ Kart::~Kart() if(m_nitro) delete m_nitro; if(m_nitro_kind) delete m_nitro_kind; if(m_zipper_fire) delete m_zipper_fire; + if(m_collision_particles) delete m_collision_particles; if(m_zipper_fire_kind) delete m_zipper_fire_kind; if(m_slipstream) delete m_slipstream; if(m_rain) delete m_rain; @@ -502,6 +504,7 @@ void Kart::reset() m_attachment->clear(); m_nitro->setCreationRate(0.0f); m_zipper_fire->setCreationRate(0.0f); + if (m_collision_particles) m_collision_particles->setCreationRate(0.0f); m_powerup.reset(); m_race_position = m_initial_position; @@ -838,7 +841,7 @@ void Kart::update(float dt) //smoke drawing control point if (UserConfigParams::m_graphical_effects) { - if (m_terrain_particles) m_terrain_particles->update(); + if (m_terrain_particles) m_terrain_particles->update(dt); if (m_rain) { m_rain->setPosition( getCamera()->getCameraSceneNode()->getPosition() ); @@ -846,8 +849,9 @@ void Kart::update(float dt) } } // UserConfigParams::m_graphical_effects - m_nitro->update(); - m_zipper_fire->update(); + m_nitro->update(dt); + m_zipper_fire->update(dt); + if (m_collision_particles) m_collision_particles->update(dt); updatePhysics(dt); @@ -1337,7 +1341,31 @@ void Kart::crashed(Kart *k, const Material *m) * for 0.5 seconds after a crash. */ if(m && m->isCrashReset() && !playingEmergencyAnimation()) + { + std::string particles = m->getCrashResetParticles(); + if (particles.size() > 0) + { + ParticleKind* kind = ParticleKindManager::get()->getParticles(particles); + if (kind != NULL) + { + if (m_collision_particles == NULL) + { + Vec3 position(-getKartWidth()*0.35f, 0.06f, getKartLength()*0.5f); + m_collision_particles = new ParticleEmitter(kind, position, getNode()); + } + else + { + m_collision_particles->setParticleType(kind); + } + } + else + { + fprintf(stderr, "Unknown particles kind <%s> in material crash-reset properties\n", particles.c_str()); + } + } + forceRescue(); + } if(World::getWorld()->getTime()-m_time_last_crash < 0.5f) return; m_time_last_crash = World::getWorld()->getTime(); @@ -1942,11 +1970,8 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz, if (m_zipper_fire) { - // the std::max call is there to let fire fade out smoothly instead of stopping sharply if (m_zipper_fire->getCreationRate() > 0) { - m_zipper_fire->setCreationRate(std::max(m_zipper_fire->getCreationRate() - dt*800.0f, 0.0f)); - // the emitter box should spread from last frame's position to the current position // if we want nitro to be emitted in a smooth, continuous flame and not in blobs m_zipper_fire->resizeBox(std::max(0.25f, getSpeed()*dt)); diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 541ad5772..01be1cae2 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -163,6 +163,9 @@ private: /** The particle kind for the nitro. */ ParticleKind *m_zipper_fire_kind; + /** For collisions */ + ParticleEmitter *m_collision_particles; + /** Handles all slipstreaming. */ SlipStream *m_slipstream;