X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=fishladder.c;h=4e41b5ad924ba80ba678ed55eb7fd3ec6162cde8;hb=1a837016d9060eb0c2ed05a93b9d2a69d0005808;hp=c928a816437a9342f5246cf7b1eb254aa8424180;hpb=4f304e68ef414a6daf4cc4a8cc9bb9d79aa892e0;p=fishladder.git diff --git a/fishladder.c b/fishladder.c index c928a81..4e41b5a 100644 --- a/fishladder.c +++ b/fishladder.c @@ -27,6 +27,157 @@ SHADER_DEFINE( shader_tile_colour, UNIFORMS({ "uPv", "uOffset", "uColour" }) ) +SHADER_DEFINE( shader_ball, + // VERTEX + "layout (location=0) in vec2 a_co;" + "uniform vec2 uOffset;" + "uniform mat3 uPv;" + "" + "out vec2 aTexCoords;" + "" + "void main()" + "{" + // Create texture coords + "aTexCoords = a_co;" + + // Vertex transform + "vec3 worldpos = vec3( a_co * 0.25 - 0.125 + uOffset, 1.0 );" + "gl_Position = vec4( uPv * worldpos, 1.0 );" + "}", + + // FRAGMENT + "out vec4 FragColor;" + "" + "uniform sampler2D uTexMain;" + "uniform vec3 uColour;" + "" + "in vec2 aTexCoords;" + "" + "void main()" + "{" + "vec4 glyph = texture( uTexMain, aTexCoords );" + "FragColor = vec4( glyph.rgb * uColour, glyph.a );" + "}" + , + UNIFORMS({ "uTexMain", "uColour", "uOffset", "uPv" }) +) + +SHADER_DEFINE( shader_tile_main, + // VERTEX + "layout (location=0) in vec2 a_co;" + "uniform vec4 uOffset;" // Tile x/y, uv x/y + "uniform mat3 uPv;" + "uniform mat2 uSubTransform;" + "" + "out vec4 aTexCoords;" + "" + "vec2 hash22(vec2 p)" + "{" + "vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973));" + "p3 += dot(p3, p3.yzx+33.33);" + "return fract((p3.xx+p3.yz)*p3.zy);" + "}" + "" + "void main()" + "{" + // Create texture coords + "vec2 random_offset = floor(hash22(uOffset.xy) * 4.0) * 0.25;" + "vec2 edge_safe_coords = a_co * 0.98 + 0.01;" + "aTexCoords = vec4((edge_safe_coords + uOffset.zw) * 0.25, edge_safe_coords * 0.25 + random_offset );" + + // Vertex transform + "vec2 subtransform = uSubTransform * (a_co-0.5) + 0.5;" + "vec3 worldpos = vec3( subtransform + uOffset.xy, 1.0 );" + "gl_Position = vec4( uPv * worldpos, 1.0 );" + "}", + + // FRAGMENT + "out vec4 FragColor;" + "" + "uniform sampler2D uTexGlyphs;" + "uniform sampler2D uTexWood;" + "" + "in vec4 aTexCoords;" + "" + "void main()" + "{" + "vec3 shadowing_colour = vec3( 0.93, 0.88536, 0.8184 );" + "vec4 glyph = texture( uTexGlyphs, aTexCoords.xy );" + "vec4 wood = texture( uTexWood, aTexCoords.zw );" + "vec4 wood_secondary = texture( uTexWood, aTexCoords.zw + 0.25 );" + "vec3 wood_comp = mix( wood_secondary.rgb * shadowing_colour, wood.rgb, clamp( glyph.b * 2.0 - 1.0, 0.0, 1.0 ) );" + + "vec3 shadows = mix( vec3( 0.85, 0.7344, 0.561 ), vec3(1.0,1.0,1.0), glyph.r );" + + "FragColor = vec4( wood_comp * shadows, glyph.b );" + "}" + , + UNIFORMS({ "uPv", "uOffset", "uTexGlyphs", "uTexWood", "uSubTransform" }) +) + +const char *level_pack[] = +{ + // Level 0 + "#########;\n" + "###-#####;acac\n" + "## ##;\n" + "## ##;\n" + "## ##;\n" + "## ##;\n" + "#####+###;acac\n" + "#########;\n", + + // Level 1 + "#########;\n" + "##-###-##;b,b\n" + "## ##;\n" + "## ##;\n" + "## ##;\n" + "## ##;\n" + "## ##;\n" + "####+####;bb\n" + "#########;\n", + + // Level 2 + "###########;\n" + "#####-#####;bbbbb\n" + "## ##;\n" + "## ###;\n" + "## # ##;\n" + "## ##;\n" + "###+##+####;bbb,bb\n" + "###########;\n", + + // Level 3 + "#############;\n" + "###-#####-###;a,aaa\n" + "## ##;\n" + "## ##;\n" + "## ##;\n" + "## ##;\n" + "## ##;\n" + "## ##;\n" + "######+######;aaaa\n" + "#############;\n", + + // Level 4 + "#############;\n" + "###-#####-###;aaa,aa\n" + "## ##;\n" + "## ##;\n" + "## ##;\n" + "## ##;\n" + "## ##;\n" + "## ##;\n" + "###+#####+###;aa,aaa\n" + "#############;\n" +}; + +GLuint tex_tile_data; +GLuint tex_tile_detail; +GLuint tex_wood; +GLuint tex_ball; + m3x3f m_projection; m3x3f m_view; m3x3f m_mdl; @@ -40,6 +191,32 @@ m3x3f m_mdl; #define FLAG_MERGER 0x40 #define FLAG_DROP_R 0x80 #define FLAG_FLIP_FLOP 0x100 +#define FLAG_FLIP_ROTATING 0x200 + +/* + 0000 0 | 0001 1 | 0010 2 | 0011 3 + | | | | | + X | X= | X | X= + | | | + 0100 4 | 0101 5 | 0110 6 | 0111 7 + | | | | | + =X | =X= | =X | =X= + | | | + 1000 8 | 1001 9 | 1010 10 | 1011 11 + | | | | | + X | X= | X | X= + | | | | | | | + 1100 12 | 1101 13 | 1110 14 | 1111 15 + | | | | | + =X | =X= | =X | =X= + | | | | | | | +*/ + +enum cell_type +{ + k_cell_type_split = 7, + k_cell_type_merge = 13 +}; v3f colour_sets[] = { { 0.9f, 0.2f, 0.01f }, @@ -62,6 +239,44 @@ static void colour_code_v3( char cc, v3f target ) v3_copy( (v3f){0.0f,0.0f,0.0f}, target ); } +struct mesh +{ + GLuint vao, vbo; + u32 elements; +}; + +static void init_mesh( struct mesh *m, float *tris, u32 length ) +{ + m->elements = length/3; + glGenVertexArrays( 1, &m->vao ); + glGenBuffers( 1, &m->vbo ); + + glBindVertexArray( m->vao ); + glBindBuffer( GL_ARRAY_BUFFER, m->vbo ); + glBufferData( GL_ARRAY_BUFFER, length*sizeof(float), tris, GL_STATIC_DRAW ); + + glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0 ); + glEnableVertexAttribArray( 0 ); + + VG_CHECK_GL(); +} + +static void free_mesh( struct mesh *m ) +{ + glDeleteVertexArrays( 1, &m->vao ); + glDeleteBuffers( 1, &m->vbo ); +} + +static void draw_mesh( int const start, int const count ) +{ + glDrawArrays( GL_TRIANGLES, start*3, count*3 ); +} + +static void use_mesh( struct mesh *m ) +{ + glBindVertexArray( m->vao ); +} + struct world { struct cell @@ -89,10 +304,7 @@ struct world u32 w, h; - GLuint tile_vao; - GLuint tile_vbo; - GLuint circle_vao; - GLuint circle_vbo; + struct mesh tile, circle; int selected; @@ -249,21 +461,20 @@ static struct cell *pcell( v2i pos ) int main( int argc, char *argv[] ) { - vg_init( argc, argv, "FishLadder" ); + vg_init( argc, argv, "Fish (Marbles Computer) Ladder Simulator 2022 | N,M: change level | SPACE: Test | LeftClick: Toggle tile" ); } void vg_register(void) { SHADER_INIT( shader_tile_colour ); + SHADER_INIT( shader_tile_main ); + SHADER_INIT( shader_ball ); } void vg_start(void) { // Quad mesh { - glGenVertexArrays( 1, &world.tile_vao ); - glGenBuffers( 1, &world.tile_vbo ); - float quad_mesh[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, @@ -276,27 +487,11 @@ void vg_start(void) 0.75f, 0.375f, 0.25f, 0.5f, 0.75f, 0.625f }; - glBindVertexArray( world.tile_vao ); - glBindBuffer( GL_ARRAY_BUFFER, world.tile_vbo ); - glBufferData - ( - GL_ARRAY_BUFFER, - sizeof( quad_mesh ), - quad_mesh, - GL_STATIC_DRAW - ); - - glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0 ); - glEnableVertexAttribArray( 0 ); - - VG_CHECK_GL(); + init_mesh( &world.tile, quad_mesh, vg_list_size(quad_mesh) ); } // Circle mesh { - glGenVertexArrays( 1, &world.circle_vao ); - glGenBuffers( 1, &world.circle_vbo ); - float circle_mesh[32*6*3]; int res = vg_list_size( circle_mesh ) / (6*3); @@ -322,40 +517,46 @@ void vg_start(void) v2_copy( v1, circle_mesh+i*6+2 ); } - glBindVertexArray( world.circle_vao ); - glBindBuffer( GL_ARRAY_BUFFER, world.circle_vbo ); - glBufferData( GL_ARRAY_BUFFER, sizeof( circle_mesh ), circle_mesh, GL_STATIC_DRAW ); + init_mesh( &world.circle, circle_mesh, vg_list_size( circle_mesh ) ); + } + + // Textures + { + tex_tile_detail = vg_tex2d_rgba( "textures/tile_overlays.png" ); + vg_tex2d_mipmap(); + vg_tex2d_linear_mipmap(); + vg_tex2d_repeat(); + + tex_tile_data = vg_tex2d_rgba( "textures/tileset.png" ); + vg_tex2d_mipmap(); + vg_tex2d_linear_mipmap(); + vg_tex2d_repeat(); - glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0 ); - glEnableVertexAttribArray( 0 ); + tex_wood = vg_tex2d_rgba( "textures/wood.png" ); + vg_tex2d_mipmap(); + vg_tex2d_linear_mipmap(); + vg_tex2d_repeat(); - VG_CHECK_GL(); + tex_ball = vg_tex2d_rgba( "textures/ball_metallic.png" ); + vg_tex2d_mipmap(); + vg_tex2d_linear_mipmap(); + vg_tex2d_clamp(); } - map_load - ( - "#############;\n" - "###-#####-###;aaa,aa\n" - "## ##;\n" - "## ##;\n" - "## ##;\n" - "## ##;\n" - "## ##;\n" - "## ##;\n" - "###+#####+###;aa,aaa\n" - "#############;\n" - ); + map_load( level_pack[ 0 ] ); } void vg_free(void) { - glDeleteVertexArrays( 1, &world.tile_vao ); - glDeleteBuffers( 1, &world.tile_vbo ); - - glDeleteVertexArrays( 1, &world.circle_vao ); - glDeleteBuffers( 1, &world.circle_vbo ); - + free_mesh( &world.tile ); + free_mesh( &world.circle ); + map_free(); + + glDeleteTextures( 1, &tex_tile_data ); + glDeleteTextures( 1, &tex_tile_detail ); + glDeleteTextures( 1, &tex_wood ); + glDeleteTextures( 1, &tex_ball ); } static int cell_interactive( v2i co ) @@ -445,6 +646,27 @@ static int cell_interactive( v2i co ) void vg_update(void) { + static int curlevel = 0; + int changelvl = curlevel; + if( vg_get_button_down( "prev" ) ) { if( curlevel > 0 ) changelvl --; } + else if( vg_get_button_down( "next" ) ) { if( curlevel < vg_list_size( level_pack )-1 ) changelvl ++; } + + if( changelvl != curlevel ) + { + map_load( level_pack[ changelvl ] ); + curlevel = changelvl; + + // TEMP!!! code dupe + world.simulating = 0; + world.num_fishes = 0; + world.sim_frame = 0; + + for( int i = 0; i < arrlen( world.io ); i ++ ) + world.io[i].recv_count = 0; + + vg_info( "Stopping simulation!\n" ); + } + float ratio = (float)vg_window_y / (float)vg_window_x; float const size = 9.5f; @@ -466,7 +688,7 @@ void vg_update(void) int tile_x = floorf( tile_pos[0] ); int tile_y = floorf( tile_pos[1] ); - + // Tilemap editing if( !world.simulating ) { @@ -519,7 +741,6 @@ void vg_update(void) // Simulation stuff // ======================================================== - // Reclassify world. TODO: Move into own function for( int y = 2; y < world.h-2; y ++ ) { for( int x = 2; x < world.w-2; x ++ ) @@ -552,11 +773,13 @@ void vg_update(void) u8 config = pcell((v2i){x,y})->config; - if( config == 0x7 ) // splitter + if( config == k_cell_type_split ) // splitter { - world.data[y*world.w+x ].state |= (FLAG_SPLIT | FLAG_DROP_L | FLAG_DROP_R); + struct cell *cell = pcell((v2i){x,y}); + + cell->state |= (FLAG_SPLIT | FLAG_DROP_L | FLAG_DROP_R); } - else if( config == 0xD ) + else if( config == k_cell_type_merge ) { world.data[y*world.w+x-1].state |= FLAG_DROP_R; world.data[y*world.w+x+1].state |= FLAG_DROP_L; @@ -592,8 +815,6 @@ void vg_update(void) v2i dirs[] = {{1,0},{-1,0},{0,-1}}; for( int j = 0; j < vg_list_size(dirs); j ++ ) - { - v2i target; if( pcell( (v2i){ posx+dirs[j][0], posy+dirs[j][1] } )->state & FLAG_CANAL ) { fish->dir[0] = dirs[j][0]; @@ -601,13 +822,22 @@ void vg_update(void) can_spawn = 1; break; } - } if( !can_spawn ) world.num_fishes--; } } } + + // Update splitter deltas + for( int i = 0; i < world.h*world.w; i ++ ) + { + struct cell *cell = &world.data[i]; + if( cell->config == k_cell_type_split ) + { + cell->state &= ~FLAG_FLIP_ROTATING; + } + } for( int i = 0; i < world.num_fishes; i ++ ) { @@ -658,7 +888,7 @@ void vg_update(void) else { struct cell *cell_next = pcell( (v2i){ fish->pos[0]+fish->dir[0], fish->pos[1]+fish->dir[1] } ); - if( !(cell_next->state & FLAG_CANAL) ) + if( !(cell_next->state & (FLAG_CANAL|FLAG_OUTPUT)) ) { // Try other directions for valid, so down, left, right.. v2i dirs[] = {{1,0},{-1,0},{0,-1}}; @@ -669,7 +899,7 @@ void vg_update(void) if( (dirs[j][0] == -fish->dir[0]) && (dirs[j][1] == -fish->dir[1]) ) continue; - if( pcell( (v2i){ fish->pos[0]+dirs[j][0], fish->pos[1]+dirs[j][1] } )->state & FLAG_CANAL ) + if( pcell( (v2i){ fish->pos[0]+dirs[j][0], fish->pos[1]+dirs[j][1] } )->state & (FLAG_CANAL|FLAG_OUTPUT) ) { fish->dir[0] = dirs[j][0]; fish->dir[1] = dirs[j][1]; @@ -680,6 +910,11 @@ void vg_update(void) fish->pos[0] += fish->dir[0]; fish->pos[1] += fish->dir[1]; + + struct cell *cell_entry = pcell( fish->pos ); + + if( cell_entry->config == k_cell_type_split ) + cell_entry->state |= FLAG_FLIP_ROTATING; } } @@ -696,70 +931,203 @@ void vg_render(void) glClearColor( 0.8f, 0.8f, 0.8f, 1.0f ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - glBindVertexArray( world.tile_vao ); - SHADER_USE( shader_tile_colour ); - glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_colour, "uPv" ), 1, GL_FALSE, (float *)vg_pv ); + float scaled_time = 0.0f, frame_lerp = 0.0f; + + if( world.simulating ) + { + scaled_time = (vg_time-world.sim_start)*2.0f; + frame_lerp = scaled_time - (float)world.sim_frame; + } + + v2f const curve_3[] = {{0.5f,1.0f},{0.5f,0.625f},{0.625f,0.5f},{1.0f,0.5f}}; + v2f const curve_6[] = {{0.5f,1.0f},{0.5f,0.625f},{0.375f,0.5f},{0.0f,0.5f}}; + v2f const curve_9[] = {{1.0f,0.5f},{0.625f,0.5f},{0.5f,0.375f},{0.5f,0.0f}}; + v2f const curve_12[]= {{0.0f,0.5f},{0.375f,0.5f},{0.5f,0.375f},{0.5f,0.0f}}; + + v2f const curve_7[] = {{0.5f,0.8438f},{0.875f,0.8438f},{0.625f,0.5f},{1.0f,0.5f}}; + v2f const curve_7_1[] = {{0.5f,0.8438f},{1.0f-0.875f,0.8438f},{1.0-0.625f,0.5f},{0.0f,0.5f}}; + + float const curve_7_linear_section = 0.1562f; + + // TILE SET RENDERING + // ====================================================================== + use_mesh( &world.tile ); + SHADER_USE( shader_tile_main ); + + m2x2f subtransform; + m2x2_identity( subtransform ); + glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main, "uSubTransform" ), 1, GL_FALSE, (float *)subtransform ); + glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_main, "uPv" ), 1, GL_FALSE, (float *)vg_pv ); + + // Bind textures + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_2D, tex_tile_data ); + glUniform1i( SHADER_UNIFORM( shader_tile_main, "uTexGlyphs" ), 0 ); + + glActiveTexture( GL_TEXTURE1 ); + glBindTexture( GL_TEXTURE_2D, tex_wood ); + glUniform1i( SHADER_UNIFORM( shader_tile_main, "uTexWood" ), 1 ); - // Shadow layer - glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 0.5f, 0.5f, 0.5f, 1.0f ); for( int y = 0; y < world.h; y ++ ) + { for( int x = 0; x < world.w; x ++ ) - { + { struct cell *cell = pcell((v2i){x,y}); + int selected = world.selected == y*world.w + x; + + int tile_offsets[][2] = + { + {2, 0}, {0, 3}, {0, 2}, {2, 2}, + {1, 0}, {2, 3}, {3, 2}, {1, 3}, + {3, 1}, {0, 1}, {1, 2}, {2, 1}, + {1, 1}, {3, 3}, {2, 1}, {2, 1} + }; + + int uv[2] = { 3, 0 }; if( cell->state & FLAG_CANAL ) { - continue; + uv[0] = tile_offsets[ cell->config ][0]; + uv[1] = tile_offsets[ cell->config ][1]; } - glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)x - 0.2f, (float)y - 0.15f, 1.0f ); - glDrawArrays( GL_TRIANGLES, 0, 6 ); + glUniform4f( SHADER_UNIFORM( shader_tile_main, "uOffset" ), (float)x, (float)y, uv[0], uv[1] ); + draw_mesh( 0, 2 ); } + } + + // Draw splitters + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation(GL_FUNC_ADD); for( int y = 0; y < world.h; y ++ ) { for( int x = 0; x < world.w; x ++ ) { struct cell *cell = pcell((v2i){x,y}); - int selected = world.selected == y*world.w + x; - - if( cell->state & FLAG_CANAL && !selected ) - continue; - - glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)x, (float)y, 1.0f ); - v4f colour; + if( cell->state & FLAG_SPLIT ) + { + float rotation = cell->state & FLAG_FLIP_FLOP? vg_rad( -45.0f ): vg_rad( 45.0f ); - if( cell->state & FLAG_WALL ) { v4_copy( (v4f){ 0.2f, 0.2f, 0.2f, 1.0f }, colour ); } - else if( cell->state & FLAG_CANAL ) { v4_copy( (v4f){ 0.6f, 0.6f, 0.6f, 1.0f }, colour ); } - else if( cell->state & FLAG_INPUT ) { v4_copy( (v4f){ 0.5f, 0.5f, 0.5f, 1.0f }, colour ); } - else if( cell->state & FLAG_OUTPUT ) { v4_copy( (v4f){ 0.2f, 0.7f, 0.3f, 1.0f }, colour ); } - else v4_copy( (v4f){ 0.9f, 0.9f, 0.9f, 1.0f }, colour ); - - //if( cell->water[world.frame&0x1] ) - // v4_copy( (v4f){ 0.2f, 0.3f, 0.7f * (float)(cell->water[world.frame&0x1]) * (1.0f/16.0f), 1.0f }, colour ); + if( cell->state & FLAG_FLIP_ROTATING ) + { + if( (frame_lerp > curve_7_linear_section) ) + { + float const rotation_speed = 0.4f; + if( (frame_lerp < 1.0f-rotation_speed) ) + { + float t = frame_lerp - curve_7_linear_section; + t *= -2.0f * (1.0f/(1.0f-(curve_7_linear_section+rotation_speed))); + t += 1.0f; + + rotation *= t; + } + else + rotation *= -1.0f; + } + } + + m2x2_create_rotation( subtransform, rotation ); + + glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main, "uSubTransform" ), 1, GL_FALSE, (float *)subtransform ); + glUniform4f( SHADER_UNIFORM( shader_tile_main, "uOffset" ), (float)x, (float)y + 0.125f, 0.0f, 0.0f ); + draw_mesh( 0, 2 ); + } + } + } + + //glDisable(GL_BLEND); + + SHADER_USE( shader_ball ); + glUniformMatrix3fv( SHADER_UNIFORM( shader_ball, "uPv" ), 1, GL_FALSE, (float *)vg_pv ); + + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_2D, tex_ball ); + glUniform1i( SHADER_UNIFORM( shader_ball, "uTexMain" ), 0 ); + + // Draw 'fish' + if( world.simulating ) + { + for( int i = 0; i < world.num_fishes; i ++ ) + { + struct fish *fish = &world.fishes[i]; - if( selected ) - v3_muls( colour, sinf( vg_time )*0.25f + 0.5f, colour ); + if( !fish->alive ) + continue; + + // Evaluate position + struct cell *cell = pcell(fish->pos); + v2f fish_pos; - //if( cell->state & (FLAG_SPLIT) ) - // v4_copy( (v4f){ 0.75f, 0.75f, 0.02f, 1.0f }, colour ); - //if( cell->state & (FLAG_MERGER) ) - // v4_copy( (v4f){ 0.75f, 0.02f, 0.75f, 1.0f }, colour ); + v2f const *curve; - glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, colour ); + float t = frame_lerp; - glDrawArrays( GL_TRIANGLES, 0, 6 ); + switch( cell->config ) + { + case 13: + if( fish->dir[0] == 1 ) + curve = curve_12; + else + curve = curve_9; + break; + case 3: curve = curve_3; break; + case 6: curve = curve_6; break; + case 9: curve = curve_9; break; + case 12: curve = curve_12; break; + case 7: + if( t > curve_7_linear_section ) + { + t -= curve_7_linear_section; + t *= (1.0f/(1.0f-curve_7_linear_section)); + + curve = cell->state & FLAG_FLIP_FLOP? curve_7: curve_7_1; + } + else curve = NULL; + break; + default: curve = NULL; break; + } - if( cell->state & FLAG_CANAL ) + if( curve ) + { + float t2 = t * t; + float t3 = t * t * t; + + float cA = 3.0f*t2 - 3.0f*t3; + float cB = 3.0f*t3 - 6.0f*t2 + 3.0f*t; + float cC = 3.0f*t2 - t3 - 3.0f*t + 1.0f; + + fish_pos[0] = t3*curve[3][0] + cA*curve[2][0] + cB*curve[1][0] + cC*curve[0][0]; + fish_pos[1] = t3*curve[3][1] + cA*curve[2][1] + cB*curve[1][1] + cC*curve[0][1]; + fish_pos[0] += (float)fish->pos[0]; + fish_pos[1] += (float)fish->pos[1]; + } + else { - glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1.0f, 1.0f, 1.0f, 1.0f ); - glDrawArrays( GL_TRIANGLES, 6, 3 ); + v2f origin; + origin[0] = (float)fish->pos[0] + (float)fish->dir[0]*-0.5f + 0.5f; + origin[1] = (float)fish->pos[1] + (float)fish->dir[1]*-0.5f + 0.5f; + + fish_pos[0] = origin[0] + (float)fish->dir[0]*t; + fish_pos[1] = origin[1] + (float)fish->dir[1]*t; } + + v4f dot_colour = { 0.0f, 0.0f, 0.0f, 1.0f }; + colour_code_v3( fish->payload, dot_colour ); + + glUniform3fv( SHADER_UNIFORM( shader_ball, "uColour" ), 1, dot_colour ); + glUniform2f( SHADER_UNIFORM( shader_ball, "uOffset" ), fish_pos[0], fish_pos[1] ); + draw_mesh( 0, 32 ); } } - glBindVertexArray( world.circle_vao ); + glDisable(GL_BLEND); + + SHADER_USE( shader_tile_colour ); + glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_colour, "uPv" ), 1, GL_FALSE, (float *)vg_pv ); + use_mesh( &world.circle ); // Draw i/o arrays for( int i = 0; i < arrlen( world.io ); i ++ ) @@ -770,9 +1138,9 @@ void vg_render(void) int is_input = world.data[ term->id ].state & FLAG_INPUT; int const filled_start = 0; - int const filled_count = 32*3; - int const empty_start = 32*3; - int const empty_count = 32*6; + int const filled_count = 32; + int const empty_start = 32; + int const empty_count = 32*2; v4f dot_colour = { 0.0f, 0.0f, 0.0f, 1.0f }; @@ -787,9 +1155,9 @@ void vg_render(void) // Draw filled if tick not passed, draw empty if empty if( world.sim_frame > j ) - glDrawArrays( GL_TRIANGLES, empty_start, empty_count ); + draw_mesh( empty_start, empty_count ); else - glDrawArrays( GL_TRIANGLES, filled_start, filled_count ); + draw_mesh( filled_start, filled_count ); } else { @@ -799,39 +1167,26 @@ void vg_render(void) v3_muls( dot_colour, 0.8f, dot_colour ); glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour ); - glDrawArrays( GL_TRIANGLES, filled_start, filled_count ); + draw_mesh( filled_start, filled_count ); } colour_code_v3( term->conditions[j], dot_colour ); glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour ); - glDrawArrays( GL_TRIANGLES, empty_start, empty_count ); + draw_mesh( empty_start, empty_count ); } } } - // Draw 'fish' if( world.simulating ) { - float scaled_time = (vg_time-world.sim_start)*2.0f; - float lerp = 1.0f-(scaled_time - (float)world.sim_frame); - - v4f dot_colour = { 0.0f, 0.0f, 0.0f, 1.0f }; - - for( int i = 0; i < world.num_fishes; i ++ ) - { - struct fish *fish = &world.fishes[i]; - - if( !fish->alive ) - continue; - - colour_code_v3( fish->payload, dot_colour ); - glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour ); - - glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)fish->pos[0] + 0.5f - (float)fish->dir[0]*lerp, (float)fish->pos[1] + 0.25f - (float)fish->dir[1]*lerp, 0.25f ); - glDrawArrays( GL_TRIANGLES, 0, 32*3 ); - } + glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 0.0f, 0.0f, 0.0f, 1.0f ); + glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), -0.5f + cosf( vg_time * 4.0f ) * 0.2f, sinf( vg_time * 4.0f ) * 0.2f + (float)world.h * 0.5f, 0.05f ); + draw_mesh( 0, 32 ); } } -void vg_ui(void){} +void vg_ui(void) +{ + //ui_test(); +}