particles, sprite animation

This commit is contained in:
2023-12-30 01:08:53 +00:00
parent 5bdf311da9
commit 6aa44042d7
13 changed files with 350 additions and 151 deletions

View File

@@ -1382,6 +1382,10 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
ret = ptr2js(cpGearJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2number(argv[3]), js2number(argv[4])));
cpSpaceAddConstraint(space,js2ptr(ret));
break;
case 224:
str = js2str(argv[1]);
ret = ints2js(gif_delays(str));
break;
}
if (str)

View File

@@ -1,36 +1,184 @@
#include "particle.h"
#include "stb_ds.h"
#include "freelist.h"
#include "render.h"
#include "particle.sglsl.h"
#include "log.h"
struct emitter make_emitter() {
struct emitter e = {0};
arrsetcap(e.particles, 200);
static emitter **emitters;
static sg_shader par_shader;
static sg_pipeline par_pipe;
static sg_bindings par_bind;
static int draw_count;
struct par_vert {
HMM_Vec2 pos;
float angle;
float scale;
struct rgba color;
};
typedef struct par_vert par_vert;
void particle_init()
{
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,
[3].format = SG_VERTEXFORMAT_FLOAT,
[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,
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.pixel_format = SG_PIXELFORMAT_DEPTH
}
});
par_bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(par_vert)*500,
.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() {
emitter *e = NULL;
e = malloc(sizeof(*e));
e->max = 20;
arrsetlen(e->particles, e->max);
for (int i = 0; i < arrlen(e->particles); i++)
e->particles[i].life = 0;
e->life = 10;
e->explosiveness = 0;
e->tte = e->life/e->max;
e->color = color_white;
e->scale = 20;
arrpush(emitters,e);
return e;
}
void free_emitter(struct emitter e) {
arrfree(e.particles);
void free_emitter(emitter *e)
{
arrfree(e->particles);
for (int i = arrlen(emitters)-1; i >= 0; i--)
if (emitters[i] == e) {
arrdelswap(emitters,i);
break;
}
}
void start_emitter(struct emitter e) {
void start_emitter(emitter *e)
{
}
void pause_emitter(struct emitter e) {
void pause_emitter(emitter *e) {
}
void stop_emitter(struct emitter e) {
void stop_emitter(emitter *e) {
}
void emitter_step(struct emitter e, double dt) {
for (int i = 0; i < arrlen(e.particles); i++) {
e.particles[i].pos = HMM_AddV3(e.particles[i].pos, HMM_MulV3F(e.particles[i].v, dt));
e.particles[i].angle = HMM_MulQ(e.particles[i].angle, HMM_MulQF(e.particles[i].angle, dt));
e.particles[i].life -= dt;
int emitter_spawn(emitter *e)
{
for (int i = 0; i < e->max; i++) {
if (e->particles[i].life > 0) continue;
e->particles[i].life = e->life;
e->particles[i].pos = (HMM_Vec3){0,0,0};
e->particles[i].v = (HMM_Vec3){20,1,0};
e->particles[i].angle = 0;
e->particles[i].av = 1;
return 1;
}
return 0;
}
if (e.particles[i].life <= 0)
arrdelswap(e.particles, i);
void emitter_emit(emitter *e, int count)
{
for (int i = 0; i < count; i++)
if (!emitter_spawn(e)) return;
}
void emitters_step(double dt)
{
for (int i = 0; i < arrlen(emitters); i++)
emitter_step(emitters[i], dt);
}
void emitters_draw()
{
draw_count = 0;
for (int i = 0; i < arrlen(emitters); i++) {
emitter *e = emitters[i];
par_bind.fs.images[0] = e->texture->id;
for (int j = 0; j < arrlen(e->particles); j++) {
particle *p = &e->particles[j];
if (p->life <= 0) continue;
struct par_vert pv;
pv.pos = p->pos.xy;
pv.angle = p->angle;
pv.scale = p->scale;
pv.color = p->color;
sg_append_buffer(par_bind.vertex_buffers[0], &(sg_range){.ptr=&pv, .size=sizeof(struct par_vert)});
draw_count++;
}
}
if (draw_count == 0) return;
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);
}
void emitter_step(emitter *e, double dt) {
for (int i = 0; i < arrlen(e->particles); i++) {
if (e->particles[i].life <= 0) continue;
e->particles[i].pos = HMM_AddV3(e->particles[i].pos, HMM_MulV3F(e->particles[i].v, dt));
e->particles[i].angle += e->particles[i].av*dt;
e->particles[i].life -= dt;
e->particles[i].color = e->color;
e->particles[i].scale = e->scale;
if (e->particles[i].life <= 0)
e->particles[i].life = 0;
}
e->tte-=dt;
if (e->tte <= 0) {
emitter_emit(e,1);
e->tte = e->life/e->max;
}
}

View File

@@ -3,34 +3,42 @@
#include "HandmadeMath.h"
#include "transform.h"
#include "texture.h"
struct particle {
typedef struct particle {
HMM_Vec3 pos;
HMM_Vec3 v; /* velocity */
HMM_Quat angle;
HMM_Quat av; /* angular velocity */
union {
double life;
unsigned int next;
};
};
float angle;
float av; /* angular velocity */
float scale;
double life;
rgba color;
} particle;
typedef struct emitter {
struct particle *particles;
transform3d t;
int max;
double life;
void (*seeder)(struct particle *p); /* Called to initialize each particle */
float explosiveness; /* 0 for a stream, 1 for all at once. Range of values allowed. */
int max; /* number of particles */
double life; /* how long a particle lasts */
double tte; /* time to emit */
rgba color;
float scale;
texture *texture;
} emitter;
struct emitter make_emitter();
void free_emitter(struct emitter e);
void particle_init();
void start_emitter(struct emitter e);
void pause_emitter(struct emitter e);
void stop_emitter(struct emitter e);
emitter *make_emitter();
void free_emitter(emitter *e);
void emitter_step(struct emitter e, double dt);
void start_emitter(emitter *e);
void pause_emitter(emitter *e);
void stop_emitter(emitter *e);
void emitter_emit(emitter *e, int count);
void emitters_step(double dt);
void emitters_draw();
void emitter_step(emitter *e, double dt);
#endif

View File

@@ -7,6 +7,7 @@
#include "gameobject.h"
#include "log.h"
#include "sprite.h"
#include "particle.h"
#include "window.h"
#include "model.h"
#include "stb_ds.h"
@@ -512,6 +513,8 @@ void full_2d_pass(struct window *window)
model_draw_all();
call_draw();
emitters_draw();
//// DEBUG
if (debugDrawPhysics) {
gameobject_draw_debugs();

View File

@@ -91,6 +91,8 @@ struct rgba {
unsigned char a;
};
typedef struct rgba rgba;
struct boundingbox {
float t;
float b;

View File

@@ -277,7 +277,6 @@ void slice9_draw(const char *img, HMM_Vec2 pos, HMM_Vec2 dimensions, struct rgba
sg_draw(sprite_count * 4, 4, 1);
sprite_count++;
}
void sprite_setframe(struct sprite *sprite, struct glrect *frame) {

View File

@@ -76,6 +76,12 @@ int gif_nframes(const char *path)
return t->frames;
}
int *gif_delays(const char *path)
{
struct Texture *t = texture_pullfromfile(path);
return t->delays;
}
/* If an empty string or null is put for path, loads default texture */
struct Texture *texture_pullfromfile(const char *path) {
if (!path) return texture_notex();
@@ -110,7 +116,13 @@ struct Texture *texture_pullfromfile(const char *path) {
tex->height = qoi.height;
n = qoi.channels;
} else if (!strcmp(ext, ".gif")) {
data = stbi_load_gif_from_memory(raw, rawlen, NULL, &tex->width, &tex->height, &tex->frames, &n, 4);
data = stbi_load_gif_from_memory(raw, rawlen, &tex->delays, &tex->width, &tex->height, &tex->frames, &n, 4);
int *dd = tex->delays;
tex->delays = NULL;
arrsetlen(tex->delays, tex->frames);
for (int i = 0; i < tex->frames;i++)
tex->delays[i] = dd[i];
free(dd);
tex->height *= tex->frames;
} else if (!strcmp(ext, ".svg")) {
#ifndef NSVG

View File

@@ -30,9 +30,12 @@ struct Texture {
int height;
unsigned char *data;
struct TextureOptions opts;
int frames;
int frames;
int *delays;
};
typedef struct Texture texture;
struct Image {
struct Texture *tex;
struct glrect frame;
@@ -47,6 +50,7 @@ void texture_sync(const char *path);
char * tex_get_path(struct Texture *tex); // Get image path for texture
int gif_nframes(const char *path);
int *gif_delays(const char *path);
struct glrect tex_get_rect(struct Texture *tex);
HMM_Vec2 tex_get_dimensions(struct Texture *tex);

View File

@@ -9,6 +9,7 @@
#include "resources.h"
#include "spline.h"
#include <stdio.h>
#include "particle.h"
#include "datastream.h"
@@ -101,6 +102,11 @@ void c_init() {
window_set_icon("icons/moon.gif");
window_resize(sapp_width(), sapp_height());
script_evalf("Game.init();");
particle_init();
// emitter *e = make_emitter();
// e->texture = texture_pullfromfile("bolt.gif");
// start_emitter(e);
}
int frame_fps() { return 1.0/sapp_frame_duration(); }
@@ -114,10 +120,7 @@ static void process_frame()
/* Timers all update every frame - once per monitor refresh */
timer_update(elapsed, timescale);
/* Update at a high level::
* Update scene graph
*
*/
emitters_step(elapsed);
if (sim_play == SIM_PLAY || sim_play == SIM_STEP) {
if (stm_sec(stm_diff(frame_t, updatelast)) > updateMS) {

View File

@@ -0,0 +1,19 @@
@vs vs
in vec2 pos;
uniform vs_p { mat4 proj; };
void main()
{
gl_Position = proj * vec4(pos,0.0,1.0);
}
@end
@fs fs
void main()
{
}
@end
@program particle vs fs

View File

@@ -0,0 +1,45 @@
@vs vs
in vec2 vertex;
in vec2 apos;
in float angle;
in float scale;
in vec4 vc;
out vec4 fcolor;
out vec2 uv;
uniform vs_p { mat4 proj; };
void main()
{
fcolor = vc;
uv = vertex;
vec2 v = vertex - 0.5;
vec2 p = vec2(
cos(angle)*v.x-sin(angle)*v.y,
sin(angle)*v.x+cos(angle)*v.y
);
p += 0.5;
p *= scale;
p += apos;
gl_Position = proj * vec4(p, 0.0, 1.0);
}
@end
@fs fs
in vec4 fcolor;
out vec4 color;
in vec2 uv;
uniform texture2D image;
uniform sampler smp;
void main()
{
color = texture(sampler2D(image,smp),uv);
color *= fcolor;
}
@end
@program particle vs fs