diff --git a/data/nitro.xml b/data/nitro.xml new file mode 100644 index 000000000..6a83923c2 --- /dev/null +++ b/data/nitro.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/smoke.xml b/data/smoke.xml new file mode 100644 index 000000000..8158f9c57 --- /dev/null +++ b/data/smoke.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index 27fbc8683..5f7f9368d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/graphics/particle_emitter.cpp b/src/graphics/particle_emitter.cpp index 3f73ee280..d6d37e441 100644 --- a/src/graphics/particle_emitter.cpp +++ b/src/graphics/particle_emitter.cpp @@ -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 diff --git a/src/graphics/particle_emitter.hpp b/src/graphics/particle_emitter.hpp index b481db90d..dc77324e0 100644 --- a/src/graphics/particle_emitter.hpp +++ b/src/graphics/particle_emitter.hpp @@ -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 (); diff --git a/src/graphics/particle_kind.cpp b/src/graphics/particle_kind.cpp new file mode 100644 index 000000000..8c4d751b8 --- /dev/null +++ b/src/graphics/particle_kind.cpp @@ -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 + +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 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] 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] 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; +} diff --git a/src/graphics/particle_kind.hpp b/src/graphics/particle_kind.hpp new file mode 100644 index 000000000..ca287a478 --- /dev/null +++ b/src/graphics/particle_kind.hpp @@ -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 +#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 + + diff --git a/src/ide/Xcode/STK_XCode.xcodeproj/project.pbxproj b/src/ide/Xcode/STK_XCode.xcodeproj/project.pbxproj index b0c39c1c4..cf52d0a68 100644 --- a/src/ide/Xcode/STK_XCode.xcodeproj/project.pbxproj +++ b/src/ide/Xcode/STK_XCode.xcodeproj/project.pbxproj @@ -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; }; diff --git a/src/io/xml_node.cpp b/src/io/xml_node.cpp index 000920fc0..04356b38c 100644 --- a/src/io/xml_node.cpp +++ b/src/io/xml_node.cpp @@ -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) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index f9d45d339..d4a5d9385 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -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) diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index a14266a54..48aa663ac 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -106,6 +106,9 @@ private: * determine startup boost. */ bool m_has_started; + /** For skidding smoke */ + int m_wheel_toggle; + float m_max_gear_rpm; /**