/* render/particles.cc This file is part of the Osirion project and is distributed under 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 --------------------------------------------- */ Particle::Particle(const math::Vector3f &location, float time) : particle_location(location) { particle_time = time; } Particle::Particle(const math::Vector3f &location, const math::Axis &axis, float time) : particle_location(location), particle_axis(axis) { particle_time = time; } /* ---- static ParticleScript registry ---------------------------------- */ ParticleScript::Registry ParticleScript::particles_registry; void ParticleScript::list() { std::string str; for (Registry::iterator it = particles_registry.begin(); it != particles_registry.end(); it++) { ParticleScript *script = (*it).second; switch (script->type()) { case Flame: str.assign("flame"); break; case Jet: str.assign("jet"); break; case Spray: str.assign("spray"); break; case Trail: str.assign("trail"); break; } con_print << " " << script->label() << " " << str << std::endl; con_print << particles_registry.size() << " particle scripts" << std::endl; } } void ParticleScript::clear() { for (Registry::iterator it = particles_registry.begin(); it != particles_registry.end(); it++) { delete(*it).second; (*it).second = 0; } particles_registry.clear(); } ParticleScript *ParticleScript::find(const std::string &label) { Registry::iterator it = particles_registry.find(label); if (it != particles_registry.end()) { return (*it).second; } else { return 0; } } ParticleScript *ParticleScript::load(const std::string &label) { ParticleScript *script = find(label); if (script) return script; filesystem::IniFile inifile; inifile.open("particles/" + label); if (!inifile.is_open()) { con_warn << "Could not open " << inifile.name() << std::endl; return 0; } script = new ParticleScript(label); con_debug << " " << inifile.name() << std::endl; std::string strval; while (inifile.getline()) { if (inifile.got_section()) { if (inifile.got_section("particles")) { continue; } else { inifile.unknown_section(); } } else if (inifile.got_key()) { if (inifile.in_section("particles")) { 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("offset", script->particles_offset)) { math::clamp(script->particles_offset, 0.0f, 1.0f); 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(); particles_registry[script->label()] = script; return script; } /* ---- class ParticleScript --------------------------------------- */ ParticleScript::ParticleScript(const std::string label) : particles_label(label) { particles_radius = 1.0f; particles_alpha = 1.0f; particles_speed = 0.0f; particles_timeout = 2.0f; particles_eject = 0.25f; particles_offset = 0.1f; particles_color.assign(1.0f, 1.0f); } ParticleScript::~ParticleScript() { } /* ---- class ParticleSystem --------------------------------------- */ ParticleSystem::ParticleSystem(ParticleScript *script, core::Entity *entity, model::Particles *modelclass) { particlesystem_entity = entity; particlesystem_last_eject = 0; // timestamp of the last eject particlesystem_script = script; particlesystem_texture = 0; particlesystem_cull = model::CullNone; particlesystem_modelclass = modelclass; if (particlesystem_script) { particlesystem_texture = Textures::load("textures/" + particlesystem_script->texture()); // map entity radius overrules script value if (modelclass->radius()) particlesystem_radius = modelclass->radius(); else particlesystem_radius = particlesystem_script->radius(); color.assign(particlesystem_script->color()); } if (particlesystem_modelclass) { particlesystem_location.assign(modelclass->location()); if (modelclass->entity()) { color.assign(entity->color()); } if (modelclass->engine()) { color.assign(entity->model()->enginecolor()); } particlesystem_axis.assign(modelclass->axis()); // particlesystem_cull = particlesystem_modelclass->cull(); } } ParticleSystem::~ParticleSystem() { clear(); } void ParticleSystem::clear() { for (Stream::iterator it = particlesystem_stream.begin(); it != particlesystem_stream.end(); it++) { delete(*it); } particlesystem_stream.clear(); } void ParticleSystem::draw(float elapsed) { if (particlesystem_entity->type() == core::Entity::Controlable) { core::EntityControlable *ec = static_cast(particlesystem_entity); if (ec->state() == core::Entity::Docked) { if (particlesystem_stream.size()) clear(); return; } } now = core::application()->time(); ejector_location.assign(particlesystem_entity->location() + (particlesystem_entity->axis() * location())); // remove dead particles Stream::reverse_iterator it = particlesystem_stream.rbegin(); while ((it != particlesystem_stream.rend()) && ((*it)->time() + particlesystem_script->timeout() <= now)) { delete(*particlesystem_stream.rbegin()); particlesystem_stream.pop_back(); it = particlesystem_stream.rbegin(); } // apply speed bool ejector_active = false; if (particlesystem_script->speed()) { for (Stream::iterator it = particlesystem_stream.begin(); it != particlesystem_stream.end(); it++) { Particle *particle = (*it); particle->location() += particle->axis().forward() * particlesystem_script->speed() * elapsed; } ejector_active = true; } else { if (particlesystem_entity->type() == core::Entity::Dynamic) { core::EntityDynamic *ed = static_cast(particlesystem_entity); if (ed->speed()) { ejector_active = true; } } else if (particlesystem_entity->type() == core::Entity::Controlable) { core::EntityControlable *ec = static_cast(particlesystem_entity); if ((ec->thrust() > 0.0f) || (ec->state() == core::Entity::ImpulseInitiate) || (ec->state() == core::Entity::Impulse)) { ejector_active = true; } } } // add new particles if (ejector_active && (particlesystem_last_eject + particlesystem_script->eject() <= now)) { particlesystem_stream.push_front(new Particle(ejector_location, particlesystem_entity->axis() * particlesystem_axis, now)); particlesystem_last_eject = now; } } /* ---- class Jet -------------------------------------------------- */ Jet::Jet(ParticleScript *script, core::Entity *entity, model::Particles *modelclass) : ParticleSystem(script, entity, modelclass) { } Jet::~Jet() {} void Jet::draw(float elapsed) { if (!particlesystem_script) return; ParticleSystem::draw(elapsed); math::Vector3f quad[4]; if (particlesystem_stream.size()) { Textures::bind(particlesystem_texture); gl::begin(gl::Quads); for (Stream::iterator it = particlesystem_stream.begin(); it != particlesystem_stream.end(); it++) { Particle *particle = (*it); 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 < 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; } f *= f; float radius = particlesystem_radius * f; color.a = f * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(0, 1); gl::vertex(particle->location() + quad[0] * radius); glTexCoord2f(0, 0); gl::vertex(particle->location() + quad[1] * radius); glTexCoord2f(1, 0); gl::vertex(particle->location() + quad[2] * radius); glTexCoord2f(1, 1); gl::vertex(particle->location() + quad[3] * radius); Stats::quads++; } gl::end(); } } /* ---- class Spray ------------------------------------------------ */ Spray::Spray(ParticleScript *script, core::Entity *entity, model::Particles *modelclass) : ParticleSystem(script, entity, modelclass) { } Spray::~Spray() {} void Spray::draw(float elapsed) { if (!particlesystem_script) return; ParticleSystem::draw(elapsed); math::Vector3f quad[4]; if (particlesystem_stream.size()) { Textures::bind(particlesystem_texture); gl::begin(gl::Quads); for (Stream::iterator it = particlesystem_stream.begin(); it != particlesystem_stream.end(); it++) { Particle *particle = (*it); 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()); float t = now - particle->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; } f *= f; float radius = particlesystem_radius * f; color.a = f * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(0, 1); gl::vertex(particle->location() + quad[0] * radius); glTexCoord2f(0, 0); gl::vertex(particle->location() + quad[1] * radius); glTexCoord2f(1, 0); gl::vertex(particle->location() + quad[2] * radius); glTexCoord2f(1, 1); gl::vertex(particle->location() + quad[3] * radius); Stats::quads++; } gl::end(); } } /* ---- class Trail ------------------------------------------------ */ Trail::Trail(ParticleScript *script, core::Entity *entity, model::Particles *modelclass) : ParticleSystem(script, entity, modelclass) { } Trail::~Trail() {} void Trail::draw(float elapsed) { if (!particlesystem_script) return; ParticleSystem::draw(elapsed); if (particlesystem_stream.size()) { Textures::bind(particlesystem_texture); gl::begin(gl::Quads); Stream::iterator first = particlesystem_stream.begin(); float tp = now - (*first)->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; } tp = (now - (*first)->time()) / particlesystem_script->timeout(); if (tp > 0) { 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((*first)->location() + (*first)->axis().left() * particlesystem_radius * fp); glTexCoord2f(1, 1); gl::vertex((*first)->location() - (*first)->axis().left() * particlesystem_radius * fp); Stats::quads++; } Stream::iterator next = first; for (next++; next != particlesystem_stream.end(); 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; } t = (now - (*next)->time()) / particlesystem_script->timeout(); color.a = fp * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(1, 0); gl::vertex((*first)->location() - (*first)->axis().left() * particlesystem_radius * fp); glTexCoord2f(0, 0); gl::vertex((*first)->location() + (*first)->axis().left() * particlesystem_radius * fp); color.a = f * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(0, 1); gl::vertex((*next)->location() + (*next)->axis().left() * particlesystem_radius * f); glTexCoord2f(1, 1); gl::vertex((*next)->location() - (*next)->axis().left() * particlesystem_radius * f); Stats::quads++; first = next; fp = f; tp = t; } gl::end(); } } /* ---- class Flame ------------------------------------------------ */ Flame::Flame(ParticleScript *script, core::Entity *entity, model::Particles *modelclass) : ParticleSystem(script, entity, modelclass) { } Flame::~Flame() {} void Flame::draw(float elapsed) { if (!particlesystem_script) return; ParticleSystem::draw(elapsed); if (particlesystem_stream.size() > 1) { Textures::bind(particlesystem_texture); gl::begin(gl::Quads); Stream::iterator first = particlesystem_stream.begin(); float tp = now - (*first)->time(); float fp = 0; if (tp < particlesystem_script->timeout() * particlesystem_script->offset()) { fp = tp / (particlesystem_script->offset() * particlesystem_script->timeout()); } else { tp = tp - particlesystem_script->timeout() * particlesystem_script->offset(); fp = tp / ((1.0f - particlesystem_script->offset()) * particlesystem_script->timeout()); fp = 1.0 - fp; } tp = (now - (*first)->time()) / particlesystem_script->timeout(); Stream::iterator next = first; if (tp > 0) { color.a = 0; gl::color(color); glTexCoord2f(1, 0); gl::vertex(ejector_location); glTexCoord2f(0, 0); gl::vertex(ejector_location); color.a = fp * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(0, tp); gl::vertex((*next)->location() + (*next)->axis().left() * particlesystem_radius * fp); glTexCoord2f(1, tp); gl::vertex((*next)->location() + (*next)->axis().up() * particlesystem_radius * fp); Stats::quads++; color.a = 0; gl::color(color); glTexCoord2f(1, 0); gl::vertex(ejector_location); glTexCoord2f(0, 0); gl::vertex(ejector_location); color.a = fp * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(0, tp); gl::vertex((*next)->location() + (*next)->axis().up() * particlesystem_radius * fp); glTexCoord2f(1, tp); gl::vertex((*next)->location() - (*next)->axis().left() * particlesystem_radius * fp); Stats::quads++; color.a = 0; gl::color(color); glTexCoord2f(1, 0); gl::vertex(ejector_location); glTexCoord2f(0, 0); gl::vertex(ejector_location); color.a = fp * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(0, tp); gl::vertex((*next)->location() + (*next)->axis().left() * particlesystem_radius * fp); glTexCoord2f(1, tp); gl::vertex((*next)->location() - (*next)->axis().up() * particlesystem_radius * fp); Stats::quads++; color.a = 0; gl::color(color); glTexCoord2f(1, 0); gl::vertex(ejector_location); glTexCoord2f(0, 0); gl::vertex(ejector_location); color.a = fp * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(0, tp); gl::vertex((*next)->location() - (*next)->axis().up() * particlesystem_radius * fp); glTexCoord2f(1, tp); gl::vertex((*next)->location() - (*next)->axis().left() * particlesystem_radius * fp); Stats::quads++; } for (next++; next != particlesystem_stream.end(); next++) { float t = now - (*next)->time(); float f = 0; if (t < particlesystem_script->timeout() * particlesystem_script->offset()) { f = t / (particlesystem_script->offset() * particlesystem_script->timeout()); } else { t = t - particlesystem_script->timeout() * particlesystem_script->offset(); f = t / ((1.0f - particlesystem_script->offset()) * particlesystem_script->timeout()); f = 1.0 - f; } t = (now - (*next)->time()) / particlesystem_script->timeout(); color.a = fp * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(1, tp); gl::vertex((*first)->location() + (*first)->axis().up() * particlesystem_radius * fp); glTexCoord2f(0, tp); gl::vertex((*first)->location() + (*first)->axis().left() * particlesystem_radius * fp); color.a = f * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(0, t); gl::vertex((*next)->location() + (*next)->axis().left() * particlesystem_radius * f); glTexCoord2f(1, t); gl::vertex((*next)->location() + (*next)->axis().up() * particlesystem_radius * f); Stats::quads++; color.a = fp * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(1, tp); gl::vertex((*first)->location() - (*first)->axis().left() * particlesystem_radius * fp); glTexCoord2f(0, tp); gl::vertex((*first)->location() + (*first)->axis().up() * particlesystem_radius * fp); color.a = f * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(0, t); gl::vertex((*next)->location() + (*next)->axis().up() * particlesystem_radius * f); glTexCoord2f(1, t); gl::vertex((*next)->location() - (*next)->axis().left() * particlesystem_radius * f); Stats::quads++; color.a = fp * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(1, tp); gl::vertex((*first)->location() - (*first)->axis().up() * particlesystem_radius * fp); glTexCoord2f(0, tp); gl::vertex((*first)->location() + (*first)->axis().left() * particlesystem_radius * fp); color.a = f * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(0, t); gl::vertex((*next)->location() + (*next)->axis().left() * particlesystem_radius * f); glTexCoord2f(1, t); gl::vertex((*next)->location() - (*next)->axis().up() * particlesystem_radius * f); Stats::quads++; color.a = fp * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(1, tp); gl::vertex((*first)->location() - (*first)->axis().left() * particlesystem_radius * fp); glTexCoord2f(0, tp); gl::vertex((*first)->location() - (*first)->axis().up() * particlesystem_radius * fp); color.a = f * particlesystem_script->alpha(); gl::color(color); glTexCoord2f(0, t); gl::vertex((*next)->location() - (*next)->axis().up() * particlesystem_radius * f); glTexCoord2f(1, t); gl::vertex((*next)->location() - (*next)->axis().left() * particlesystem_radius * f); Stats::quads++; first = next; fp = f; tp = t; } gl::end(); } } } // namespace render