X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;ds=sidebyside;f=fishladder.c;h=4e41b5ad924ba80ba678ed55eb7fd3ec6162cde8;hb=1a837016d9060eb0c2ed05a93b9d2a69d0005808;hp=bf877e33b19e04f21bf4102ea50a8171f77a7381;hpb=85e0098733bc5fa6545590a285f046e668ed5a21;p=fishladder.git diff --git a/fishladder.c b/fishladder.c index bf877e3..4e41b5a 100644 --- a/fishladder.c +++ b/fishladder.c @@ -27,13 +27,49 @@ 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;" + "out vec4 aTexCoords;" "" "vec2 hash22(vec2 p)" "{" @@ -44,10 +80,15 @@ SHADER_DEFINE( shader_tile_main, "" "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 );" - "gl_Position = vec4( uPv * vec3( a_co + uOffset.xy, 1.0 ), 1.0 );" + + // 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 @@ -68,10 +109,10 @@ SHADER_DEFINE( shader_tile_main, "vec3 shadows = mix( vec3( 0.85, 0.7344, 0.561 ), vec3(1.0,1.0,1.0), glyph.r );" - "FragColor = vec4( wood_comp * shadows, 1.0 );" + "FragColor = vec4( wood_comp * shadows, glyph.b );" "}" , - UNIFORMS({ "uPv", "uOffset", "uTexGlyphs", "uTexWood" }) + UNIFORMS({ "uPv", "uOffset", "uTexGlyphs", "uTexWood", "uSubTransform" }) ) const char *level_pack[] = @@ -135,6 +176,7 @@ const char *level_pack[] = GLuint tex_tile_data; GLuint tex_tile_detail; GLuint tex_wood; +GLuint tex_ball; m3x3f m_projection; m3x3f m_view; @@ -149,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 }, @@ -236,7 +304,7 @@ struct world u32 w, h; - struct mesh tile, circle, splitter_l, splitter_r; + struct mesh tile, circle; int selected; @@ -400,6 +468,7 @@ void vg_register(void) { SHADER_INIT( shader_tile_colour ); SHADER_INIT( shader_tile_main ); + SHADER_INIT( shader_ball ); } void vg_start(void) @@ -451,21 +520,6 @@ void vg_start(void) init_mesh( &world.circle, circle_mesh, vg_list_size( circle_mesh ) ); } - // splitters (temp) - { - float splitter_l[] = - { - #include "models/splitter_l.obj.h" - }; - float splitter_r[] = - { - #include "models/splitter_r.obj.h" - }; - - init_mesh( &world.splitter_l, splitter_l, vg_list_size( splitter_l ) ); - init_mesh( &world.splitter_r, splitter_r, vg_list_size( splitter_r ) ); - } - // Textures { tex_tile_detail = vg_tex2d_rgba( "textures/tile_overlays.png" ); @@ -482,6 +536,11 @@ void vg_start(void) vg_tex2d_mipmap(); vg_tex2d_linear_mipmap(); vg_tex2d_repeat(); + + tex_ball = vg_tex2d_rgba( "textures/ball_metallic.png" ); + vg_tex2d_mipmap(); + vg_tex2d_linear_mipmap(); + vg_tex2d_clamp(); } map_load( level_pack[ 0 ] ); @@ -491,14 +550,13 @@ void vg_free(void) { free_mesh( &world.tile ); free_mesh( &world.circle ); - free_mesh( &world.splitter_l ); - free_mesh( &world.splitter_r ); - + 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 ) @@ -514,11 +572,10 @@ static int cell_interactive( v2i co ) // List of 3x3 configurations that we do not allow static u32 invalid_src[][9] = { - /* { 0,1,0, 1,1,1, 0,1,0 - },*/ + }, { 0,0,0, 0,1,1, 0,1,1 @@ -535,7 +592,6 @@ static int cell_interactive( v2i co ) 1,1,0, 0,0,0 }, - /* { 0,1,0, 0,1,1, 0,1,0 @@ -543,7 +599,7 @@ static int cell_interactive( v2i co ) { 0,1,0, 1,1,0, 0,1,0 - }*/ + } }; // Statically compile invalid configurations into bitmasks @@ -717,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; @@ -770,6 +828,16 @@ void vg_update(void) } } } + + // 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 ++ ) { @@ -842,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; } } @@ -858,79 +931,32 @@ void vg_render(void) glClearColor( 0.8f, 0.8f, 0.8f, 1.0f ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - // 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}); - - if( cell->state & FLAG_CANAL ) - { - continue; - } - - glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)x - 0.2f, (float)y - 0.15f, 1.0f ); - draw_mesh( 0, 2 ); - } + float scaled_time = 0.0f, frame_lerp = 0.0f; - for( int y = 0; y < world.h; y ++ ) + if( world.simulating ) { - 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_SPLIT ) - { - glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 0.9f, 0.9f, 0.9f, 1.0f ); - glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)x, (float)y, 1.0f ); - - struct mesh *splitter = cell->state & FLAG_FLIP_FLOP? &world.splitter_r: &world.splitter_l; - - use_mesh( splitter ); - draw_mesh( 0, splitter->elements ); - use_mesh( &world.tile ); - } - - 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_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.4f, 0.4f, 0.4f, 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( selected ) - v3_muls( colour, sinf( vg_time )*0.25f + 0.5f, colour ); - - //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 ); - - glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, colour ); - - draw_mesh( 0, 2 ); - } + scaled_time = (vg_time-world.sim_start)*2.0f; + frame_lerp = scaled_time - (float)world.sim_frame; } - */ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBlendEquation(GL_FUNC_ADD); + 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 @@ -949,25 +975,6 @@ void vg_render(void) struct cell *cell = pcell((v2i){x,y}); int selected = world.selected == y*world.w + x; - /* - 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= - | | | | | | | - */ - int tile_offsets[][2] = { {2, 0}, {0, 3}, {0, 2}, {2, 2}, @@ -984,104 +991,79 @@ void vg_render(void) uv[1] = tile_offsets[ cell->config ][1]; } - glUniform4f( SHADER_UNIFORM( shader_tile_main, "uOffset" ), (float)x, (float)y, uv[0], uv[1] ); // TODO: PICK GLYPH + 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); - SHADER_USE( shader_tile_colour ); - glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_colour, "uPv" ), 1, GL_FALSE, (float *)vg_pv ); - - glDisable(GL_BLEND); - - - - use_mesh( &world.circle ); - - // Draw i/o arrays - for( int i = 0; i < arrlen( world.io ); i ++ ) + for( int y = 0; y < world.h; y ++ ) { - struct cell_terminal *term = &world.io[ i ]; - int posx = term->id % world.w; - int posy = (term->id - posx)/world.w; - int is_input = world.data[ term->id ].state & FLAG_INPUT; - - int const filled_start = 0; - 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 }; - - for( int j = 0; j < arrlen( term->conditions ); j ++ ) + for( int x = 0; x < world.w; x ++ ) { - glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)posx + 0.2f + 0.2f * (float)j, (float)posy + 0.2f, 0.1f ); - - if( is_input ) - { - colour_code_v3( term->conditions[j], dot_colour ); - glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour ); + struct cell *cell = pcell((v2i){x,y}); - // Draw filled if tick not passed, draw empty if empty - if( world.sim_frame > j ) - draw_mesh( empty_start, empty_count ); - else - draw_mesh( filled_start, filled_count ); - } - else + if( cell->state & FLAG_SPLIT ) { - if( term->recv_count > j ) + float rotation = cell->state & FLAG_FLIP_FLOP? vg_rad( -45.0f ): vg_rad( 45.0f ); + + if( cell->state & FLAG_FLIP_ROTATING ) { - colour_code_v3( term->recv[j], dot_colour ); - v3_muls( dot_colour, 0.8f, dot_colour ); - glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour ); - - draw_mesh( filled_start, filled_count ); + 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; + } } - - colour_code_v3( term->conditions[j], dot_colour ); - glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour ); - - draw_mesh( empty_start, empty_count ); + + 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 ) { - float scaled_time = (vg_time-world.sim_start)*2.0f; - float lerp = 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 ); - + // Evaluate position struct cell *cell = pcell(fish->pos); v2f fish_pos; - 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 linear_section = 0.1562f; - v2f const *curve; - float t = lerp; + float t = frame_lerp; switch( cell->config ) { @@ -1096,10 +1078,10 @@ void vg_render(void) case 9: curve = curve_9; break; case 12: curve = curve_12; break; case 7: - if( t > linear_section ) + if( t > curve_7_linear_section ) { - t -= linear_section; - t *= (1.0f/(1.0f-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; } @@ -1110,7 +1092,6 @@ void vg_render(void) if( curve ) { - // bezier thing float t2 = t * t; float t3 = t * t * t; @@ -1133,14 +1114,70 @@ void vg_render(void) fish_pos[1] = origin[1] + (float)fish->dir[1]*t; } - vg_line_box( (v2f){fish->pos[0],fish->pos[1]}, - (v2f){ (float)fish->pos[0]+1.0f, (float)fish->pos[1]+1.0f }, 0xffffffff ); + v4f dot_colour = { 0.0f, 0.0f, 0.0f, 1.0f }; + colour_code_v3( fish->payload, dot_colour ); - glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), fish_pos[0], fish_pos[1], 0.125f ); + 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 ); } } + 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 ++ ) + { + struct cell_terminal *term = &world.io[ i ]; + int posx = term->id % world.w; + int posy = (term->id - posx)/world.w; + int is_input = world.data[ term->id ].state & FLAG_INPUT; + + int const filled_start = 0; + 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 }; + + for( int j = 0; j < arrlen( term->conditions ); j ++ ) + { + glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)posx + 0.2f + 0.2f * (float)j, (float)posy + 0.2f, 0.1f ); + + if( is_input ) + { + colour_code_v3( term->conditions[j], dot_colour ); + glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour ); + + // Draw filled if tick not passed, draw empty if empty + if( world.sim_frame > j ) + draw_mesh( empty_start, empty_count ); + else + draw_mesh( filled_start, filled_count ); + } + else + { + if( term->recv_count > j ) + { + colour_code_v3( term->recv[j], dot_colour ); + v3_muls( dot_colour, 0.8f, dot_colour ); + glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour ); + + draw_mesh( filled_start, filled_count ); + } + + colour_code_v3( term->conditions[j], dot_colour ); + glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour ); + + draw_mesh( empty_start, empty_count ); + } + } + } + if( world.simulating ) { glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 0.0f, 0.0f, 0.0f, 1.0f );