/* render/particleejector.cc This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ #include #include "core/application.h" #include "render/particleejector.h" #include "render/camera.h" #include "render/gl.h" #include "render/textures.h" #include "render/draw.h" namespace render { /* ---- class ParticleEjector -------------------------------------- */ ParticleEjector::ParticleEjector(const ParticleEjectorScript &script) : ParticleEjectorScript(script) { ejector_last_eject = 0; ejector_enabled = true; ejector_timestamp = 0; } ParticleEjector::~ParticleEjector() { clear(); } void ParticleEjector::clear() { for (Particles::iterator it = particles().begin(); it != particles().end(); ++it) { delete (*it); (*it) = 0; } ejector_last_eject = 0; } void ParticleEjector::frame(const float seconds, const Camera &camera, const math::Vector3f & ps_location, const math::Axis & ps_axis, const float thrust_factor) { unsigned long now = core::application()->timestamp(); // remove dead particles Particles::reverse_iterator it = particles().rbegin(); while ((it != particles().rend()) && ((*it)->timestamp() + lifespan() <= now)) { delete(*particles().rbegin()); particles().pop_back(); it = particles().rbegin(); } ejector_last_eject += (unsigned long) (1000.0f * seconds); // add new particles if (ejector_enabled) { if (!ejector_timestamp) { ejector_timestamp = now; } if (!timeout() || (ejector_timestamp + timeout() > now)) { while (ejector_last_eject > interval()) { math::Vector3f particle_location; math::Axis particle_axis; if (!attached()) { particle_location.assign(ps_location); particle_axis.assign(ps_axis); } particle_axis.change_roll(math::randomf(360.0f)); if (cone() > 0) { particle_axis.change_pitch(math::randomf(cone()) - (cone() * 0.5f) ); } if (spawn_radius() > 0) { // FIXME find a faster formula math::Axis random_axis(ps_axis); random_axis.change_roll(math::randomf(360.0f)); random_axis.change_pitch(math::randomf(180.0f)); particle_location += random_axis.forward() * spawn_radius(); } Particle *particle = new Particle(particle_location, particle_axis, now); particle->set_radius(radius_vec()[0]); particle->set_color(color()); particle->get_color().a = alpha_vec()[0]; particle->set_rotation(math::randomf(2.0f * M_PI)); particle->set_speed(math::randomf(speed_vec()[0], speed_vec()[1]) * thrust_factor ); particles().push_front(particle); if (type() == Streak) { Particle *tail = new Particle(particle_location, particle_axis, now); tail->set_radius(radius_vec()[0]); tail->set_color(color()); tail->get_color().a = alpha_vec()[0]; tail->set_rotation(particle->rotation()); tail->set_speed(math::randomf(tailspeed_vec()[0], tailspeed_vec()[1])); particles().push_front(tail); } ejector_last_eject -= interval(); } } } else { ejector_last_eject = 0; ejector_timestamp = 0; } for (Particles::iterator it = particles().begin(); it != particles().end(); ++it) { Particle *particle = (*it); if (particle->timestamp() < now) { // apply acceleration if (acceleration()) { particle->set_speed(particle->speed() + acceleration() * seconds); } // apply velocity particle->get_location() += (particle->axis().forward() * particle->speed() * seconds); // interpolate particle radius, color and alpha const float age = (float) (now - particle->timestamp()); const float halflife = offset() * (float) lifespan(); float t; if (age < halflife) { t = age / halflife; particle->set_radius((1.0f - t) * radius_vec()[0] + t * radius_vec()[1]); particle->get_color().a = ((1.0f - t) * alpha_vec()[0] + t * alpha_vec()[1]); t *= 0.5f; } else { t = (age - halflife) / ((float) lifespan() - halflife); particle->set_radius((1.0f - t) * radius_vec()[1] + t * radius_vec()[2]); particle->get_color().a = ((1.0f - t) * alpha_vec()[1] + t * alpha_vec()[2]); t = 0.5f + t * 0.5f; } if (color_interpolated()) { particle->get_color().r = color().r * (1.0f - t) + color_second().r * t; particle->get_color().g = color().g * (1.0f - t) + color_second().g * t; particle->get_color().b = color().b * (1.0f - t) + color_second().b * t; } } } switch(cull()) { case model::CullNone: gl::disable(GL_CULL_FACE); break; case model::CullBack: gl::enable(GL_CULL_FACE); gl::cullface(GL_BACK); break; case model::CullFront: gl::enable(GL_CULL_FACE); gl::cullface(GL_FRONT); break; } if (particles().size()) { draw(camera, ps_location, ps_axis); } } void ParticleEjector::draw(const Camera &camera, const math::Vector3f & ps_location, const math::Axis & ps_axis) { } /* ---- class ParticleEjectorSprite --------------------------------- */ ParticleEjectorSprite::ParticleEjectorSprite(const ParticleEjectorScript &script) : ParticleEjector(script) { } ParticleEjectorSprite::~ParticleEjectorSprite() { } void ParticleEjectorSprite::draw(const Camera &camera, const math::Vector3f & ps_location, const math::Axis & ps_axis) { math::Vector3f quad[4]; Textures::bind(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 (Particles::iterator it = particles().begin(); it != particles().end(); it++) { Particle *particle = (*it); math::Axis rotation; rotation.rotate(camera.axis().forward(), particle->rotation()); math::Vector3f l(attached() ? ps_location + ps_axis * particle->location() : particle->location()); const float r = particle->radius(); gl::color(particle->color()); glTexCoord2f(0, 1); gl::vertex(l + rotation * quad[0] * r); glTexCoord2f(0, 0); gl::vertex(l + rotation * quad[1] * r); glTexCoord2f(1, 0); gl::vertex(l + rotation * quad[2] * r); glTexCoord2f(1, 1); gl::vertex(l + rotation * quad[3] * r); Stats::quads++; } gl::end(); } /* ---- class ParticleEjectorFlare --------------------------------- */ ParticleEjectorFlare::ParticleEjectorFlare(const ParticleEjectorScript &script) : ParticleEjector(script) { } ParticleEjectorFlare::~ParticleEjectorFlare() { } void ParticleEjectorFlare::draw(const Camera &camera, const math::Vector3f & ps_location, const math::Axis & ps_axis) { Textures::bind(texture()); gl::begin(gl::Quads); for (Particles::iterator it = particles().begin(); it != particles().end(); it++) { Particle *particle = (*it); math::Vector3f particle_location(attached() ? ps_location + ps_axis * particle->location() : particle->location()); math::Axis particle_axis(attached() ? ps_axis * particle->axis() : particle->axis()); const float r = particle->radius(); gl::color(particle->color()); glTexCoord2f(0, 1); gl::vertex(particle_location + (particle_axis.up() - particle_axis.left()) * r); glTexCoord2f(0, 0); gl::vertex(particle_location + (particle_axis.up() + particle_axis.left()) * r); glTexCoord2f(1, 0); gl::vertex(particle_location + (particle_axis.up() * -1 + particle_axis.left()) * r); glTexCoord2f(1, 1); gl::vertex(particle_location + (particle_axis.up() * -1 - particle_axis.left()) * r); Stats::quads++; } gl::end(); } /* ---- class ParticleEjectorTrail --------------------------------- */ ParticleEjectorTrail::ParticleEjectorTrail(const ParticleEjectorScript &script) : ParticleEjector(script) { } ParticleEjectorTrail::~ParticleEjectorTrail() { } void ParticleEjectorTrail::draw(const Camera &camera, const math::Vector3f & ps_location, const math::Axis & ps_axis) { if (!particles().size()) { return; } (*particles().rbegin())->get_color().a = (0.0f); Particles::iterator first = particles().begin(); Particles::iterator next = first; ++next; Textures::bind(texture()); gl::begin(gl::Quads); gl::color((*first)->color()); //math::Vector3f first_location(attached() ? ps_location + ps_axis * (*first)->location() : (*first)->location()); math::Vector3f first_normal(math::crossproduct(((*first)->location() - ps_location), ((*first)->location() - camera.location()))); first_normal.normalize(); math::Vector3f next_normal(first_normal); const float length = (float) particles().size(); float position = 1.0f; glTexCoord2f(1, 0); gl::vertex(ps_location - first_normal * (*first)->radius()); glTexCoord2f(0, 0); gl::vertex(ps_location + first_normal * (*first)->radius()); glTexCoord2f(0, position / length); gl::vertex((*first)->location() + next_normal * (*first)->radius()); glTexCoord2f(1, position / length); gl::vertex((*first)->location() - next_normal * (*first)->radius()); Stats::quads++; while (next != particles().end()) { next_normal.assign(math::crossproduct(((*next)->location() - (*first)->location()), ((*next)->location() - camera.location()))); next_normal.normalize(); gl::color((*first)->color()); glTexCoord2f(1, position / length); gl::vertex((*first)->location() - first_normal * (*first)->radius()); glTexCoord2f(0, position / length); gl::vertex((*first)->location() + first_normal * (*first)->radius()); gl::color((*next)->color()); position += 1.0f; glTexCoord2f(0, position / length); gl::vertex((*next)->location() + next_normal * (*next)->radius()); glTexCoord2f(1, position / length); gl::vertex((*next)->location() - next_normal * (*next)->radius()); Stats::quads++; first_normal = next_normal; first = next; ++next; } gl::end(); } /* ---- class ParticleEjectorStreak --------------------------------- */ ParticleEjectorStreak::ParticleEjectorStreak(const ParticleEjectorScript &script) : ParticleEjector(script) { } ParticleEjectorStreak::~ParticleEjectorStreak() { } void ParticleEjectorStreak::draw(const Camera &camera, const math::Vector3f & ps_location, const math::Axis & ps_axis) { if (!particles().size()) { return; } assert( !(particles().size() % 2) ); Particles::iterator first = particles().begin(); Particles::iterator next = first; math::Vector3f normal; Textures::bind(texture()); gl::begin(gl::Quads); while (first != particles().end()) { next = first; ++next; math::Vector3f first_location(attached() ? ps_location + ps_axis * (*first)->location() : (*first)->location()); math::Vector3f next_location(attached() ? ps_location + ps_axis * (*next)->location() : (*next)->location()); normal.assign(math::crossproduct((first_location - camera.location()), (next_location - camera.location()))); normal.normalize(); gl::color((*first)->color()); glTexCoord2f(1, 0); gl::vertex(first_location - normal * (*first)->radius()); glTexCoord2f(0, 0); gl::vertex(first_location + normal * (*first)->radius()); gl::color((*next)->color()); glTexCoord2f(0, 1); gl::vertex(next_location + normal * (*next)->radius()); glTexCoord2f(1, 1); gl::vertex(next_location - normal * (*next)->radius()); Stats::quads++; ++first; ++first; } gl::end(); } } // namespace render