#include "particles.h"
#include "../util/util_time.h"
#include "../util/util.h"
#include <string.h>
#include <stdlib.h>
ParticleManager particles;
static int compare_particles(const void *_a, const void *_b);
void Particles_Init()
{
memset( &particles, 0, sizeof(ParticleManager) );
}
ParticleSystem *Particles_AddSystem()
{
if(particles.num_systems >= MAX_PARTICLES_SYSTEMS)
return NULL;
ParticleSystem *s = malloc( sizeof(ParticleSystem) );
particles.systems[particles.num_systems] = s;
memset( s, 0, sizeof(ParticleSystem) );
particles.num_systems += 1;
return s;
}
void Particles_RemoveSystem(ParticleSystem *system)
{
int index = &system - particles.systems;
if(index > MAX_PARTICLES_SYSTEMS || index < 0)
{
fprintf(stderr, "Invalid particle system!\n");
return;
}
/* TODO: Finish this function */
}
void Particles_EmitParticle(ParticleSystem *system, Particle *p)
{
if(system->num_particles >= MAX_PARTICLES_PER_SYSTEM)
return;
Particle *dest = &system->particles[system->num_particles];
memcpy(dest, p, sizeof(Particle));
system->num_particles += 1;
}
void Particles_Update(const Vec3 *camera_position)
{
Particle *c = NULL; /* Current particle */
ParticleSystem *s = NULL; /* Current particle system */
Texture *t = NULL; /* Texture of the current system */
Vec3 d; /* Vector for calculating the distance to the camera */
Vec3 velocity;
int i, j;
for(i = 0; i < particles.num_systems; i++)
{
s = particles.systems[i];
t = s->texture;
/* We ceil it so we dont truncate the decimal part and get always 0 */
int particles_to_emit = (int)ceil( s->emit_rate * Time_GetFrameTime() );
/* Emit the automatically emited particles */
for(j = 0; j < particles_to_emit; j++)
{
velocity.x = Util_RandomF(0.0f, 50.0f);
velocity.y = Util_RandomF(0.0f, 50.0f);
velocity.z = Util_RandomF(0.0f, 50.0f);
velocity = vec3_normalize(&velocity);
if(s->num_particles >= MAX_PARTICLES_PER_SYSTEM)
break; /* So we dont overflow */
c = &s->particles[s->num_particles ++];
memset(c, 0, sizeof(Particle));
c->life_length = s->life_length;
c->position = s->position;
c->scale = 10.0f;
c->weight = s->weight;
c->velocity = vec3_scalar_mul(&velocity, s->speed);
}
for(j = 0; j < s->num_particles; j++)
{
c = &s->particles[j];
/* Update each particle -.- */
c->velocity.y += c->weight * Time_GetFrameTime();
Vec3 velocity = vec3_scalar_mul(&c->velocity, Time_GetFrameTime());
c->position = vec3_add(&c->position, &velocity);
/* Update animations
* How far into its life the particle is */
float life_factor = c->elapsed_time / c->life_length;
/* How many different stages there are */
int stage_count = t->number_of_rows * t->number_of_rows;
float atlas_progression = life_factor * stage_count;
int index = floor(atlas_progression);
int column = index % t->number_of_rows;
int row = index / t->number_of_rows;
c->tex_offset.x = (float)column / t->number_of_rows;
c->tex_offset.y = (float)row / t->number_of_rows;
/* End of animation update */
c->elapsed_time += Time_GetFrameTime();
if(c->elapsed_time > c->life_length)
{
/* "Kill" the particle by overriding it with the last particle on the array */
memmove( c, &s->particles[s->num_particles], sizeof(Particle) );
s->num_particles -= 1;
}
else
{
d = vec3_sub(camera_position, &c->position);
c->distance_to_camera = vec3_length2(&d);
}
}
/*Sort all particles on system by distance to camera */
qsort(s->particles, s->num_particles, sizeof(Particle), compare_particles);
}
}
void Particles_Shutdown()
{
int i;
for(i = 0; i < particles.num_systems; i++)
{
if(particles.systems[i])
free(particles.systems[i]);
}
memset(&particles, 0, sizeof(ParticleManager));
}
static int compare_particles(const void *_a, const void *_b)
{
Particle *a = (Particle *)_a;
Particle *b = (Particle *)_b;
if(a->distance_to_camera < b->distance_to_camera) return -1;
if(a->distance_to_camera > b->distance_to_camera) return 1;
return 0;
}