/*
   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::particlescript_registry;

void ParticleScript::list()
{
	
	for (Registry::const_iterator it = particlescript_registry.begin(); it != particlescript_registry.end(); it++) {

		const ParticleScript *script = (*it).second;
		
		std::string strval;
		size_t count = 0;
		while (script) {
		
			switch (script->type()) {
				case Flame:
					strval.append(" flame");
					break;
				case Jet:
					strval.append(" jet");
					break;
				case Spray:
					strval.append(" spray");
					break;
				case Trail:
					strval.append(" trail");
					break;
			}

			count++;
			script = script->next();
		}
		con_print << "  " << (*it).second->label() << strval << " " << count << " " << aux::plural("ejector", count) << std::endl;		
		con_print << particlescript_registry.size() << " particle scripts" << std::endl;
	}
}

void ParticleScript::clear()
{
	for (Registry::iterator it = particlescript_registry.begin(); it != particlescript_registry.end(); it++) {
		delete(*it).second;
		(*it).second = 0;
	}
	particlescript_registry.clear();
}

ParticleScript *ParticleScript::find(const std::string &label)
{
	Registry::iterator it = particlescript_registry.find(label);

	if (it != particlescript_registry.end()) {
		return (*it).second;
	} else {
		return 0;
	}
}

ParticleScript *ParticleScript::load(const std::string &label)
{
	ParticleScript *parent_script = find(label);
	if (parent_script)
		return parent_script;

	filesystem::IniFile inifile;

	inifile.open("particles/" + label);

	if (!inifile.is_open()) {
		con_warn << "Could not open " << inifile.name() << std::endl;
		return 0;
	}

	ParticleScript *script = 0;
	std::string strval;
	float pitch, yaw, roll = 0.0f;

	while (inifile.getline()) {

		if (inifile.got_section()) {
	
			if (inifile.got_section("particles")) {
				if (script) {
					script->particlescript_next = new ParticleScript(std::string());
					script = script->particlescript_next;
				} else {
					script = new ParticleScript(label);
					particlescript_registry[script->label()] = script;
					parent_script = script;
				}
				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->particlescript_type = ParticleScript::Flame;
					} else if (strval.compare("jet") == 0) {
						script->particlescript_type = ParticleScript::Jet;
					} else if (strval.compare("trail") == 0) {
						script->particlescript_type = ParticleScript::Trail;
					} else {
						inifile.unknown_value();
					}
					continue;
					
				} else if (inifile.got_key_string("cull", strval)) {
					aux::to_label(strval);
					if (strval.compare("none") == 0) {
						script->particlescript_cull = model::CullNone;
					} else if (strval.compare("back") == 0) {
						script->particlescript_cull = model::CullBack;
					} else if (strval.compare("front") == 0) {
						script->particlescript_cull = model::CullFront;
					} else {
						inifile.unknown_value();
					}
					continue;

				} else if (inifile.got_key_string("texture", script->particlescript_texture)) {
					Textures::load("textures/" + script->texture());
					continue;
				} else if (inifile.got_key_float("speed", script->particlescript_speed)) {
					continue;
				} else if (inifile.got_key_float("offset", script->particlescript_offset)) {
					math::clamp(script->particlescript_offset, 0.0f, 1.0f);
					continue;
				} else if (inifile.got_key_float("alpha", script->particlescript_alpha)) {
					continue;
				} else if (inifile.got_key_float("radius", script->particlescript_radius)) {
					script->particlescript_radius *= model::SCALE;
					continue;
				} else if (inifile.got_key_float("eject", script->particlescript_eject)) {
					continue;
				} else if (inifile.got_key_float("timeout", script->particlescript_timeout)) {
					continue;
				} else if (inifile.got_key_color("color", script->particlescript_color)) {
					script->particlescript_has_color = true;
					continue;
				} else if (inifile.got_key_bool("engine", script->particlescript_engine)) {
					continue;
				} else if (inifile.got_key_bool("entity", script->particlescript_entity)) {
					continue;
				} else if (inifile.got_key_float("angle", yaw)) {
				
					if (yaw == model::ANGLEUP) {
						script->particlescript_axis.change_pitch(-90.0f);
					} else if (yaw == model::ANGLEDOWN) {
						script->particlescript_axis.change_pitch(90.0f);
					} else {
						script->particlescript_axis.change_direction(yaw);
					}		
					continue;
					
				} else if (inifile.got_key("angles")) {
					
					std::istringstream str(inifile.value());
					if (str >> pitch >> yaw >> roll) {
						script->particlescript_axis.assign(yaw, pitch, roll);
					} else {
						inifile.unknown_value();
					}
					continue;

				} else if (inifile.got_key_float("pitch", pitch)) {
					script->particlescript_axis.change_pitch(-pitch);
					continue;

				} else if (inifile.got_key_float("yaw", yaw)) {
					script->particlescript_axis.change_direction(yaw);
					continue;

				} else if (inifile.got_key_float("roll", roll)) {
					script->particlescript_axis.change_roll(-roll);
					continue;
	
				} else {
					inifile.unknown_key();
				}
			}
		}
	}

	inifile.close();
	
	strval.clear();
	
	size_t count = 0;
	for (script = parent_script; script != 0; script = script->particlescript_next) {
		switch (script->type()) {
			case Flame:
				strval.append(" flame");
				break;
			case Jet:
				strval.append(" jet");
				break;
			case Spray:
				strval.append(" spray");
				break;
			case Trail:
				strval.append(" trail");
				break;
		}
		count++;
	}
	con_debug << "  " << inifile.name() << strval << " " << count << " " << aux::plural("ejector", count) << std::endl;

	return parent_script;
}

/* ---- class ParticleScript --------------------------------------- */

