diff --git a/engine/particle_sys.c b/engine/particle_sys.c index 4109f71..805a6c5 100644 --- a/engine/particle_sys.c +++ b/engine/particle_sys.c @@ -4,9 +4,6 @@ #include #include -// TEMPORARY VARIABLE: NEED TO FIND A WAY TO DEAL WITH THIS -#define DELTA_T 0.017 - void init_particle_system(ParticleSystem_t* system) { memset(system, 0, sizeof(ParticleSystem_t)); @@ -17,62 +14,114 @@ void init_particle_system(ParticleSystem_t* system) } system->tail_idx = 0; } -void play_particle_emitter(ParticleSystem_t* system, const ParticleEmitter_t* in_emitter) -{ - if (in_emitter == NULL) return; - if (in_emitter->config == NULL) return; - if (sc_queue_empty(&system->free_list)) return; - uint32_t idx = sc_queue_del_first(&system->free_list); - system->emitter_list[system->tail_idx].next = idx; - system->tail_idx = idx; - system->emitter_list[idx].next = 0; +uint16_t get_number_of_free_emitter(ParticleSystem_t* system) +{ + return sc_queue_size(&system->free_list); +} + +static inline void spawn_particle(ParticleEmitter_t* emitter, uint32_t idx) +{ + uint32_t lifetime = (emitter->config->particle_lifetime[1] - emitter->config->particle_lifetime[0]); + emitter->particles[idx].timer = emitter->config->particle_lifetime[0]; + emitter->particles[idx].timer += rand() % lifetime; + emitter->particles[idx].alive = true; + + float angle = emitter->config->launch_range[1] - emitter->config->launch_range[0]; + angle *= (float)rand() / (float)RAND_MAX; + angle += emitter->config->launch_range[0]; + if(angle > 360) angle -= 360; + if(angle < -360) angle += 360; + + float speed = emitter->config->speed_range[1] - emitter->config->speed_range[0]; + speed *= (float)rand() / (float)RAND_MAX; + speed += emitter->config->speed_range[0]; + + emitter->particles[idx].velocity.x = speed * cos(angle * PI / 180); + emitter->particles[idx].velocity.y = speed * sin(angle * PI / 180); + emitter->particles[idx].position = emitter->position; + emitter->particles[idx].rotation = angle; + emitter->particles[idx].angular_vel = -10 + 20 * (float)rand() / (float)RAND_MAX; + emitter->particles[idx].size = 10 + 20 * (float)rand() / (float)RAND_MAX; +; +} + +uint16_t load_in_particle_emitter(ParticleSystem_t* system, const ParticleEmitter_t* in_emitter) +{ + if (in_emitter == NULL) return 0; + if (in_emitter->config == NULL) return 0 ; + if (in_emitter->config->type == EMITTER_UNKNOWN) return 0; + + if (sc_queue_empty(&system->free_list)) return 0; + uint16_t idx = sc_queue_del_first(&system->free_list); + system->emitters[idx] = *in_emitter; system->emitters[idx].active = true; if (system->emitters[idx].n_particles > MAX_PARTICLES) { system->emitters[idx].n_particles = MAX_PARTICLES; } + return idx; +} + +void play_emitter_handle(ParticleSystem_t* system, uint16_t handle) +{ + if (handle == 0) return; + system->emitter_list[system->tail_idx].next = handle; + system->tail_idx = handle; + system->emitter_list[handle].next = 0; +} + +// An emitter cannot be unloaded or paused mid-way when particles to still +// emitting, so defer into update function to do so +void pause_emitter_handle(ParticleSystem_t* system, uint16_t handle) +{ + if (handle == 0) return; + + system->emitters[handle].active = false; +} + +void unload_emitter_handle(ParticleSystem_t* system, uint16_t handle) +{ + if (handle == 0) return; + + system->emitters[handle].active = false; + system->emitters[handle].finished = true; +} + +void play_particle_emitter(ParticleSystem_t* system, const ParticleEmitter_t* in_emitter) +{ + uint16_t idx = load_in_particle_emitter(system, in_emitter); + if (idx == 0) return; + ParticleEmitter_t* emitter = system->emitters + idx; // Generate particles based on type - for (uint32_t i = 0; i < emitter->n_particles; ++i) + // Burst type need to generate all at once + // TODO: stream type need to generate one at a time + if (emitter->config->type == EMITTER_BURST) { - uint32_t lifetime = (emitter->config->particle_lifetime[1] - emitter->config->particle_lifetime[0]); - emitter->particles[i].timer = emitter->config->particle_lifetime[0]; - emitter->particles[i].timer += rand() % lifetime; - emitter->particles[i].alive = true; - - float angle = emitter->config->launch_range[1] - emitter->config->launch_range[0]; - angle *= (float)rand() / (float)RAND_MAX; - angle += emitter->config->launch_range[0]; - if(angle > 360) angle -= 360; - if(angle < -360) angle += 360; - - float speed = emitter->config->speed_range[1] - emitter->config->speed_range[0]; - speed *= (float)rand() / (float)RAND_MAX; - speed += emitter->config->speed_range[0]; - - emitter->particles[i].velocity.x = speed * cos(angle * PI / 180); - emitter->particles[i].velocity.y = speed * sin(angle * PI / 180); - emitter->particles[i].position = emitter->position; - emitter->particles[i].rotation = angle; - emitter->particles[i].angular_vel = -10 + 20 * (float)rand() / (float)RAND_MAX; - emitter->particles[i].size = 10 + 20 * (float)rand() / (float)RAND_MAX; -; + for (uint32_t i = 0; i < emitter->n_particles; ++i) + { + spawn_particle(emitter, i); + } } + play_emitter_handle(system, idx); } + void update_particle_system(ParticleSystem_t* system) { uint32_t emitter_idx = system->emitter_list[0].next; - uint32_t last_idx = 0; + uint32_t prev_idx = 0; while (emitter_idx != 0) { - uint32_t next_idx = system->emitter_list[emitter_idx].next; ParticleEmitter_t* emitter = system->emitters + emitter_idx; uint32_t inactive_count = 0; for (uint32_t i = 0; i < emitter->n_particles; ++i) { + // TODO: If a particle is not spawned, run its timer. Spawn on zero + + // Otherwise do the usual check if (emitter->particles[i].alive) { if (emitter->update_func != NULL) @@ -87,6 +136,7 @@ void update_particle_system(ParticleSystem_t* system) emitter->particles[i].alive = false; inactive_count++; } + // TODO: If streaming and not one shot, immediately revive the particle } else { @@ -95,18 +145,32 @@ void update_particle_system(ParticleSystem_t* system) } if (inactive_count == emitter->n_particles) { + // TODO: If burst and not one shot, revive all particles emitter->active = false; - system->emitter_list[last_idx].next = system->emitter_list[emitter_idx].next; + } + + if (!emitter->active) + { + if (!emitter->finished && emitter->config->one_shot) + { + emitter->finished = true; + } + system->emitter_list[prev_idx].next = system->emitter_list[emitter_idx].next; system->emitter_list[emitter_idx].next = 0; if (system->tail_idx == emitter_idx) { - system->tail_idx = last_idx; + system->tail_idx = prev_idx; + } + + if (emitter->finished) + { + sc_queue_add_last(&system->free_list, emitter_idx); + emitter_idx = prev_idx; } - sc_queue_add_last(&system->free_list, emitter_idx); - emitter_idx = last_idx; } - last_idx = emitter_idx; - emitter_idx = next_idx; + + prev_idx = emitter_idx; + emitter_idx = system->emitter_list[emitter_idx].next; } } void draw_particle_system(ParticleSystem_t* system) diff --git a/engine/particle_sys.h b/engine/particle_sys.h index 8c81e52..3d458c5 100644 --- a/engine/particle_sys.h +++ b/engine/particle_sys.h @@ -7,6 +7,8 @@ #include #include +typedef uint16_t EmitterHandle; + typedef enum PartEmitterType { EMITTER_BURST = 0, @@ -23,6 +25,7 @@ typedef struct Particle float size; uint32_t timer; bool alive; + bool spawned; }Particle_t; typedef void (*particle_update_func_t)(Particle_t* part, void* user_data); @@ -66,7 +69,16 @@ typedef struct ParticleSystem }ParticleSystem_t; void init_particle_system(ParticleSystem_t* system); +uint16_t get_number_of_free_emitter(ParticleSystem_t* system); + +// For one-shots void play_particle_emitter(ParticleSystem_t* system, const ParticleEmitter_t* in_emitter); + +EmitterHandle load_in_particle_emitter(ParticleSystem_t* system, const ParticleEmitter_t* in_emitter); +void play_emitter_handle(ParticleSystem_t* system, EmitterHandle handle); +void pause_emitter_handle(ParticleSystem_t* system, EmitterHandle handle); +void unload_emitter_handle(ParticleSystem_t* system, EmitterHandle handle); + void update_particle_system(ParticleSystem_t* system); void draw_particle_system(ParticleSystem_t* system); void deinit_particle_system(ParticleSystem_t* system); diff --git a/particle_test.c b/particle_test.c index 43fbb12..8e68fe4 100644 --- a/particle_test.c +++ b/particle_test.c @@ -79,6 +79,7 @@ int main(void) }; bool key_press = false; + char text_buffer[32]; while(!WindowShouldClose()) { if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) @@ -92,10 +93,11 @@ int main(void) key_press = false; } update_particle_system(&part_sys); - + sprintf(text_buffer, "free: %u", get_number_of_free_emitter(&part_sys)); BeginDrawing(); ClearBackground(RAYWHITE); draw_particle_system(&part_sys); + DrawText(text_buffer, 0, 0, 16, BLACK); EndDrawing(); } UnloadTexture(tex);