/* 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 "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::Vector3f &up, const math::Vector3f &left, float time) : particle_location(location), particle_up(up), particle_left(left) { 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) { particles_texture = texture; particles_entity = entity; if (!entity) { particles_oriented = false; } else { particles_oriented = oriented; } particles_radius = radius; particles_timeout = 1.5f; // a particles lives for 3 seconds particles_eject = 0.02f; // a particle is ejected every 0.2 seconds particles_last_eject = 0; // timestamp of the last eject } ParticleStream::~ParticleStream() { for (Particles::iterator it = particles_stream.begin(); it != particles_stream.end(); it++) { delete (*it); } particles_stream.clear(); } void ParticleStream::draw() { float now = core::application()->time(); // remove dead particles Particles::iterator it = particles_stream.begin(); while ((it != particles_stream.end()) && ((*it)->time() + particles_timeout <= now)) { delete (*it); particles_stream.erase(it); it = particles_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(); } 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 { particles_stream.push_back(new Particle(location(), now)); } particles_last_eject = now; } math::Vector3f quad[4]; if (particles_stream.size()) { Textures::bind(particles_texture); gl::begin(gl::Quads); for (Particles::reverse_iterator rit = particles_stream.rbegin(); rit != particles_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()); } float t = now - particle->time(); float f = 0; if (t < particles_timeout * 0.1f) { f = t / (0.1f * particles_timeout); } else { t = t - particles_timeout * 0.1f; f = t / (0.9f * particles_timeout); f = 1.0 - f; } f *= f; float radius = particles_radius * f; particles_color.a = f; gl::color(particles_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(); } } } // namespace render