aboutsummaryrefslogtreecommitdiff
#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;
}