/* 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; } /* ---- class ParticleSystem --------------------------------------- */ ParticleSystem::ParticleSystem(const ParticleScript *script, const core::Entity *entity, const model::Particles *modelclass) { particlesystem_script = script; particlesystem_entity = entity; particlesystem_modelclass = modelclass; particlesystem_last_eject = 0; // timestamp of the last eject particlesystem_cull = model::CullNone; particlesystem_texture = 0; particlesystem_radius = 1.0f; if (particlesystem_script) { // initial particle system parameters are set by the script particlesystem_texture = Textures::load("textures/" + particlesystem_script->texture()); particlesystem_radius = particlesystem_script->radius(); particlesystem_cull = particlesystem_script->cull(); if (particlesystem_script->entity()) { if (particlesystem_script->entity_second()) { // entity tertiary color for (size_t i = 0; i < 3; i++) { particlesystem_color[i] = (entity->color()[i] + entity->color_second()[i]) / 2; } } else { // entity primary color particlesystem_color.assign(entity->color()); } } else if (particlesystem_script->entity_second()) { // entity secondary color particlesystem_color.assign(entity->color_second()); } else if (particlesystem_script->engine()) { // FIXME engine_color or thrust_activated particlesystem_color.assign(entity->model()->enginecolor()); } else { particlesystem_color.assign(particlesystem_script->color()); } particlesystem_axis.assign(particlesystem_script->axis()); } if (particlesystem_modelclass) { // the modeltag can override the script parameters particlesystem_radius *= particlesystem_modelclass->scale(); if (particlesystem_modelclass->entity()) { if (particlesystem_modelclass->entity_second()) { // entity tertiary color for (size_t i = 0; i < 3; i++) { particlesystem_color[i] = (entity->color()[i] + entity->color_second()[i]) / 2; } } else { // entity primary color particlesystem_color.assign(entity->color()); } } else if (particlesystem_modelclass->entity_second()) { // entity secondary color particlesystem_color.assign(entity->color_second()); } else if (particlesystem_modelclass->has_color()) { particlesystem_color.assign(particlesystem_modelclass->color()); } else if (particlesystem_modelclass->engine()) { // FIXME engine_color or thrust_activated particlesystem_color.assign(entity->model()->enginecolor()); } particlesystem_location.assign(modelclass->location()); particlesystem_axis.assign(modelclass->axis() * particlesystem_axis); } // rescale particle system according to entity radius if (entity->model() && entity->model()->radius()) { const float modelscale = entity->radius() / entity->model()->radius(); particlesystem_location *= modelscale; particlesystem_radius *= modelscale; } } 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(const float elapsed) { if (particlesystem_entity->type() == core::Entity::Controlable) { const core::EntityControlable *controlable = static_cast(particlesystem_entity); if (controlable->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) { const core::EntityDynamic *ed = static_cast(particlesystem_entity); if (ed->speed()) { ejector_active = true; } } else if (particlesystem_entity->type() == core::Entity::Controlable) { const core::EntityControlable *controlable = static_cast(particlesystem_entity); if ((controlable->thrust() > 0.0f) || (controlable->state() == core::Entity::ImpulseInitiate) || (controlable->state() == core::Entity::Impulse)) { ejector_active = true; } } else if (particlesystem_entity->type() == core::Entity::Projectile) { ejector_active = true; } } // add new particles if (ejector_active && (particlesystem_last_eject + particlesystem_script->eject() <= now)) { math::Axis particle_axis(particlesystem_entity->axis() * particlesystem_axis); math::Vector3f particle_location(ejector_location); if (particlesystem_script->type() == ParticleScript::Spray) { particle_axis.change_direction(math::randomf(180)); particle_axis.change_pitch(math::randomf(180)); particle_axis.change_roll(math::randomf(180)); particle_location += particle_axis.forward() * particlesystem_entity->radius() * 0.5f; } particlesystem_stream.push_front(new Particle(particle_location, particle_axis, now)); particlesystem_last_eject = now; } } /* ---- class Jet -------------------------------------------------- */ Jet::Jet(const ParticleScript *script, const core::Entity *entity, const model::Particles *modelclass) : ParticleSystem(script, entity, modelclass) { } Jet::~Jet() {} void Jet::draw(const 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; particlesystem_color.a = f * particlesystem_script->alpha(); gl::color(particlesystem_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(const ParticleScript *script, const core::Entity *entity, const model::Particles *modelclass) : ParticleSystem(script, entity, modelclass) { } Trail::~Trail() {} void Trail::draw(const 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) { particlesystem_color.a = 0.0f; gl::color(particlesystem_color); glTexCoord2f(1, 0); gl::vertex(ejector_location); glTexCoord2f(0, 0); gl::vertex(ejector_location); particlesystem_color.a = fp; gl::color(particlesystem_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(); particlesystem_color.a = fp * particlesystem_script->alpha(); gl::color(particlesystem_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); particlesystem_color.a = f * particlesystem_script->alpha(); gl::color(particlesystem_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(const ParticleScript *script, const core::Entity *entity, const model::Particles *modelclass) : ParticleSystem(script, entity, modelclass) { } Flame::~Flame() {} void Flame::draw(const 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) { particlesystem_color.a = 0; gl::color(particlesystem_color); glTexCoord2f(1, 0); gl::vertex(ejector_location); glTexCoord2f(0, 0); gl::vertex(ejector_location); particlesystem_color.a = fp * particlesystem_script->alpha(); gl::color(particlesystem_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++; particlesystem_color.a = 0; gl::color(particlesystem_color); glTexCoord2f(1, 0); gl::vertex(ejector_location); glTexCoord2f(0, 0); gl::vertex(ejector_location); particlesystem_color.a = fp * particlesystem_script->alpha(); gl::color(particlesystem_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++; particlesystem_color.a = 0; gl::color(particlesystem_color); glTexCoord2f(1, 0); gl::vertex(ejector_location); glTexCoord2f(0, 0); gl::vertex(ejector_location); particlesystem_color.a = fp * particlesystem_script->alpha(); gl::color(particlesystem_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++; particlesystem_color.a = 0; gl::color(particlesystem_color); glTexCoord2f(1, 0); gl::vertex(ejector_location); glTexCoord2f(0, 0); gl::vertex(ejector_location); particlesystem_color.a = fp * particlesystem_script->alpha(); gl::color(particlesystem_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(); particlesystem_color.a = fp * particlesystem_script->alpha(); gl::color(particlesystem_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); particlesystem_color.a = f * particlesystem_script->alpha(); gl::color(particlesystem_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++; particlesystem_color.a = fp * particlesystem_script->alpha(); gl::color(particlesystem_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); particlesystem_color.a = f * particlesystem_script->alpha(); gl::color(particlesystem_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++; particlesystem_color.a = fp * particlesystem_script->alpha(); gl::color(particlesystem_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); particlesystem_color.a = f * particlesystem_script->alpha(); gl::color(particlesystem_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++; particlesystem_color.a = fp * particlesystem_script->alpha(); gl::color(particlesystem_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); particlesystem_color.a = f * particlesystem_script->alpha(); gl::color(particlesystem_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(); } } /* ---- class Spray ------------------------------------------------ */ Spray::Spray(const ParticleScript *script, const core::Entity *entity, const model::Particles *modelclass) : ParticleSystem(script, entity, modelclass) { } Spray::~Spray() { } void Spray::draw(const 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); 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())); for (Stream::iterator it = particlesystem_stream.begin(); it != particlesystem_stream.end(); it++) { Particle *particle = (*it); const float t = now - particle->time(); const float f = t / particlesystem_script->timeout(); const float radius = particlesystem_radius * f; particlesystem_color.a = (1.0f - f) * particlesystem_script->alpha(); gl::color(particlesystem_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