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
This commit is contained in:
auria 2011-08-28 16:15:42 +00:00
parent 2c677b46c0
commit 0dcb1fa792
10 changed files with 225 additions and 108 deletions

31
data/gfx/sparks.xml Normal file
View File

@ -0,0 +1,31 @@
<?xml version="1.0"?>
<particles emitter="box" box_x="0.5" box_y="0.5" box_z="0.2">
<spreading angle="180" />
<velocity x="0.000"
y="0.004"
z="0.000" />
<material file="flaring-star.png" />
<!-- Amount of particles emitted per second -->
<rate min="200"
max="200"
decay_rate="200" />
<!-- Minimal and maximal lifetime of a particle, in milliseconds. -->
<lifetime min="500"
max="600" />
<!-- Size of the particles -->
<size min="0.1"
max="0.2" />
<color min="255 255 255"
max="255 255 255" />
<!-- How much time in milliseconds before the particle is fully faded out -->
<fadeout time="500" />
</particles>

View File

@ -11,7 +11,8 @@
<!-- Amount of particles emitted per second --> <!-- Amount of particles emitted per second -->
<rate min="800" <rate min="800"
max="1000" /> max="1000"
decay_rate="800" />
<!-- Minimal and maximal lifetime of a particle, in milliseconds. --> <!-- Minimal and maximal lifetime of a particle, in milliseconds. -->
<lifetime min="100" <lifetime min="100"

View File

