From: hgn Date: Sun, 26 Nov 2023 12:24:03 +0000 (+0000) Subject: particle systems X-Git-Url: https://harrygodden.com/git/?a=commitdiff_plain;h=60b814f700fb47b6730d603dd85a5affad67272e;p=carveJwlIkooP6JGAAIwe30JlM.git particle systems --- diff --git a/build.c b/build.c index 596c698..e769465 100644 --- a/build.c +++ b/build.c @@ -269,7 +269,8 @@ void build_shaders(void){ _S( "model_font", "model_font.vs", "model_font.fs" ); /* Pointcloud */ - _S( "point_map", "cloud.vs", "cloud.fs" ); + //_S( "point_map", "cloud.vs", "cloud.fs" ); + _S( "particle", "particle.vs", "particle.fs" ); /* 2D */ _S( "blit", "blit.vs", "blit.fs" ); diff --git a/particle.c b/particle.c new file mode 100644 index 0000000..115c1fe --- /dev/null +++ b/particle.c @@ -0,0 +1,152 @@ +#include "particle.h" + +static void particle_spawn( particle_system *sys, + v3f co, v3f v, f32 lifetime, u32 colour ){ + if( sys->alive == sys->max ) return; + + particle *p = &sys->array[ sys->alive ++ ]; + v3_copy( co, p->co ); + v3_copy( v, p->v ); + p->life = lifetime; + p->colour = colour; +} + +static void particle_system_update( particle_system *sys, f32 dt ){ + u32 i = 0; +iter: if( i == sys->alive ) return; + + particle *p = &sys->array[i]; + p->life -= dt; + + if( p->life < 0.0f ){ + *p = sys->array[ -- sys->alive ]; + goto iter; + } + + v3_muladds( p->co, p->v, dt, p->co ); + p->v[1] += -9.8f * dt; + + i ++; + goto iter; +} + +static void particle_system_debug( particle_system *sys ){ + for( u32 i=0; ialive; i ++ ){ + particle *p = &sys->array[i]; + v3f p1; + v3_muladds( p->co, p->v, 0.2f, p1 ); + vg_line( p->co, p1, p->colour ); + } +} + +struct particle_init_args { + particle_system *sys; + u16 indices[]; +}; + +static void async_particle_init( void *payload, u32 size ){ + struct particle_init_args *args = payload; + particle_system *sys = args->sys; + + glGenVertexArrays( 1, &sys->vao ); + glGenBuffers( 1, &sys->vbo ); + glGenBuffers( 1, &sys->ebo ); + glBindVertexArray( sys->vao ); + + size_t stride = sizeof(particle_vert); + + glBindBuffer( GL_ARRAY_BUFFER, sys->vbo ); + glBufferData( GL_ARRAY_BUFFER, sys->max*stride*4, NULL, GL_DYNAMIC_DRAW ); + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, sys->ebo ); + glBufferData( GL_ELEMENT_ARRAY_BUFFER, + sys->max*sizeof(u16)*6, args->indices, GL_STATIC_DRAW ); + + /* 0: coordinates */ + glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0 ); + glEnableVertexAttribArray( 0 ); + + /* 3: colour */ + glVertexAttribPointer( 1, 4, GL_UNSIGNED_BYTE, GL_TRUE, + stride, (void *)offsetof(particle_vert, colour) ); + glEnableVertexAttribArray( 1 ); + + VG_CHECK_GL_ERR(); +} + +static void particle_init( particle_system *sys, u32 max ){ + static int reg = 1; + if( reg ){ + shader_particle_register(); + reg = 0; + } + + size_t stride = sizeof(particle_vert); + + particles_grind.max = max; + particles_grind.array = + vg_linear_alloc( vg_mem.rtmemory, max*sizeof(particle) ); + particles_grind.vertices = + vg_linear_alloc( vg_mem.rtmemory, max*stride*4 ); + + vg_async_item *call = + vg_async_alloc( sizeof(particle_system *) + max*sizeof(u16)*6 ); + struct particle_init_args *init = call->payload; + init->sys = sys; + + for( u32 i=0; iindices[i*6+0] = i*4; + init->indices[i*6+1] = i*4+1; + init->indices[i*6+2] = i*4+2; + init->indices[i*6+3] = i*4; + init->indices[i*6+4] = i*4+2; + init->indices[i*6+5] = i*4+3; + } + + vg_async_dispatch( call, async_particle_init ); +} + +static void particle_system_prerender( particle_system *sys ){ + for( u32 i=0; ialive; i ++ ){ + particle *p = &sys->array[i]; + particle_vert *vs = &sys->vertices[i*4]; + + v3f v, right; + v3_copy( p->v, v ); + v3_normalize( v ); + v3_cross( v, (v3f){0,1,0}, right ); + + f32 l = 0.3f, w = 0.025f; + + v3f p0, p1; + v3_muladds( p->co, p->v, l, p0 ); + v3_muladds( p->co, p->v, -l, p1 ); + + v3_muladds( p0, right, w, vs[0].co ); + v3_muladds( p1, right, w, vs[1].co ); + v3_muladds( p1, right, -w, vs[2].co ); + v3_muladds( p0, right, -w, vs[3].co ); + + vs[0].colour = p->colour; + vs[1].colour = p->colour; + vs[2].colour = p->colour; + vs[3].colour = p->colour; + } + + glBindVertexArray( sys->vao ); + + size_t stride = sizeof(particle_vert); + glBindBuffer( GL_ARRAY_BUFFER, sys->vbo ); + glBufferSubData( GL_ARRAY_BUFFER, 0, sys->alive*stride*4, sys->vertices ); +} + +static void particle_system_render( particle_system *sys, camera *cam ){ + glDisable( GL_CULL_FACE ); + glDisable( GL_DEPTH_TEST ); + + shader_particle_use(); + shader_particle_uPv( cam->mtx.pv ); + shader_particle_uPvPrev( cam->mtx_prev.pv ); + + glBindVertexArray( sys->vao ); + glDrawElements( GL_TRIANGLES, sys->alive*6, GL_UNSIGNED_SHORT, NULL ); +} diff --git a/particle.h b/particle.h new file mode 100644 index 0000000..e4080be --- /dev/null +++ b/particle.h @@ -0,0 +1,35 @@ +#ifndef PARTICLE_H +#define PARTICLE_H + +typedef struct particle_system particle_system; +typedef struct particle particle; +typedef struct particle_vert particle_vert; + +struct particle_system { + struct particle { + v3f co, v; + f32 life; + u32 colour; + } + *array; + +#pragma pack(push,1) + struct particle_vert { + v3f co; + u32 colour; + } + *vertices; +#pragma pack(pop) + + u32 alive, max; + GLuint vao, vbo, ebo; +} +static particles_grind; + +static void particle_spawn( particle_system *sys, + v3f co, v3f v, f32 lifetime, u32 colour ); +static void particle_init( particle_system *sys, u32 max ); + +#include "shaders/particle.h" + +#endif /* PARTICLE_H */ diff --git a/shaders/particle.fs b/shaders/particle.fs new file mode 100644 index 0000000..f45b4ef --- /dev/null +++ b/shaders/particle.fs @@ -0,0 +1,17 @@ +layout (location = 0) out vec4 oColour; +in vec4 aColour; + +#include "motion_vectors_fs.glsl" + +void main(){ + compute_motion_vectors(); + + //vec2 ssuv = gl_FragCoord.xy; + //vec3 vDither = vec3( dot( vec2( 171.0, 231.0 ), ssuv) ); + //float dither = fract( vDither.g / 71.0 ) - 0.5; + + //if( vsamplemain.a+dither<0.5 ) + // discard; + + oColour = aColour; +} diff --git a/shaders/particle.h b/shaders/particle.h new file mode 100644 index 0000000..60bc981 --- /dev/null +++ b/shaders/particle.h @@ -0,0 +1,108 @@ +#ifndef SHADER_particle_H +#define SHADER_particle_H +static void shader_particle_link(void); +static void shader_particle_register(void); +static struct vg_shader _shader_particle = { + .name = "particle", + .link = shader_particle_link, + .vs = +{ +.orig_file = "shaders/particle.vs", +.static_src = +"layout (location=0) in vec3 a_co;\n" +"layout (location=1) in vec4 a_colour;\n" +"\n" +"#line 1 1 \n" +"const float k_motion_lerp_amount = 0.01;\n" +"\n" +"#line 2 0 \n" +"\n" +"out vec3 aMotionVec0;\n" +"out vec3 aMotionVec1;\n" +"\n" +"void vs_motion_out( vec4 vproj0, vec4 vproj1 )\n" +"{\n" +" // This magically solves some artifacting errors!\n" +" //\n" +" vproj1 = vproj0*(1.0-k_motion_lerp_amount) + vproj1*k_motion_lerp_amount;\n" +"\n" +" aMotionVec0 = vec3( vproj0.xy, vproj0.w );\n" +" aMotionVec1 = vec3( vproj1.xy, vproj1.w );\n" +"}\n" +"\n" +"#line 5 0 \n" +"\n" +"uniform mat4 uPv;\n" +"uniform mat4 uPvPrev;\n" +"\n" +"out vec4 aColour;\n" +"\n" +"void main(){\n" +" vec4 vproj0 = uPv * vec4( a_co, 1.0 );\n" +" vec4 vproj1 = uPvPrev * vec4( a_co, 1.0 );\n" +" vs_motion_out( vproj0, vproj1 );\n" +"\n" +" gl_Position = vproj0;\n" +" aColour = a_colour;\n" +"}\n" +""}, + .fs = +{ +.orig_file = "shaders/particle.fs", +.static_src = +"layout (location = 0) out vec4 oColour;\n" +"in vec4 aColour;\n" +"\n" +"#line 1 1 \n" +"const float k_motion_lerp_amount = 0.01;\n" +"\n" +"#line 2 0 \n" +"\n" +"layout (location = 1) out vec2 oMotionVec;\n" +"\n" +"in vec3 aMotionVec0;\n" +"in vec3 aMotionVec1;\n" +"\n" +"void compute_motion_vectors()\n" +"{\n" +" // Write motion vectors\n" +" vec2 vmotion0 = aMotionVec0.xy / aMotionVec0.z;\n" +" vec2 vmotion1 = aMotionVec1.xy / aMotionVec1.z;\n" +"\n" +" oMotionVec = (vmotion1-vmotion0) * (1.0/k_motion_lerp_amount);\n" +"}\n" +"\n" +"#line 5 0 \n" +"\n" +"void main(){\n" +" compute_motion_vectors();\n" +"\n" +" //vec2 ssuv = gl_FragCoord.xy;\n" +" //vec3 vDither = vec3( dot( vec2( 171.0, 231.0 ), ssuv) );\n" +" //float dither = fract( vDither.g / 71.0 ) - 0.5;\n" +"\n" +" //if( vsamplemain.a+dither<0.5 )\n" +" // discard;\n" +"\n" +" oColour = aColour;\n" +"}\n" +""}, +}; + +static GLuint _uniform_particle_uPv; +static GLuint _uniform_particle_uPvPrev; +static void shader_particle_uPv(m4x4f m){ + glUniformMatrix4fv(_uniform_particle_uPv,1,GL_FALSE,(float*)m); +} +static void shader_particle_uPvPrev(m4x4f m){ + glUniformMatrix4fv(_uniform_particle_uPvPrev,1,GL_FALSE,(float*)m); +} +static void shader_particle_register(void){ + vg_shader_register( &_shader_particle ); +} +static void shader_particle_use(void){ glUseProgram(_shader_particle.id); } +static void shader_particle_link(void){ + _uniform_particle_uPv = glGetUniformLocation( _shader_particle.id, "uPv" ); + _uniform_particle_uPvPrev = glGetUniformLocation( _shader_particle.id, "uPvPrev" ); +} +#endif /* SHADER_particle_H */ diff --git a/shaders/particle.vs b/shaders/particle.vs new file mode 100644 index 0000000..7ecbea5 --- /dev/null +++ b/shaders/particle.vs @@ -0,0 +1,18 @@ +layout (location=0) in vec3 a_co; +layout (location=1) in vec4 a_colour; + +#include "motion_vectors_vs.glsl" + +uniform mat4 uPv; +uniform mat4 uPvPrev; + +out vec4 aColour; + +void main(){ + vec4 vproj0 = uPv * vec4( a_co, 1.0 ); + vec4 vproj1 = uPvPrev * vec4( a_co, 1.0 ); + vs_motion_out( vproj0, vproj1 ); + + gl_Position = vproj0; + aColour = a_colour; +} diff --git a/skaterift.c b/skaterift.c index d8aae1b..14529f6 100644 --- a/skaterift.c +++ b/skaterift.c @@ -56,6 +56,7 @@ #include "player_remote.c" #include "vg/vg_audio_dsp.h" #include "world_routes_ui.c" +#include "particle.c" static int k_tools_mode = 0; @@ -194,6 +195,8 @@ static void vg_load(void){ skaterift.replay.size = bytes; replay_clear( &skaterift.replay ); + particle_init( &particles_grind, 300 ); + player_load_animation_reference( "models/ch_none.mdl" ); player_model_load( &localplayer.fallback_model, "models/ch_none.mdl" ); player__bind(); @@ -476,6 +479,15 @@ static void render_scene(void){ world_instance *view_world = get_view_world(); render_world( view_world, &skaterift.cam, 0, 0, 1, 1 ); + particle_spawn( &particles_grind, localplayer.rb.co, + (v3f){vg_randf64()*2.0f,vg_randf64()*3.0f,vg_randf64()*2.0f}, + vg_randf64(), 0xff0000ff ); + particle_system_update( &particles_grind, vg.time_delta ); + //particle_system_debug( &particles_grind ); + particle_system_prerender( &particles_grind ); + particle_system_render( &particles_grind, &skaterift.cam ); + + /* * render transition */