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:
parent
2c677b46c0
commit
0dcb1fa792
31
data/gfx/sparks.xml
Normal file
31
data/gfx/sparks.xml
Normal 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>
|
@ -11,7 +11,8 @@
|
||||
|
||||
<!-- Amount of particles emitted per second -->
|
||||
<rate min="800"
|
||||
max="1000" />
|
||||
max="1000"
|
||||
decay_rate="800" />
|
||||
|
||||
<!-- Minimal and maximal lifetime of a particle, in milliseconds. -->
|
||||
<lifetime min="100"
|
||||
|
@ -98,6 +98,11 @@ Material::Material(const XMLNode *node, int index)
|
||||
|
||||
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))
|
||||
{
|
||||
m_normal_map = true;
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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; }
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user