@ -98,6 +98,11 @@ Material::Material(const XMLNode *node, int index)
node->get("mask", &m_mask); node->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)) if (node->get("normal-map", &m_normal_map_tex))
{ {
m_normal_map = true; m_normal_map = true;

View File

@ -80,6 +80,10 @@ private:
bool m_drive_reset; bool m_drive_reset;
/** If a kart is rescued when crashing into this surface. */ /** If a kart is rescued when crashing into this surface. */
bool m_crash_reset; 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 /** If the property should be ignored in the physics. Example would be
* plants that a kart can just drive through. */ * plants that a kart can just drive through. */
bool m_ignore; bool m_ignore;
@ -176,6 +180,9 @@ public:
/** Returns if this material should trigger a rescue if a kart /** Returns if this material should trigger a rescue if a kart
* crashes against it. */ * crashes against it. */
bool isCrashReset () const { return m_crash_reset; } bool isCrashReset () const { return m_crash_reset; }
std::string getCrashResetParticles() const { return m_crash_reset_particles; }
bool highTireAdhesion () const { return m_high_tire_adhesion; } bool highTireAdhesion () const { return m_high_tire_adhesion; }
const std::string& const std::string&
getTexFname () const { return m_texname; } getTexFname () const { return m_texname; }

View File

@ -187,6 +187,8 @@ ParticleEmitter::ParticleEmitter(const ParticleKind* type,
m_node = NULL; m_node = NULL;
m_particle_type = NULL; m_particle_type = NULL;
m_parent = parent; m_parent = parent;
m_emission_decay_rate = 0;
setParticleType(type); setParticleType(type);
assert(m_node != NULL); assert(m_node != NULL);
@ -205,7 +207,8 @@ ParticleEmitter::~ParticleEmitter()
} // ~ParticleEmitter } // ~ParticleEmitter
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void ParticleEmitter::update()
void ParticleEmitter::update(float dt)
{ {
assert(m_magic_number == 0x58781325); assert(m_magic_number == 0x58781325);
@ -222,6 +225,12 @@ void ParticleEmitter::update()
transform.rotateVect(velocity); transform.rotateVect(velocity);
m_emitter->setDirection(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, // There seems to be no way to randomise the velocity for particles,
// so we have to do this manually, by changing the default velocity. // so we have to do this manually, by changing the default velocity.
// Irrlicht expects velocity (called 'direction') in m/ms!! // Irrlicht expects velocity (called 'direction') in m/ms!!
@ -243,6 +252,9 @@ void ParticleEmitter::setCreationRate(float f)
m_emitter->setMinParticlesPerSecond(int(f)); m_emitter->setMinParticlesPerSecond(int(f));
m_emitter->setMaxParticlesPerSecond(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 // 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 // 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 // the node has a method called "clearParticles" that should be cleaner than this
@ -280,8 +292,9 @@ void ParticleEmitter::setPosition(const Vec3 &pos)
void ParticleEmitter::setParticleType(const ParticleKind* type) void ParticleEmitter::setParticleType(const ParticleKind* type)
{ {
assert(m_magic_number == 0x58781325); assert(m_magic_number == 0x58781325);
if (m_particle_type == type) return; // already the right type bool isNewType = (m_particle_type != type);
if (isNewType)
{
if (m_node != NULL) if (m_node != NULL)
{ {
m_node->removeAll(); m_node->removeAll();
@ -297,8 +310,10 @@ void ParticleEmitter::setParticleType(const ParticleKind* type)
m_node->setParent(m_parent); m_node->setParent(m_parent);
} }
m_particle_type = type; m_particle_type = type;
}
m_emission_decay_rate = type->getEmissionDecayRate();
Material* material = type->getMaterial(); Material* material = type->getMaterial();
const float minSize = type->getMinSize(); const float minSize = type->getMinSize();
@ -321,7 +336,11 @@ void ParticleEmitter::setParticleType(const ParticleKind* type)
m_node->setName(debug_name.c_str()); m_node->setName(debug_name.c_str());
} }
#endif #endif
m_min_rate = type->getMinRate();
m_max_rate = type->getMaxRate();
if (isNewType)
{
video::SMaterial& mat0 = m_node->getMaterial(0); video::SMaterial& mat0 = m_node->getMaterial(0);
m_node->setPosition(m_position.toIrrVector()); m_node->setPosition(m_position.toIrrVector());
@ -339,6 +358,7 @@ void ParticleEmitter::setParticleType(const ParticleKind* type)
m_node->setMaterialTexture(0, irr_driver->getTexture((file_manager->getDataDir() + "/gui/main_help.png").c_str())); m_node->setMaterialTexture(0, irr_driver->getTexture((file_manager->getDataDir() + "/gui/main_help.png").c_str()));
} }
switch (type->getShape()) switch (type->getShape())
{ {
case EMITTER_POINT: case EMITTER_POINT:
@ -370,7 +390,7 @@ void ParticleEmitter::setParticleType(const ParticleKind* type)
m_particle_type->getAngleSpread() /* angle */ m_particle_type->getAngleSpread() /* angle */
); );
#if VISUALIZE_BOX_EMITTER #if VISUALIZE_BOX_EMITTER
if (m_parent != NULL) if (m_parent != NULL)
{ {
for (int x=0; x<2; x++) for (int x=0; x<2; x++)
@ -389,7 +409,7 @@ void ParticleEmitter::setParticleType(const ParticleKind* type)
} }
} }
} }
#endif #endif
break; break;
} }
default: default:
@ -398,9 +418,18 @@ void ParticleEmitter::setParticleType(const ParticleKind* type)
return; 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->setMinStartSize(core::dimension2df(minSize, minSize));
m_emitter->setMaxStartSize(core::dimension2df(maxSize, maxSize)); m_emitter->setMaxStartSize(core::dimension2df(maxSize, maxSize));
if (isNewType)
{
m_node->setEmitter(m_emitter); // this grabs the emitter m_node->setEmitter(m_emitter); // this grabs the emitter
m_emitter->drop(); // so we can drop our references m_emitter->drop(); // so we can drop our references
@ -425,6 +454,7 @@ void ParticleEmitter::setParticleType(const ParticleKind* type)
m_node->addAffector(faa); m_node->addAffector(faa);
faa->drop(); faa->drop();
} }
}
} // setParticleType } // setParticleType
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -436,6 +466,8 @@ void ParticleEmitter::addHeightMapAffector(Track* t)
hmca->drop(); hmca->drop();
} }
//-----------------------------------------------------------------------------
void ParticleEmitter::resizeBox(float size) void ParticleEmitter::resizeBox(float size)
{ {
scene::IParticleBoxEmitter* emitter = (scene::IParticleBoxEmitter*)m_emitter; scene::IParticleBoxEmitter* emitter = (scene::IParticleBoxEmitter*)m_emitter;

View File

@ -66,13 +66,20 @@ private:
unsigned int m_magic_number; 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: public:
ParticleEmitter (const ParticleKind* type, ParticleEmitter (const ParticleKind* type,
const Vec3 &position, const Vec3 &position,
scene::ISceneNode* parent = NULL); scene::ISceneNode* parent = NULL);
virtual ~ParticleEmitter(); virtual ~ParticleEmitter();
virtual void update (); virtual void update (float dt);
void setCreationRate(float f); void setCreationRate(float f);
int getCreationRate(); int getCreationRate();

View File

@ -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_start = -1.0f;
m_fade_away_end = -1.0f; m_fade_away_end = -1.0f;
m_force_lost_to_gravity_time = 1000; m_force_lost_to_gravity_time = 1000;
m_emission_decay_rate = 0;
// ----- Read XML file // ----- 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("min", &m_min_rate);
rate->get("max", &m_max_rate); rate->get("max", &m_max_rate);
rate->get("decay_rate", &m_emission_decay_rate);
} }
//std::cout << "m_min_rate = " << m_min_rate << "\n"; //std::cout << "m_min_rate = " << m_min_rate << "\n";

