ddnet/src/game/client/gc_particles.cpp

147 lines
3.4 KiB
C++
Raw Normal View History

#include <engine/e_client_interface.h>
#include "gc_client.h"
#include "../generated/gc_data.h"
// NOTE: the way the particle system works isn't very cache friendly
enum
{
MAX_PARTICLES=1024*8,
};
static particle particles[MAX_PARTICLES];
static int first_free = -1;
static int first_part[NUM_PARTGROUPS] = {-1};
void particle_reset()
{
// reset particles
for(int i = 0; i < MAX_PARTICLES; i++)
{
particles[i].prev_part = i-1;
particles[i].next_part = i+1;
}
particles[0].prev_part = 0;
particles[MAX_PARTICLES-1].next_part = -1;
first_free = 0;
for(int i = 0; i < NUM_PARTGROUPS; i++)
first_part[i] = -1;
}
void particle_add(int group, particle *part)
{
if (first_free == -1)
return;
// remove from the free list
int id = first_free;
first_free = particles[id].next_part;
particles[first_free].prev_part = -1;
// copy data
particles[id] = *part;
// insert to the group list
particles[id].prev_part = -1;
particles[id].next_part = first_part[group];
if(first_part[group] != -1)
particles[first_part[group]].prev_part = id;
first_part[group] = id;
// set some parameters
particles[id].life = 0;
}
void particle_update(float time_passed)
{
static float friction_fraction = 0;
friction_fraction += time_passed;
if(friction_fraction > 2.0f) // safty messure
friction_fraction = 0;
int friction_count = 0;
while(friction_fraction > 0.05f)
{
friction_count++;
friction_fraction -= 0.05f;
}
for(int g = 0; g < NUM_PARTGROUPS; g++)
{
int i = first_part[g];
while(i != -1)
{
int next = particles[i].next_part;
particles[i].vel += flow_get(particles[i].pos)*time_passed * particles[i].flow_affected;
particles[i].vel.y += particles[i].gravity*time_passed;
for(int f = 0; f < friction_count; f++) // apply friction
particles[i].vel *= particles[i].friction;
// move the point
vec2 vel = particles[i].vel*time_passed;
move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom(), NULL);
particles[i].vel = vel* (1.0f/time_passed);
particles[i].life += time_passed;
particles[i].rot += time_passed * particles[i].rotspeed;
// check particle death
if(particles[i].life > particles[i].life_span)
{
// remove it from the group list
if(particles[i].prev_part != -1)
particles[particles[i].prev_part].next_part = particles[i].next_part;
else
first_part[g] = particles[i].next_part;
if(particles[i].next_part != -1)
particles[particles[i].next_part].prev_part = particles[i].prev_part;
// insert to the free list
if(first_free != -1)
particles[first_free].prev_part = i;
particles[i].prev_part = -1;
particles[i].next_part = first_free;
}
i = next;
}
}
}
void particle_render(int group)
{
gfx_blend_normal();
//gfx_blend_additive();
gfx_texture_set(data->images[IMAGE_PARTICLES].id);
gfx_quads_begin();
int i = first_part[group];
while(i != -1)
{
select_sprite(particles[i].spr);
float a = particles[i].life / particles[i].life_span;
vec2 p = particles[i].pos;
float size = mix(particles[i].start_size, particles[i].end_size, a);
gfx_quads_setrotation(particles[i].rot);
gfx_setcolor(
particles[i].color.r,
particles[i].color.g,
particles[i].color.b,
particles[i].color.a); // pow(a, 0.75f) *
gfx_quads_draw(p.x, p.y, size, size);
i = particles[i].next_part;
}
gfx_quads_end();
gfx_blend_normal();
}