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,32 +292,35 @@ 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)
{ {
m_node->removeAll(); if (m_node != NULL)
m_node->removeAllAffectors(); {
} m_node->removeAll();
else m_node->removeAllAffectors();
{ }
m_node = irr_driver->addParticleNode(); else
{
m_node = irr_driver->addParticleNode();
}
if (m_parent != NULL)
{
m_node->setParent(m_parent);
}
m_particle_type = type;
} }
if (m_parent != NULL) m_emission_decay_rate = type->getEmissionDecayRate();
{
m_node->setParent(m_parent);
}
m_particle_type = type;
Material* material = type->getMaterial(); Material* material = type->getMaterial();
const float minSize = type->getMinSize(); const float minSize = type->getMinSize();
const float maxSize = type->getMaxSize(); const float maxSize = type->getMaxSize();
const int lifeTimeMin = type->getMinLifetime(); const int lifeTimeMin = type->getMinLifetime();
const int lifeTimeMax = type->getMaxLifetime(); const int lifeTimeMax = type->getMaxLifetime();
assert(maxSize >= minSize); assert(maxSize >= minSize);
assert(lifeTimeMax >= lifeTimeMin); assert(lifeTimeMax >= lifeTimeMin);
@ -321,109 +336,124 @@ 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();
video::SMaterial& mat0 = m_node->getMaterial(0); if (isNewType)
m_node->setPosition(m_position.toIrrVector());
if (material != NULL)
{ {
assert(material->getTexture() != NULL); video::SMaterial& mat0 = m_node->getMaterial(0);
material->setMaterialProperties(&mat0);
m_node->setMaterialTexture(0, material->getTexture()); m_node->setPosition(m_position.toIrrVector());
mat0.ZWriteEnable = !material->isTransparent(); // disable z-buffer writes if material is transparent if (material != NULL)
}
else
{
m_node->setMaterialTexture(0, irr_driver->getTexture((file_manager->getDataDir() + "/gui/main_help.png").c_str()));
}
switch (type->getShape())
{
case EMITTER_POINT:
{ {
m_emitter = m_node->createPointEmitter(core::vector3df(m_particle_type->getVelocityX(), assert(material->getTexture() != NULL);
m_particle_type->getVelocityY(), material->setMaterialProperties(&mat0);
m_particle_type->getVelocityZ()), // velocity in m/ms m_node->setMaterialTexture(0, material->getTexture());
type->getMinRate(), type->getMaxRate(),
type->getMinColor(), type->getMaxColor(), mat0.ZWriteEnable = !material->isTransparent(); // disable z-buffer writes if material is transparent
lifeTimeMin, lifeTimeMax, }
m_particle_type->getAngleSpread() /* angle */ else
); {
break; 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; case EMITTER_POINT:
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 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( for (int z=0; z<2; z++)
irr_driver->getSceneManager()->addSphereSceneNode(0.05f, 16, m_parent, -1, {
core::vector3df((x ? box_size_x : -box_size_x), m_visualisation.push_back(
(y ? box_size_y : -box_size_y), irr_driver->getSceneManager()->addSphereSceneNode(0.05f, 16, m_parent, -1,
-0.6 - (z ? 0 : type->getBoxSizeZ()))) 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->setMinStartSize(core::dimension2df(minSize, minSize));
m_emitter->setMaxStartSize(core::dimension2df(maxSize, maxSize)); 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), if (isNewType)
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), m_node->setEmitter(m_emitter); // this grabs the emitter
type->getForceLostToGravityTime()); m_emitter->drop(); // so we can drop our references
m_node->addAffector(gaf);
gaf->drop(); scene::IParticleFadeOutAffector *af = m_node->createFadeOutParticleAffector(video::SColor(0, 255, 255, 255),
} type->getFadeoutTime());
m_node->addAffector(af);
const float fas = type->getFadeAwayStart(); af->drop();
const float fae = type->getFadeAwayEnd();
if (fas > 0.0f && fae > 0.0f) if (type->getGravityStrength() != 0)
{ {
FadeAwayAffector* faa = new FadeAwayAffector(fas*fas, fae*fae); scene::IParticleGravityAffector *gaf = m_node->createGravityAffector(core::vector3df(00.0f, type->getGravityStrength(), 0.0f),
m_node->addAffector(faa); type->getForceLostToGravityTime());
faa->drop(); 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 } // 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;