View File

@ -81,6 +81,8 @@ private:
/** Distance from camera at which particles start fading out, or negative if disabled */ /** Distance from camera at which particles start fading out, or negative if disabled */
float m_fade_away_start, m_fade_away_end; float m_fade_away_start, m_fade_away_end;
int m_emission_decay_rate;
std::string m_name; std::string m_name;
std::string m_material_file; std::string m_material_file;
@ -137,6 +139,8 @@ public:
void setBoxSizeY (float newVal) { m_box_y = newVal; } void setBoxSizeY (float newVal) { m_box_y = newVal; }
void setBoxSizeZ (float newVal) { m_box_z = newVal; } void setBoxSizeZ (float newVal) { m_box_z = newVal; }
int getEmissionDecayRate() const { return m_emission_decay_rate; }
std::string getName() const { return m_name; } std::string getName() const { return m_name; }
}; };

View File

@ -104,6 +104,7 @@ Kart::Kart (const std::string& ident, Track* track, int position, bool is_first_
m_nitro_kind = NULL; m_nitro_kind = NULL;
m_zipper_fire = NULL; m_zipper_fire = NULL;
m_zipper_fire_kind = NULL; m_zipper_fire_kind = NULL;
m_collision_particles = NULL;
m_slipstream = NULL; m_slipstream = NULL;
m_skidmarks = NULL; m_skidmarks = NULL;
m_camera = NULL; m_camera = NULL;
@ -390,6 +391,7 @@ Kart::~Kart()
if(m_nitro) delete m_nitro; if(m_nitro) delete m_nitro;
if(m_nitro_kind) delete m_nitro_kind; if(m_nitro_kind) delete m_nitro_kind;
if(m_zipper_fire) delete m_zipper_fire; 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_zipper_fire_kind) delete m_zipper_fire_kind;
if(m_slipstream) delete m_slipstream; if(m_slipstream) delete m_slipstream;
if(m_rain) delete m_rain; if(m_rain) delete m_rain;
@ -502,6 +504,7 @@ void Kart::reset()
m_attachment->clear(); m_attachment->clear();
m_nitro->setCreationRate(0.0f); m_nitro->setCreationRate(0.0f);
m_zipper_fire->setCreationRate(0.0f); m_zipper_fire->setCreationRate(0.0f);
if (m_collision_particles) m_collision_particles->setCreationRate(0.0f);
m_powerup.reset(); m_powerup.reset();
m_race_position = m_initial_position; m_race_position = m_initial_position;
@ -838,7 +841,7 @@ void Kart::update(float dt)
//smoke drawing control point //smoke drawing control point
if (UserConfigParams::m_graphical_effects) 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) if (m_rain)
{ {
m_rain->setPosition( getCamera()->getCameraSceneNode()->getPosition() ); m_rain->setPosition( getCamera()->getCameraSceneNode()->getPosition() );
@ -846,8 +849,9 @@ void Kart::update(float dt)
} }
} // UserConfigParams::m_graphical_effects } // UserConfigParams::m_graphical_effects
m_nitro->update(); m_nitro->update(dt);
m_zipper_fire->update(); m_zipper_fire->update(dt);
if (m_collision_particles) m_collision_particles->update(dt);
updatePhysics(dt); updatePhysics(dt);
@ -1337,7 +1341,31 @@ void Kart::crashed(Kart *k, const Material *m)
* for 0.5 seconds after a crash. * for 0.5 seconds after a crash.
*/ */
if(m && m->isCrashReset() && !playingEmergencyAnimation()) 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(); forceRescue();
}
if(World::getWorld()->getTime()-m_time_last_crash < 0.5f) return; if(World::getWorld()->getTime()-m_time_last_crash < 0.5f) return;
m_time_last_crash = World::getWorld()->getTime(); m_time_last_crash = World::getWorld()->getTime();
@ -1942,11 +1970,8 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz,
if (m_zipper_fire) 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) 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 // 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 // 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)); m_zipper_fire->resizeBox(std::max(0.25f, getSpeed()*dt));

View File

@ -163,6 +163,9 @@ private:
/** The particle kind for the nitro. */ /** The particle kind for the nitro. */
ParticleKind *m_zipper_fire_kind; ParticleKind *m_zipper_fire_kind;
/** For collisions */
ParticleEmitter *m_collision_particles;
/** Handles all slipstreaming. */ /** Handles all slipstreaming. */
SlipStream *m_slipstream; SlipStream *m_slipstream;