New XML-based particle system. May need a few tweaks

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@7255 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
auria 2011-01-05 01:39:18 +00:00
parent a3df5843aa
commit ec3c1f76a0
11 changed files with 375 additions and 47 deletions

26
data/nitro.xml Normal file
View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<particles emitter="box">
<spreading value="0.002" />
<material file="nitro-particle.png" />
<!-- Amount of particles emitted per second -->
<rate min="5"
max="10" />
<!-- Minimal and maximal lifetime of a particle, in milliseconds. -->
<lifetime min="1000"
max="2000" />
<!-- Size of the particles -->
<size min="0.22"
max="0.5" />
<!-- Doesn't seem to work -->
<color min="255 255 255"
max="255 255 255" />
<!-- How much time in milliseconds before the particle is fully faded out -->
<fadeout time="1500" />
</particles>

26
data/smoke.xml Normal file
View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<particles emitter="box">
<spreading value="0.003" />
<material file="smoke.png" />
<!-- Amount of particles emitted per second -->
<rate min="5"
max="10" />
<!-- Minimal and maximal lifetime of a particle, in milliseconds. -->
<lifetime min="1000"
max="2000" />
<!-- Size of the particles -->
<size min="0.2"
max="0.66" />
<!-- Doesn't seem to work -->
<color min="255 255 255"
max="255 255 255" />
<!-- How much time in milliseconds before the particle is fully faded out -->
<fadeout time="1500" />
</particles>

View File

@ -73,6 +73,8 @@ supertuxkart_SOURCES = \
graphics/moving_texture.hpp \
graphics/particle_emitter.cpp \
graphics/particle_emitter.hpp \
graphics/particle_kind.cpp \
graphics/particle_kind.hpp \
graphics/shadow.cpp \
graphics/shadow.hpp \
graphics/skid_marks.cpp \

View File

