particle systems
authorhgn <hgodden00@gmail.com>
Sun, 26 Nov 2023 12:24:03 +0000 (12:24 +0000)
committerhgn <hgodden00@gmail.com>
Sun, 26 Nov 2023 12:24:03 +0000 (12:24 +0000)
build.c
particle.c [new file with mode: 0644]
particle.h [new file with mode: 0644]
shaders/particle.fs [new file with mode: 0644]
shaders/particle.h [new file with mode: 0644]
shaders/particle.vs [new file with mode: 0644]
skaterift.c

diff --git a/build.c b/build.c
index 596c698b4f2781db29764885e27e67adfe74fc5b..e769465c428149e70f9ec090e58f7f96eb8b78a7 100644 (file)
--- 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 (file)
index 0000000..115c1fe
--- /dev/null
@@ -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; i<sys->alive; 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; i<max; i ++ ){
+      init->indices[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; i<sys->alive; 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 (file)
index 0000000..e4080be
--- /dev/null
@@ -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 (file)
index 0000000..f45b4ef
--- /dev/null
@@ -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 (file)
index 0000000..60bc981
--- /dev/null
@@ -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 (file)
index 0000000..7ecbea5
--- /dev/null
@@ -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;
+}
index d8aae1bde47d3b99950d19b32a394b98ffb5fc77..14529f67e0e3ea20bd447cad1c72124993e95304 100644 (file)
@@ -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 
     */