prosperon/source/engine/particle.c

235 lines
6.2 KiB
C
Raw Normal View History

2022-12-28 16:50:54 -06:00
#include "particle.h"
#include "stb_ds.h"
2023-12-29 19:08:53 -06:00
#include "render.h"
#include "particle.sglsl.h"
2024-01-01 17:30:42 -06:00
#include "2dphysics.h"
2023-12-29 19:08:53 -06:00
#include "log.h"
2024-01-02 07:55:22 -06:00
#include "simplex.h"
2024-01-03 08:38:17 -06:00
#include "pthread.h"
2024-01-04 09:46:28 -06:00
#include "math.h"
2022-12-28 16:50:54 -06:00
2024-01-03 12:29:27 -06:00
#define SCHED_IMPLEMENTATION
#include "sched.h"
2023-12-29 19:08:53 -06:00
static emitter **emitters;
static sg_shader par_shader;
static sg_pipeline par_pipe;
static sg_bindings par_bind;
static int draw_count;
2024-01-03 08:38:17 -06:00
#define MAX_PARTICLES 1000000
2024-01-02 07:55:22 -06:00
2024-01-03 12:29:27 -06:00
struct scheduler sched;
void *mem;
2023-12-29 19:08:53 -06:00
struct par_vert {
HMM_Vec2 pos;
float angle;
2024-01-01 17:30:42 -06:00
HMM_Vec2 scale;
2023-12-29 19:08:53 -06:00
struct rgba color;
};
typedef struct par_vert par_vert;
void particle_init()
{
2024-01-03 12:29:27 -06:00
sched_size needed;
scheduler_init(&sched, &needed, 1, NULL);
mem = calloc(needed, 1);
scheduler_start(&sched,mem);
2023-12-29 19:08:53 -06:00
par_shader = sg_make_shader(particle_shader_desc(sg_query_backend()));
par_pipe = sg_make_pipeline(&(sg_pipeline_desc){
.shader = par_shader,
.layout = {
.attrs = {
[1].format = SG_VERTEXFORMAT_FLOAT2,
[2].format = SG_VERTEXFORMAT_FLOAT,
2024-01-01 17:30:42 -06:00
[3].format = SG_VERTEXFORMAT_FLOAT2,
2023-12-29 19:08:53 -06:00
[4].format = SG_VERTEXFORMAT_UBYTE4N,
[0].format = SG_VERTEXFORMAT_FLOAT2,
[0].buffer_index = 1
},
.buffers[0].step_func = SG_VERTEXSTEP_PER_INSTANCE,
},
.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP,
.label = "particle pipeline",
.cull_mode = SG_CULLMODE_BACK,
.colors[0].blend = blend_trans,
.depth = {
.write_enabled = true,
2024-03-03 16:49:34 -06:00
.compare = SG_COMPAREFUNC_LESS_EQUAL
2023-12-29 19:08:53 -06:00
}
});
par_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
2024-01-02 07:55:22 -06:00
.size = sizeof(par_vert)*MAX_PARTICLES,
2023-12-29 19:08:53 -06:00
.type = SG_BUFFERTYPE_VERTEXBUFFER,
.usage = SG_USAGE_STREAM,
.label = "particle buffer"
});
float circleverts[8] = {
0,0,
0,1,
1,0,
1,1,
};
par_bind.vertex_buffers[1] = sg_make_buffer(&(sg_buffer_desc){
.data = (sg_range){.ptr = circleverts, .size = sizeof(float)*8},
.usage = SG_USAGE_IMMUTABLE
});
par_bind.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){});
}
emitter *make_emitter() {
2024-01-04 08:14:37 -06:00
emitter *e = calloc(sizeof(*e),1);
2023-12-29 19:08:53 -06:00
e->max = 20;
2024-01-01 17:30:42 -06:00
arrsetcap(e->particles, e->max);
2023-12-29 19:08:53 -06:00
for (int i = 0; i < arrlen(e->particles); i++)
e->particles[i].life = 0;
e->life = 10;
2024-01-02 07:55:22 -06:00
e->tte = lerp(e->explosiveness, e->life/e->max, 0);
2024-01-01 17:30:42 -06:00
sampler_add(&e->color, 0, (HMM_Vec4){1,1,1,1});
e->scale = 1;
e->speed = 20;
e->texture = texture_from_file("glass_chunk2.gif");
2023-12-29 19:08:53 -06:00
arrpush(emitters,e);
2023-05-12 13:22:05 -05:00
return e;
2022-12-28 16:50:54 -06:00
}
2024-01-14 10:24:31 -06:00
void emitter_free(emitter *e)
2023-12-29 19:08:53 -06:00
{
2024-01-14 10:24:31 -06:00
YughWarn("kill emitter");
2023-12-29 19:08:53 -06:00
arrfree(e->particles);
for (int i = arrlen(emitters)-1; i >= 0; i--)
if (emitters[i] == e) {
arrdelswap(emitters,i);
break;
}
2022-12-28 16:50:54 -06:00
}
2024-01-01 17:30:42 -06:00
void start_emitter(emitter *e) { e->on = 1; }
void stop_emitter(emitter *e) { e->on = 0; }
2022-12-28 16:50:54 -06:00
2024-01-04 09:46:28 -06:00
/* Variate a value around variance. Variance between 0 and 1. */
float variate(float val, float variance)
{
return val + val*(frand(variance)-(variance/2));
}
2023-12-29 19:08:53 -06:00
int emitter_spawn(emitter *e)
{
2024-01-01 17:30:42 -06:00
particle p;
p.life = e->life;
2024-01-04 09:46:28 -06:00
p.pos = (HMM_Vec4){e->t.pos.x,e->t.pos.y,0,0};
float newan = e->t.rotation.Elements[0]+(2*HMM_PI*(frand(e->divergence)-(e->divergence/2)));
HMM_Vec2 norm = HMM_V2Rotate((HMM_Vec2){0,1}, newan);
p.v = HMM_MulV4F((HMM_Vec4){norm.x,norm.y,0,0}, variate(e->speed, e->variation));
2024-01-01 17:30:42 -06:00
p.angle = 0;
2024-01-04 09:46:28 -06:00
p.scale = variate(e->scale, e->scale_var);
// p.av = 1;
2024-01-01 17:30:42 -06:00
arrput(e->particles,p);
return 1;
2023-12-29 19:08:53 -06:00
}
void emitter_emit(emitter *e, int count)
{
for (int i = 0; i < count; i++)
2024-01-01 17:30:42 -06:00
emitter_spawn(e);
2023-12-29 19:08:53 -06:00
}
void emitters_step(double dt)
{
for (int i = 0; i < arrlen(emitters); i++)
emitter_step(emitters[i], dt);
}
2024-01-02 07:55:22 -06:00
static struct par_vert pv[MAX_PARTICLES];
2024-01-03 12:29:27 -06:00
void parallel_pv(emitter *e, struct scheduler *sched, struct sched_task_partition t, sched_uint thread_num)
{
for (int i=t.start; i < t.end; i++) {
2024-01-04 09:46:28 -06:00
if (e->particles[i].time >= e->particles[i].life) continue;
2024-01-03 12:29:27 -06:00
particle *p = &e->particles[i];
pv[i].pos = p->pos.xy;
pv[i].angle = p->angle;
2024-01-04 09:46:28 -06:00
float s = p->scale;
if (p->time < e->grow_for)
s = lerp(p->time/e->grow_for, 0, p->scale);
else if (p->time > (p->life - e->shrink_for))
s = lerp((p->time-(p->life-e->shrink_for))/e->shrink_for, p->scale, 0);
pv[i].scale = HMM_ScaleV2(tex_get_dimensions(e->texture), s);
2024-01-03 12:29:27 -06:00
pv[i].color = vec2rgba(p->color);
}
}
2023-12-29 19:08:53 -06:00
void emitters_draw()
{
2024-01-03 12:29:27 -06:00
if (arrlen(emitters) == 0) return;
2024-01-02 07:55:22 -06:00
int draw_count = 0;
2023-12-29 19:08:53 -06:00
for (int i = 0; i < arrlen(emitters); i++) {
emitter *e = emitters[i];
par_bind.fs.images[0] = e->texture->id;
2024-01-03 08:38:17 -06:00
2024-01-03 12:29:27 -06:00
struct sched_task task;
2024-01-03 14:26:42 -06:00
scheduler_add(&sched, &task, parallel_pv, e, arrlen(e->particles), arrlen(e->particles)/sched.threads_num);
2024-01-03 12:29:27 -06:00
scheduler_join(&sched, &task);
2024-01-03 08:38:17 -06:00
2024-01-02 07:55:22 -06:00
sg_append_buffer(par_bind.vertex_buffers[0], &(sg_range){.ptr=&pv, .size=sizeof(struct par_vert)*arrlen(e->particles)});
draw_count += arrlen(e->particles);
2023-12-29 19:08:53 -06:00
}
2024-01-02 07:55:22 -06:00
2023-12-29 19:08:53 -06:00
sg_apply_pipeline(par_pipe);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(projection));
sg_apply_bindings(&par_bind);
sg_draw(0, 4, draw_count);
}
2024-01-03 12:29:27 -06:00
static double dt;
static HMM_Vec4 g_accel;
2024-01-02 07:55:22 -06:00
2024-01-03 12:29:27 -06:00
void parallel_step(emitter *e, struct scheduler *shed, struct sched_task_partition t, sched_uint thread_num)
{
for (int i = t.end-1; i >=0; i--) {
2024-01-04 09:46:28 -06:00
if (e->particles[i].time >= e->particles[i].life) continue;
2024-01-04 08:14:37 -06:00
if (e->warp_mask & gravmask)
2024-01-03 12:29:27 -06:00
e->particles[i].v = HMM_AddV4(e->particles[i].v, g_accel);
2024-01-04 08:14:37 -06:00
2024-01-03 12:29:27 -06:00
e->particles[i].pos = HMM_AddV4(e->particles[i].pos, HMM_MulV4F(e->particles[i].v, dt));
e->particles[i].angle += e->particles[i].av*dt;
2024-01-04 09:46:28 -06:00
e->particles[i].time += dt;
e->particles[i].color = sample_sampler(&e->color, e->particles[i].time/e->particles[i].life);
2024-01-03 12:29:27 -06:00
e->particles[i].scale = e->scale;
2024-01-04 09:46:28 -06:00
if (e->particles[i].time >= e->particles[i].life)
arrdelswap(e->particles, i);
else if (query_point(e->particles[i].pos.xy))
arrdelswap(e->particles,i);
2023-12-29 19:08:53 -06:00
}
2024-01-03 12:29:27 -06:00
}
void emitter_step(emitter *e, double mdt) {
dt = mdt;
g_accel = HMM_MulV4F((HMM_Vec4){cpSpaceGetGravity(space).x, cpSpaceGetGravity(space).y, 0, 0}, dt);
if (arrlen(e->particles) == 0) return;
struct sched_task task;
2024-01-03 14:26:42 -06:00
scheduler_add(&sched, &task, parallel_step, e, arrlen(e->particles), arrlen(e->particles)/sched.threads_num);
2024-01-03 12:29:27 -06:00
scheduler_join(&sched, &task);
2022-12-28 16:50:54 -06:00
2024-01-01 17:30:42 -06:00
if (!e->on) return;
2023-12-29 19:08:53 -06:00
e->tte-=dt;
if (e->tte <= 0) {
2024-01-01 17:30:42 -06:00
emitter_spawn(e);
2024-01-02 07:55:22 -06:00
e->tte = lerp(e->explosiveness, e->life/e->max,0);
2023-05-12 13:22:05 -05:00
}
}