From 2e93e755a4f4631419ba0eee26660a5638a7a7c6 Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Sun, 23 Nov 2008 22:13:40 +0000 Subject: particle systems --- src/render/draw.cc | 6 +- src/render/particles.cc | 374 +++++++++++++++++++++++++++++++++++++++--------- src/render/particles.h | 144 ++++++++++++++++--- src/render/render.cc | 7 + src/render/renderext.cc | 45 +++--- src/render/renderext.h | 6 +- 6 files changed, 467 insertions(+), 115 deletions(-) diff --git a/src/render/draw.cc b/src/render/draw.cc index a700d14..b42a849 100644 --- a/src/render/draw.cc +++ b/src/render/draw.cc @@ -962,9 +962,9 @@ void draw_pass_model_fx(float elapsed) gl::end(); gl::disable(GL_CULL_FACE); - for (RenderExt::Particles::iterator it = ext_render(entity)->particles().begin(); it != ext_render(entity)->particles().end(); it++) { - ParticleStream *particles = (*it); - particles->draw(); + for (RenderExt::ParticleSystems::iterator it = ext_render(entity)->particles().begin(); it != ext_render(entity)->particles().end(); it++) { + ParticleSystem *particlesystem = (*it); + particlesystem->draw(elapsed); } gl::enable(GL_CULL_FACE); diff --git a/src/render/particles.cc b/src/render/particles.cc index 94a3a07..638b8b3 100644 --- a/src/render/particles.cc +++ b/src/render/particles.cc @@ -4,13 +4,14 @@ the terms of the GNU General Public License version 2 */ +#include "auxiliary/functions.h" #include "render/particles.h" #include "render/camera.h" #include "render/gl.h" #include "render/draw.h" #include "render/textures.h" - #include "core/application.h" + namespace render { /* ---- class Particle --------------------------------------------- */ @@ -20,111 +21,261 @@ Particle::Particle(const math::Vector3f &location, float time) : particle_locati particle_time = time; } -Particle::Particle(const math::Vector3f &location, const math::Vector3f &up, const math::Vector3f &left, float time) : particle_location(location), particle_up(up), particle_left(left) +Particle::Particle(const math::Vector3f &location, const math::Axis &axis, float time) : particle_location(location), particle_axis(axis) { particle_time = time; } -/* ---- class ParticleStream --------------------------------------- */ -ParticleStream::ParticleStream(const math::Vector3f &location, const math::Color &color, float radius, size_t texture, core::EntityControlable *entity, bool oriented) : particles_location(location), particles_color(color) +/* ---- static ParticleScript registry ---------------------------------- */ + +ParticleScript::Registry ParticleScript::particles_registry; + +void ParticleScript::clear() { - particles_texture = texture; - particles_entity = entity; - if (!entity) { - particles_oriented = false; - } else { - particles_oriented = oriented; + for (Registry::iterator it = particles_registry.begin(); it != particles_registry.end(); it++) { + delete (*it).second; + (*it).second = 0; } + particles_registry.clear(); +} - particles_radius = radius; +ParticleScript *ParticleScript::find(const std::string &label) +{ + Registry::iterator it = particles_registry.find(label); - particles_timeout = 1.5f; // a particles lives for 3 seconds - particles_eject = 0.02f; // a particle is ejected every 0.2 seconds + if (it != particles_registry.end()) { + return (*it).second; + } else { + return 0; + } +} + +void ParticleScript::init() +{ + clear(); - particles_last_eject = 0; // timestamp of the last eject + ParticleScript *script = 0; + + filesystem::IniFile inifile; + inifile.open("particles"); + + if (!inifile.is_open()) { + con_error << "Could not open " << inifile.name() << std::endl; + return; + } + + con_print << "^BLoading particle systems..." << std::endl; + + std::string strval; + + while (inifile.getline()) { + + if (inifile.got_section()) { + if (inifile.got_section("particles")) { + if (script) { + if (script->label().size()) { + if (find(script->label())) { + con_warn << "Duplicate particle system '" << script->label() << "'" << std::endl; + delete script; + } else { + particles_registry[script->label()] = script; + } + } else { + con_warn << "Particle system without label" << std::endl; + delete script; + } + } + script = new ParticleScript(); + } + + } else if (inifile.got_key()) { + + if (inifile.in_section("particles")) { + + if (inifile.got_key_string("label", script->particles_label)) { + continue; + } else if (inifile.got_key_string("type", strval)) { + aux::to_label(strval); + if (strval.compare("flame") == 0) { + script->particles_type = ParticleScript::Flame; + } else if (strval.compare("jet") == 0) { + script->particles_type = ParticleScript::Jet; + } else if (strval.compare("trail") == 0) { + script->particles_type = ParticleScript::Trail; + } else { + inifile.unknown_value(); + } + continue; + } else if (inifile.got_key_string("texture", script->particles_texture)) { + Textures::load("textures/" + script->texture()); + continue; + } else if (inifile.got_key_float("speed", script->particles_speed)) { + continue; + } else if (inifile.got_key_float("alpha", script->particles_alpha)) { + continue; + } else if (inifile.got_key_float("radius", script->particles_radius)) { + script->particles_radius *= model::SCALE; + continue; + } else if (inifile.got_key_float("eject", script->particles_eject)) { + continue; + } else if (inifile.got_key_float("timeout", script->particles_timeout)) { + continue; + } else if (inifile.got_key_color("color", script->particles_color)) { + continue; + } else { + inifile.unkown_key(); + } + } + } + } + inifile.close(); + + if (script) { + if (script->label().size()) { + if (find(script->label())) { + con_warn << "Duplicate particle system '" << script->label() << "'" << std::endl; + delete script; + } else { + particles_registry[script->label()] = script; + } + } else { + con_warn << "Particle system without label" << std::endl; + delete script; + } + } + + con_debug << " " << inifile.name() << " " << particles_registry.size() << " " << aux::plural("particle system", particles_registry.size()) << std::endl; +} + +/* ---- class ParticleScript --------------------------------------- */ + +ParticleScript::ParticleScript() +{ + particles_radius = 1.0f; + particles_alpha = 1.0f; + + particles_speed = 0.0f; + particles_timeout = 2.0f; + particles_eject = 0.25f; + + particles_color.assign(1.0f, 1.0f); +} + +ParticleScript::~ParticleScript() +{ +} + +/* ---- class ParticleSystem --------------------------------------- */ + +ParticleSystem::ParticleSystem(ParticleScript *script, core::Entity *entity, const math::Vector3f &location) : + particlesystem_location(location) +{ + particlesystem_entity = entity; + particlesystem_last_eject = 0; // timestamp of the last eject + + particlesystem_script = script; + particlesystem_texture = 0; + + if (particlesystem_script) { + particlesystem_texture = Textures::load("textures/" + particlesystem_script->texture()); + color.assign(particlesystem_script->color()); + } } -ParticleStream::~ParticleStream() +ParticleSystem::~ParticleSystem() { - for (Particles::iterator it = particles_stream.begin(); it != particles_stream.end(); it++) { + for (Stream::iterator it = particlesystem_stream.begin(); it != particlesystem_stream.end(); it++) { delete (*it); } - particles_stream.clear(); + particlesystem_stream.clear(); } -void ParticleStream::draw() { - float now = core::application()->time(); +void ParticleSystem::draw(float elapsed) +{ + now = core::application()->time(); + ejector_location.assign(particlesystem_entity->location() + (particlesystem_entity->axis() * location())); // remove dead particles - Particles::iterator it = particles_stream.begin(); - while ((it != particles_stream.end()) && ((*it)->time() + particles_timeout <= now)) { + Stream::iterator it = particlesystem_stream.begin(); + while ((it != particlesystem_stream.end()) && ((*it)->time() + particlesystem_script->timeout() <= now)) { delete (*it); - particles_stream.erase(it); - it = particles_stream.begin(); + particlesystem_stream.erase(it); + it = particlesystem_stream.begin(); } - // add new particles - if (particles_last_eject + particles_eject <= now) { - if (particles_entity) { - float u = 0; - if ((particles_entity->eventstate() == core::Entity::ImpulseInitiate ) || (particles_entity->eventstate() == core::Entity::Impulse)) { - u = 1.0f; - } else if(particles_entity->eventstate() == core::Entity::Normal) { - u = particles_entity->thrust(); + // apply speed + bool ejector_active = false; + if (particlesystem_script->speed()) { + for (Stream::reverse_iterator rit = particlesystem_stream.rbegin(); rit != particlesystem_stream.rend(); rit++) { + Particle *particle = (*rit); + particle->location() += particle->axis().forward() * particlesystem_script->speed() * elapsed; + } + } else { + if (particlesystem_entity->type() == core::Entity::Dynamic) { + core::EntityDynamic *ed = static_cast(particlesystem_entity); + if (ed->speed()) { + ejector_active = true; } - if (u > 0.0f) { - math::Vector3f l(particles_entity->location() + (particles_entity->axis() * location())); - Particle *p = 0; - if (particles_oriented) { - p = new Particle(l, particles_entity->axis().up(), particles_entity->axis().left(), now); - } else { - p = new Particle(l, now); - } - particles_stream.push_back(p); + } else if (particlesystem_entity->type() == core::Entity::Controlable) { + core::EntityControlable *ec = static_cast(particlesystem_entity); + if ((ec->thrust() > 0.0f) || (ec->eventstate() == core::Entity::ImpulseInitiate) || (ec->eventstate() == core::Entity::Impulse)) { + ejector_active = true; } - } else { - particles_stream.push_back(new Particle(location(), now)); } - particles_last_eject = now; } + // add new particles + if (ejector_active && (particlesystem_last_eject + particlesystem_script->eject() <= now)) { + particlesystem_stream.push_back(new Particle(ejector_location, particlesystem_entity->axis() * particlesystem_axis, now)); + particlesystem_last_eject = now; + } +} + +/* ---- class Jet -------------------------------------------------- */ + +Jet::Jet(ParticleScript *script, core::Entity *entity, const math::Vector3f &location) : + ParticleSystem(script, entity, location) { +} + +Jet::~Jet() +{} + +void Jet::draw(float elapsed) { + if (!particlesystem_script) + return; + + ParticleSystem::draw(elapsed); + math::Vector3f quad[4]; - if (particles_stream.size()) { - Textures::bind(particles_texture); + if (particlesystem_stream.size()) { + Textures::bind(particlesystem_texture); gl::begin(gl::Quads); - for (Particles::reverse_iterator rit = particles_stream.rbegin(); rit != particles_stream.rend(); rit++) { + for (Stream::reverse_iterator rit = particlesystem_stream.rbegin(); rit != particlesystem_stream.rend(); rit++) { Particle *particle = (*rit); - - if (particles_oriented) { - quad[0].assign(particle->up() - particle->left()); - quad[1].assign(particle->up() + particle->left()); - quad[2].assign(particle->up() * -1 + particle->left()); - quad[3].assign(particle->up() * -1 - particle->left()); - } else { - quad[0].assign(Camera::axis().up() - Camera::axis().left()); - quad[1].assign(Camera::axis().up() + Camera::axis().left()); - quad[2].assign(Camera::axis().up() * -1 + Camera::axis().left()); - quad[3].assign(Camera::axis().up() * -1 - Camera::axis().left()); - } - + + quad[0].assign(particle->axis().up() - particle->axis().left()); + quad[1].assign(particle->axis().up() + particle->axis().left()); + quad[2].assign(particle->axis().up() * -1 + particle->axis().left()); + quad[3].assign(particle->axis().up() * -1 - particle->axis().left()); + float t = now - particle->time(); float f = 0; - if (t < particles_timeout * 0.1f) { - f = t / (0.1f * particles_timeout); + if (t < particlesystem_script->timeout() * 0.1f) { + f = t / (0.1f * particlesystem_script->timeout()); } else { - t = t - particles_timeout * 0.1f; - f = t / (0.9f * particles_timeout); + t = t - particlesystem_script->timeout() * 0.1f; + f = t / (0.9f * particlesystem_script->timeout()); f = 1.0 - f; } f *= f; - float radius = particles_radius * f; - particles_color.a = f; - gl::color(particles_color); + float radius = particlesystem_script->radius() * f; + color.a = f * particlesystem_script->alpha(); + gl::color(color); glTexCoord2f(0,1); gl::vertex(particle->location() + quad[0] * radius); @@ -141,4 +292,95 @@ void ParticleStream::draw() { } } +/* ---- class Trail ------------------------------------------------ */ + +Trail::Trail(ParticleScript *script, core::Entity *entity, const math::Vector3f &location) : + ParticleSystem(script, entity, location) { +} + +Trail::~Trail() +{} + +void Trail::draw(float elapsed) { + + if (!particlesystem_script) + return; + + ParticleSystem::draw(elapsed); + + if (particlesystem_stream.size() > 1) { + Textures::bind(particlesystem_texture); + + gl::begin(gl::Quads); + + Stream::reverse_iterator previous = particlesystem_stream.rbegin(); + + float tp = now - (*previous)->time(); + float fp = 0; + + if (tp < particlesystem_script->timeout() * 0.1f) { + fp = tp / (0.1f * particlesystem_script->timeout()); + } else { + tp = tp - particlesystem_script->timeout() * 0.1f; + fp = tp / (0.9f * particlesystem_script->timeout()); + fp = 1.0 - fp; + } + + Stream::reverse_iterator next = previous; + for (next++; next != particlesystem_stream.rend(); next++) { + + float t = now - (*next)->time(); + float f = 0; + + if (t < particlesystem_script->timeout() * 0.1f) { + f = t / (0.1f * particlesystem_script->timeout()); + } else { + t = t - particlesystem_script->timeout() * 0.1f; + f = t / (0.9f * particlesystem_script->timeout()); + f = 1.0 - f; + } + + color.a = f * particlesystem_script->alpha(); + gl::color(color); + + glTexCoord2f(1,0); + gl::vertex((*next)->location() - (*next)->axis().left() * particlesystem_script->radius() * f); + glTexCoord2f(0,0); + gl::vertex((*next)->location() + (*next)->axis().left() * particlesystem_script->radius() * f); + + color.a = fp * particlesystem_script->alpha(); + gl::color(color); + + glTexCoord2f(0,1); + gl::vertex((*previous)->location() + (*previous)->axis().left() * particlesystem_script->radius() * fp); + glTexCoord2f(1,1); + gl::vertex((*previous)->location() - (*previous)->axis().left() * particlesystem_script->radius() * fp); + Stats::quads++; + + previous = next; + fp = f; + tp = t; + } + + color.a = 0.0f; + gl::color(color); + + glTexCoord2f(1,0); + gl::vertex(ejector_location); + glTexCoord2f(0,0); + gl::vertex(ejector_location); + + color.a = fp; + gl::color(color); + + glTexCoord2f(0,1); + gl::vertex((*previous)->location() + (*previous)->axis().left() * particlesystem_script->radius() * fp); + glTexCoord2f(1,1); + gl::vertex((*previous)->location() - (*previous)->axis().left() * particlesystem_script->radius() * fp); + Stats::quads++; + + gl::end(); + } +} + } // namespace render diff --git a/src/render/particles.h b/src/render/particles.h index 400971c..e74c3d8 100644 --- a/src/render/particles.h +++ b/src/render/particles.h @@ -9,64 +9,164 @@ #include +#include "math/axis.h" #include "math/color.h" #include "math/vector3f.h" #include "core/entity.h" namespace render { +/* ---- class Particle --------------------------------------------- */ + +/// one particle class Particle { public: Particle(const math::Vector3f &location, float time); - Particle(const math::Vector3f &location, const math::Vector3f &up, const math::Vector3f &left, float time); - - inline const math::Vector3f &location() const { return particle_location; } + Particle(const math::Vector3f &location, const math::Axis &axis, float time); - inline const math::Vector3f &up() const { return particle_up; } + /// location of the particle, in world coordinates + inline math::Vector3f &location() { return particle_location; } - inline const math::Vector3f &left() const { return particle_left; } + inline math::Axis &axis() { return particle_axis; } inline const float time() const { return particle_time; } protected: math::Vector3f particle_location; - math::Vector3f particle_up; - math::Vector3f particle_left; + math::Axis particle_axis; float particle_time; }; +/* ---- class ParticleScript --------------------------------------- */ -class ParticleStream { +/// class to hold particle scripts +class ParticleScript { public: - ParticleStream(const math::Vector3f &location, const math::Color &color, float radius, size_t texture, core::EntityControlable *entity = 0, bool oriented = false); - ~ParticleStream(); - void draw(); + enum Type {Jet=0, Trail=1, Flame=2 }; - inline const size_t texture() const { return particles_texture; } + inline const Type type() const { return particles_type; } - inline const math::Vector3f &location() const { return particles_location; } + inline const std::string &label() const { return particles_label; } -protected: - typedef std::deque Particles; + inline const std::string &texture() const { return particles_texture; } + + inline const math::Color &color() const { return particles_color; } + + inline float radius() const { return particles_radius; } + + inline float timeout() const { return particles_timeout; } + + inline float eject() const { return particles_eject; } + + inline float speed() const { return particles_speed; } + + inline float alpha() const { return particles_alpha; } + + static ParticleScript *find(const std::string &label); + + static void init(); - math::Vector3f particles_location; - math::Color particles_color; - size_t particles_texture; - bool particles_oriented; + static void clear(); + +private: + ParticleScript(); + ~ParticleScript(); + + std::string particles_label; + std::string particles_texture; + Type particles_type; + math::Color particles_color; float particles_radius; float particles_timeout; float particles_eject; + float particles_speed; + float particles_alpha; + + typedef std::map Registry; + + static Registry particles_registry; +}; + +/* ---- class ParticleSystem --------------------------------------- */ + +/// abstract base class for a particle system attached to an entity +class ParticleSystem { +public: + ParticleSystem(ParticleScript *script, core::Entity *entity, const math::Vector3f &location); + virtual ~ParticleSystem(); + + /// index of the texture to use + inline const size_t texture() const { return particlesystem_texture; } + + /// location of the particle system within the entity + inline const math::Vector3f &location() const { return particlesystem_location; } + + /// axis of the particle system within the entity + inline const math::Axis &axis() const { return particlesystem_axis; } - float particles_last_eject; + virtual void draw(float elapsed); - core::EntityControlable *particles_entity; + void set_timeout(float timeout); - Particles particles_stream; + void set_eject(float eject); + +protected: + core::Entity *particlesystem_entity; + + typedef std::deque Stream; + + inline Stream & stream() { return particlesystem_stream; } + + size_t particlesystem_texture; + + math::Axis particlesystem_axis; + math::Vector3f particlesystem_location; + + float particlesystem_last_eject; + + ParticleScript *particlesystem_script; + + Stream particlesystem_stream; + + math::Vector3f ejector_location; + bool ejector_active; + float now; + math::Color color; +}; + +/* ---- class Flame ------------------------------------------------ */ + +/// flame style particles, like engine flames +class Flame : public ParticleSystem +{ }; +/* ---- class Jet -------------------------------------------------- */ + +/// jet style particles, like smoke +class Jet : public ParticleSystem +{ +public: + Jet(ParticleScript *script, core::Entity *entity, const math::Vector3f &location); + virtual ~Jet(); + + virtual void draw(float elapsed); +}; + +/* ---- class Trail ------------------------------------------------ */ + +/// trail style particles, like an engine trail +class Trail : public ParticleSystem +{ +public: + Trail(ParticleScript *script, core::Entity *entity, const math::Vector3f &location); + virtual ~Trail(); + + virtual void draw(float elapsed); +}; } // namespace render diff --git a/src/render/render.cc b/src/render/render.cc index ab3b751..1abd197 100644 --- a/src/render/render.cc +++ b/src/render/render.cc @@ -56,6 +56,8 @@ void init(int width, int height) Dust::init(); + ParticleScript::init(); + // size of the vertex array in megabytes r_arraysize = core::Cvar::get("r_arraysize", 0.0f , core::Cvar::Archive); r_arraysize->set_info("[int] size of the vertex array in Mb"); @@ -156,6 +158,9 @@ void clear() // clear model refistry model::Model::clear(); + // clear particle system scripts + ParticleScript::clear(); + // clear vertex array delete vertexarray; vertexarray = 0; @@ -172,6 +177,8 @@ void reset() Textures::init(); + ParticleScript::init(); + size_t mb = (size_t) r_arraysize->value(); if (mb < 4 * sizeof(float)) mb = 4 * sizeof(float); diff --git a/src/render/renderext.cc b/src/render/renderext.cc index 5b6f3b4..01e8fcf 100644 --- a/src/render/renderext.cc +++ b/src/render/renderext.cc @@ -13,6 +13,7 @@ #include "render/renderext.h" #include "render/render.h" #include "render/textures.h" +#include "sys/sys.h" namespace render { @@ -25,8 +26,6 @@ RenderExt::RenderExt(core::Entity *entity) : core::Extension(core::Extension::Re //state_engine_trail_offset = 0; - using namespace model; - if (!entity->model() && entity->modelname().size()) { entity->set_modelname(entity->modelname()); if (!entity->model()) @@ -36,8 +35,8 @@ RenderExt::RenderExt(core::Entity *entity) : core::Extension(core::Extension::Re if (entity->model()) { model::Model *model = entity->model(); - for (Model::Lights::iterator lit = model->lights().begin(); lit != model->lights().end(); lit++) { - Light *light = (*lit); + for (model::Model::Lights::iterator lit = model->lights().begin(); lit != model->lights().end(); lit++) { + model::Light *light = (*lit); // load light texture std::stringstream flarename; @@ -45,22 +44,8 @@ RenderExt::RenderExt(core::Entity *entity) : core::Extension(core::Extension::Re light->render_texture = Textures::load(flarename.str()); } - if ((entity->type() == core::Entity::Controlable) && entity->model()->engines().size()) { - - size_t trail_texure = Textures::load("bitmaps/fx/circle00"); - - for(Model::Engines::iterator eit = model->engines().begin(); eit != model->engines().end(); eit++) { - Engine *engine = (*eit); - - // add trail particles - math::Color c; - ParticleStream *p = new ParticleStream(engine->location(), c, 0.0625 * engine->radius(), trail_texure, static_cast(entity), true); - state_particles.push_back(p); - } - } - - for (Model::Flares::iterator flit = model->flares().begin(); flit != model->flares().end(); flit++) { - Flare *flare = (*flit); + for (model::Model::Flares::iterator flit = model->flares().begin(); flit != model->flares().end(); flit++) { + model::Flare *flare = (*flit); // load flare texture std::stringstream flarename; @@ -68,6 +53,24 @@ RenderExt::RenderExt(core::Entity *entity) : core::Extension(core::Extension::Re flare->render_texture = Textures::load(flarename.str()); } + for(model::Model::ParticleSystems::iterator pit = model->particles().begin(); pit != model->particles().end(); pit++) { + model::Particles *particlesystem = (*pit); + + // load particle systems + ParticleScript *script = ParticleScript::find(particlesystem->script()); + if (script) { + if (script->type() == render::ParticleScript::Trail) { + Trail *trail = new Trail(script, entity, particlesystem->location()); + state_particles.push_back(trail); + } else if (script->type() == render::ParticleScript::Jet) { + Jet *jet = new Jet(script, entity, particlesystem->location()); + state_particles.push_back(jet); + } + } else { + con_warn << "Could not find particle system '" << particlesystem->script() << "'" << std::endl; + } + } + } else if (entity->type() == core::Entity::Globe) { core::EntityGlobe *globe = static_cast(entity); @@ -84,7 +87,7 @@ RenderExt::RenderExt(core::Entity *entity) : core::Extension(core::Extension::Re RenderExt::~RenderExt() { - for (Particles::iterator it = state_particles.begin(); it != state_particles.end(); it++) { + for (ParticleSystems::iterator it = state_particles.begin(); it != state_particles.end(); it++) { delete (*it); } state_particles.clear(); diff --git a/src/render/renderext.h b/src/render/renderext.h index eb2ae67..28a2c23 100644 --- a/src/render/renderext.h +++ b/src/render/renderext.h @@ -21,7 +21,7 @@ public: RenderExt(core::Entity *entity); ~RenderExt(); - typedef std::list Particles; + typedef std::list ParticleSystems; virtual void frame(float elapsed); @@ -34,7 +34,7 @@ public: inline float distance() const { return state_distance; } /// particles - inline Particles &particles() { return state_particles; } + inline ParticleSystems &particles() { return state_particles; } private: bool state_visible; bool state_detailvisible; @@ -42,7 +42,7 @@ private: float state_fuzz; float state_distance; - Particles state_particles; + ParticleSystems state_particles; }; } // namespace render -- cgit v1.2.3