ParticleScript::ParticleScript(const std::string & label) : particlescript_label(label)
{
	particlescript_entity = false;
	particlescript_engine = false;
	particlescript_has_color = true;
	particlescript_radius = 1.0f;
	particlescript_alpha = 1.0f;

	particlescript_speed = 0.0f;
	particlescript_timeout = 2.0f;
	particlescript_eject = 0.25f;
	particlescript_offset = 0.1f;
	
	particlescript_color.assign(1.0f, 1.0f);
	particlescript_cull = model::CullNone;
	
	particlescript_next = 0;
}

ParticleScript::~ParticleScript()
{
	if (particlescript_next) {
		delete particlescript_next;
	}
}

/* ---- 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) {
		particlesystem_texture = Textures::load("textures/" + particlesystem_script->texture());
		particlesystem_radius = particlesystem_script->radius();
		particlesystem_cull = particlesystem_script->cull();

		if (particlesystem_script->entity()) {
			particlesystem_color.assign(entity->color());
		} else if (particlesystem_script->has_color()) {
			particlesystem_color.assign(particlesystem_script->color());
		} else if (particlesystem_script->engine()) {
			particlesystem_color.assign(entity->model()->enginecolor());
		}
		
		particlesystem_axis.assign(particlesystem_script->axis());
	}

	if (particlesystem_modelclass) {
		particlesystem_radius *= particlesystem_modelclass->scale();

		if (particlesystem_modelclass->entity()) {
			particlesystem_color.assign(entity->color());
		} else if (particlesystem_modelclass->has_color()) {
			particlesystem_color.assign(particlesystem_modelclass->color());
		} else if (particlesystem_modelclass->engine()) {
			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<const core::EntityControlable *>(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<const core::EntityDynamic *>(particlesystem_entity);
			if (ed->speed()) {
				ejector_active = true;
			}
		} else if (particlesystem_entity->type() == core::Entity::Controlable) {
			const core::EntityControlable *controlable = static_cast<const core::EntityControlable *>(particlesystem_entity);
			if ((controlable->thrust() > 0.0f) || (controlable->state() == core::Entity::ImpulseInitiate) || (controlable->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(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 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);

		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;
			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();
	}
}

} // namespace render