@ -21,18 +21,26 @@
#include "graphics/material.hpp"
#include "graphics/material_manager.hpp"
#include "graphics/particle_kind.hpp"
#include "graphics/irr_driver.hpp"
#include "io/file_manager.hpp"
#include "utils/constants.hpp"
ParticleEmitter::ParticleEmitter(float particleSize, core::vector3df position, Material* material,
int minParticlesPerSecond, int maxParticlesPerSecond,
video::SColor minStartColor, video::SColor maxStartColor,
int lifeTimeMin, int lifeTimeMax, int maxAngle, int fadeOutTime,
float directionMultiplier, float minSize, float maxSize, scene::ISceneNode* parent)
ParticleEmitter::ParticleEmitter(ParticleKind* type, core::vector3df position,
scene::ISceneNode* parent)
{
m_direction_multiplier = directionMultiplier;
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() + ")";
@ -53,20 +61,22 @@ ParticleEmitter::ParticleEmitter(float particleSize, core::vector3df position, M
m_node->setMaterialTexture(0, material->getTexture());
// FIXME: does the maxAngle param work at all??
m_emitter = m_node->createPointEmitter(core::vector3df(0.0f, 0.3f, 0.0f), // velocity in m/ms
minParticlesPerSecond, maxParticlesPerSecond,
minStartColor, maxStartColor,
// FIXME: the min and max color params don't appear to work
m_emitter = m_node->createPointEmitter(core::vector3df(0.0f, 0.0f, 0.0f), // velocity in m/ms
type->getMinRate(), type->getMaxRate(),
type->getMinColor(), type->getMaxColor(),
lifeTimeMin, lifeTimeMax,
maxAngle
0 /* angle */
);
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: fade-out color doesn't seem to quite work
scene::IParticleFadeOutAffector *af = m_node->createFadeOutParticleAffector(video::SColor(0, 255, 0, 0), fadeOutTime);
// 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, 0, 0, 0),
type->getFadeoutTime());
m_node->addAffector(af);
af->drop();
@ -86,12 +96,14 @@ void ParticleEmitter::update()
// No particles to emit, no need to change the speed
if (m_emitter->getMinParticlesPerSecond() == 0) return;
const float spreading = m_particle_type->getSpreadFactor();
// 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!!
Vec3 dir(cos(DEGREE_TO_RAD*(rand()%180))*m_direction_multiplier,
sin(DEGREE_TO_RAD*(rand()%100))*m_direction_multiplier,
sin(DEGREE_TO_RAD*(rand()%180))*m_direction_multiplier);
Vec3 dir(cos(DEGREE_TO_RAD*(rand()%180))*spreading,
sin(DEGREE_TO_RAD*(rand()%100))*spreading,
sin(DEGREE_TO_RAD*(rand()%180))*spreading);
m_emitter->setDirection(dir.toIrrVector());
} // update

View File

@ -26,6 +26,7 @@
using namespace irr;
class Material;
class ParticleKind;
/**
* \brief manages smoke particle effects
@ -41,18 +42,12 @@ private:
/** The emitters. Access to these is needed to adjust the number of
* particles per second. */
scene::IParticleEmitter *m_emitter;
/** Size of the particles. */
float m_particle_size;
float m_direction_multiplier;
ParticleKind *m_particle_type;
public:
ParticleEmitter (float particleSize, core::vector3df position, Material* material,
int minParticlesPerSeconds, int maxParticlesPerSecond,
video::SColor minStartColor, video::SColor maxStartColor,
int lifeTimeMin, int lifeTimeMax, int maxAngle, int fadeOutTime,
float directionMultiplier, float minSize, float maxSize,
ParticleEmitter (ParticleKind* type, core::vector3df position,
scene::ISceneNode* parent = NULL);
virtual ~ParticleEmitter();
virtual void update ();

View File

@ -0,0 +1,154 @@
// $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.hpp"
#include "graphics/material.hpp"
#include "graphics/material_manager.hpp"
#include "graphics/irr_driver.hpp"
#include "io/file_manager.hpp"
#include "io/xml_node.hpp"
#include "utils/constants.hpp"
#include <stdexcept>
ParticleKind::ParticleKind(const std::string file) : m_min_start_color(255,255,255,255), m_max_start_color(255,255,255,255)
{
// ---- Initial values to prevent readin uninitialized values
m_max_size = 0.5;
m_min_size = 0.5;
m_spread_factor = 0.001;
m_shape = EMITTER_POINT;
m_material = NULL;
m_min_rate = 10;
m_max_rate = 10;
m_lifetime_min = 400;
m_lifetime_max = 400;
m_fadeout_time = 400;
// ----- Read XML file
//std::cout << "==== READING " << file << " ====\n";
XMLNode* xml = file_manager->createXMLTree(file);
if (xml == NULL)
{
throw std::runtime_error("[ParticleKind] Cannot find file " + file);
}
if (xml->getName() != "particles")
{
delete xml;
throw std::runtime_error("[ParticleKind] No <particles> main node in smoke.xml");
}
// ------------------------------------------------------------------------
std::string emitterShape = "point";
xml->get("emitter", &emitterShape);
if (emitterShape == "point")
{
m_shape = EMITTER_POINT;
}
else if (emitterShape == "box")
{
m_shape = EMITTER_BOX;
}
else
{
std::cerr << "[ParticleKind] <particles> main node has unknown value for attribute 'emitter'\n";
m_shape = EMITTER_POINT;
}
// ------------------------------------------------------------------------
const XMLNode* spreading = xml->getNode("spreading");
spreading->get("value", &m_spread_factor);
//std::cout << "m_spread_factor = " << m_spread_factor << "\n";
// ------------------------------------------------------------------------
const XMLNode* material = xml->getNode("material");
std::string materialFile;
material->get("file", &materialFile);
if (materialFile.size() == 0)
{
delete xml;
throw std::runtime_error("[ParticleKind] <material> tag has invalid 'file' attribute");
}
m_material = material_manager->getMaterial(materialFile);
if (m_material->getTexture() == NULL)
{
throw std::runtime_error("[ParticleKind] Cannot locate file " + materialFile);
}
// ------------------------------------------------------------------------
const XMLNode* rate = xml->getNode("rate");
rate->get("min", &m_min_rate);
rate->get("max", &m_max_rate);
//std::cout << "m_min_rate = " << m_min_rate << "\n";
//std::cout << "m_max_rate = " << m_max_rate << "\n";
// ------------------------------------------------------------------------
const XMLNode* lifetime = xml->getNode("lifetime");
lifetime->get("min", &m_lifetime_min);
lifetime->get("max", &m_lifetime_max);
//std::cout << "m_lifetime_min = " << m_lifetime_min << "\n";
//std::cout << "m_lifetime_max = " << m_lifetime_max << "\n";
// ------------------------------------------------------------------------
const XMLNode* size = xml->getNode("size");
//size->get("default", &m_particle_size);
size->get("min", &m_min_size);
size->get("max", &m_max_size);
//std::cout << "m_particle_size = " << m_particle_size << "\n";
//std::cout << "m_min_size = " << m_min_size << "\n";
//std::cout << "m_max_size = " << m_max_size << "\n";
// ------------------------------------------------------------------------
const XMLNode* color = xml->getNode("color");
video::SColor minColor;
video::SColor maxColor;
color->get("min", &m_min_start_color);
color->get("max", &m_max_start_color);
// ------------------------------------------------------------------------
const XMLNode* fadeout = xml->getNode("fadeout");
fadeout->get("time", &m_fadeout_time);
//std::cout << "m_fadeout_time = " << m_fadeout_time << "\n";
// ------------------------------------------------------------------------
delete xml;
}

View File

@ -0,0 +1,99 @@
// $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.
#ifndef HEADER_PARTICLE_KIND_HPP
#define HEADER_PARTICLE_KIND_HPP
#include "utils/no_copy.hpp"
#include <string>
#include "irrlicht.h"
using namespace irr;
class Material;
enum EmitterShape
{
EMITTER_POINT,
EMITTER_BOX
};
/**
* \brief type of particles
* \ingroup graphics
*/
class ParticleKind : public NoCopy
{
private:
/** Size of the particles. */
//float m_particle_size;
float m_max_size;
float m_min_size;
float m_spread_factor;
EmitterShape m_shape;
Material* m_material;
/** Minimal emission rate in particles per second */
int m_min_rate;
/** Maximal emission rate in particles per second */
int m_max_rate;
int m_lifetime_min;
int m_lifetime_max;
int m_fadeout_time;
video::SColor m_min_start_color;
video::SColor m_max_start_color;
public:
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; }
int getMinRate () const { return m_min_rate; }
int getMaxRate () const { return m_max_rate; }
float getSpreadFactor() const { return m_spread_factor; }
EmitterShape getShape () const { return m_shape; }
Material* getMaterial () const { return m_material; }
int getMaxLifetime () const { return m_lifetime_max; }
int getMinLifetime () const { return m_lifetime_min; }
int getFadeoutTime () const { return m_fadeout_time; }
video::SColor getMinColor() const { return m_min_start_color; }
video::SColor getMaxColor() const { return m_max_start_color; }
};
#endif

View File

@ -57,6 +57,7 @@
953C304E12BEF384005BB4CD /* tutorial_data.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 953C304C12BEF384005BB4CD /* tutorial_data.cpp */; };
953F8B2111F7C13C00205E66 /* scalable_font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 953F8B1F11F7C13C00205E66 /* scalable_font.cpp */; };
9542FC7712D3BDB000C00366 /* particle_emitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9542FC7512D3BDB000C00366 /* particle_emitter.cpp */; };
9542FD4C12D3E0D700C00366 /* particle_kind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9542FD4A12D3E0D700C00366 /* particle_kind.cpp */; };
95453ACA11808B8700A155B9 /* emergency_animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95453AC811808B8700A155B9 /* emergency_animation.cpp */; };
9545ABCA11E3E38300D3C37A /* progress_bar_widget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9545ABC811E3E38300D3C37A /* progress_bar_widget.cpp */; };
954E486A11B19C4100B1DF63 /* fribidi.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 954E486911B19C4100B1DF63 /* fribidi.framework */; };
@ -439,6 +440,8 @@
9540E2570FD5F8FD002985B8 /* no_copy.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = no_copy.hpp; path = ../../utils/no_copy.hpp; sourceTree = SOURCE_ROOT; };
9542FC7512D3BDB000C00366 /* particle_emitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = particle_emitter.cpp; path = ../../graphics/particle_emitter.cpp; sourceTree = SOURCE_ROOT; };
9542FC7612D3BDB000C00366 /* particle_emitter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = particle_emitter.hpp; path = ../../graphics/particle_emitter.hpp; sourceTree = SOURCE_ROOT; };
9542FD4A12D3E0D700C00366 /* particle_kind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = particle_kind.cpp; path = ../../graphics/particle_kind.cpp; sourceTree = SOURCE_ROOT; };
9542FD4B12D3E0D700C00366 /* particle_kind.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = particle_kind.hpp; path = ../../graphics/particle_kind.hpp; sourceTree = SOURCE_ROOT; };
95453AC811808B8700A155B9 /* emergency_animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = emergency_animation.cpp; path = ../../karts/emergency_animation.cpp; sourceTree = SOURCE_ROOT; };
95453AC911808B8700A155B9 /* emergency_animation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = emergency_animation.hpp; path = ../../karts/emergency_animation.hpp; sourceTree = SOURCE_ROOT; };
9545ABC811E3E38300D3C37A /* progress_bar_widget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = progress_bar_widget.cpp; path = ../../guiengine/widgets/progress_bar_widget.cpp; sourceTree = SOURCE_ROOT; };
@ -1251,6 +1254,8 @@
952A153A103F66D600B1895D /* moving_texture.hpp */,
9542FC7512D3BDB000C00366 /* particle_emitter.cpp */,
9542FC7612D3BDB000C00366 /* particle_emitter.hpp */,
9542FD4A12D3E0D700C00366 /* particle_kind.cpp */,
9542FD4B12D3E0D700C00366 /* particle_kind.hpp */,
952A153D103F66D600B1895D /* shadow.cpp */,
952A153E103F66D600B1895D /* shadow.hpp */,
952A153F103F66D600B1895D /* skid_marks.cpp */,
@ -2726,6 +2731,7 @@
951B50AE12C9698B004F6993 /* xml_writer.cpp in Sources */,
9538A56012CD094200CE3220 /* addon.cpp in Sources */,
9542FC7712D3BDB000C00366 /* particle_emitter.cpp in Sources */,
9542FD4C12D3E0D700C00366 /* particle_kind.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -255,6 +255,8 @@ int XMLNode::get(const std::string &attribute, int *value) const
{
std::string s;
if(!get(attribute, &s)) return 0;
// FIXME: don't use "atoi", if the number in the attribute is not an int we want an error message,
// not silently return 0... easy to get bitten by this
*value = atoi(s.c_str());
return 1;
} // get(int)

View File

@ -31,6 +31,7 @@
#include "graphics/camera.hpp"
#include "graphics/material_manager.hpp"
#include "graphics/particle_emitter.hpp"
#include "graphics/particle_kind.hpp"
#include "graphics/shadow.hpp"
#include "graphics/skid_marks.hpp"
#include "graphics/slip_stream.hpp"
@ -55,8 +56,6 @@
# pragma warning(disable:4355)
#endif
const float SMOKE_PARTICLE_SIZE = 0.33f;
/** The kart constructor.
* \param ident The identifier for the kart model to use.
* \param position The position (or rank) for this kart (between 1 and
@ -86,6 +85,7 @@ Kart::Kart (const std::string& ident, int position,
m_race_position = position;
m_collected_energy = 0;
m_finished_race = false;
m_wheel_toggle = 1;
m_finish_time = 0.0f;
m_shadow_enabled = false;
m_shadow = NULL;
@ -1442,25 +1442,29 @@ void Kart::loadData()
// Attach Particle System
if (UserConfigParams::m_graphical_effects)
{
core::vector3df position(-getKartWidth()*0.35f, SMOKE_PARTICLE_SIZE*0.25f, -getKartLength()*0.5f);
m_smoke_system = new ParticleEmitter(SMOKE_PARTICLE_SIZE, position, material_manager->getMaterial("smoke.png"),
5 /* min particles */, 10 /* max particles */,
video::SColor(255,0,0,0) /* min color */,
video::SColor(255,255,255,255) /* max color */,
300 /* lifeTimeMin */, 500 /* lifeTimeMax */, 20 /* max angle */,
500 /* fade-out time */, 0.003f, SMOKE_PARTICLE_SIZE/1.5f, SMOKE_PARTICLE_SIZE*1.5f);
try
{
core::vector3df position(-getKartWidth()*0.35f, 0.06, -getKartLength()*0.5f);
m_smoke_system = new ParticleEmitter(new ParticleKind(file_manager->getDataFile("smoke.xml")), position);
}
catch (std::runtime_error& e)
{
std::cerr << e.what() << std::endl;
}
}
m_water_splash_system = new WaterSplash(this);
const float particle_size = 0.25f;
core::vector3df position(0, particle_size*0.25f, -getKartLength()*0.5f);
m_nitro = new ParticleEmitter(particle_size, position, material_manager->getMaterial("nitro-particle.png"),
5 /* min particles */, 10 /* max particles */,
video::SColor(255,0,0,0) /* min color */,
video::SColor(255,255,255,255) /* max color */,
150 /* lifeTimeMin */, 250 /* lifeTimeMax */, 40 /* max angle */,
2500 /* fade-out time */, 0.003f, particle_size/1.5f, particle_size*2.0f,
getNode());
try
{
m_nitro = new ParticleEmitter(new ParticleKind(file_manager->getDataFile("nitro.xml")), position, getNode());
}
catch (std::runtime_error& e)
{
std::cerr << e.what() << std::endl;
}
m_slipstream = new SlipStream(this);
@ -1551,16 +1555,15 @@ void Kart::updateGraphics(const Vec3& offset_xyz,
f=250.0f;
m_smoke_system->setCreationRate((m_skidding-1)*f);
static int left = 1;
left = 1 - left;
const btWheelInfo &wi = getVehicle()->getWheelInfo(2 + left);
m_wheel_toggle = 1 - m_wheel_toggle;
const btWheelInfo &wi = getVehicle()->getWheelInfo(2 + m_wheel_toggle);
Vec3 c = wi.m_raycastInfo.m_contactPointWS;
// FIXME: instead of constantly moving the emitter around, just make it a child of the kart node
// FIXME: the X position is not yet always accurate.
m_smoke_system->setPosition(core::vector3df(c.getX() + SMOKE_PARTICLE_SIZE*0.25f * (left ? +1 : -1),
m_smoke_system->setPosition(core::vector3df(c.getX() + 0.06f * (m_wheel_toggle ? +1 : -1),
c.getY(),
c.getZ() + SMOKE_PARTICLE_SIZE*0.25f));
c.getZ() + 0.06f));
}
if(m_water_splash_system)

View File

@ -106,6 +106,9 @@ private:
* determine startup boost. */
bool m_has_started;
/** For skidding smoke */
int m_wheel_toggle;
float m_max_gear_rpm; /**<Maximum engine rpm's for the current gear*/
float m_bounce_back_time; /**<A short time after a collision acceleration
* is disabled to allow the karts to bounce back*/