#include "particles.h" #include "../util/util_time.h" #include "../util/util.h" #include #include 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; }