X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=fishladder.c;h=54e1a537926a36f78a3c4adf5a8e719233b81196;hb=f19285848db49f99e37df4e08d1f053758c6f7d4;hp=ce43fa27bc29108473e17010c9b8efa85b6363df;hpb=c8165f11120b296045f670bbdd8e614e92b0c5e8;p=fishladder.git diff --git a/fishladder.c b/fishladder.c index ce43fa2..54e1a53 100644 --- a/fishladder.c +++ b/fishladder.c @@ -27,6 +27,41 @@ 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.5 - 0.25 + 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( uColour + glyph.rgb * 0.2, glyph.a );" + "}" + , + UNIFORMS({ "uTexMain", "uColour", "uOffset", "uPv" }) +) + SHADER_DEFINE( shader_tile_main, // VERTEX "layout (location=0) in vec2 a_co;" @@ -34,7 +69,7 @@ SHADER_DEFINE( shader_tile_main, "uniform mat3 uPv;" "uniform mat2 uSubTransform;" "" - "out vec4 aTexCoords;" + "out vec4 aTexCoords;" "" "vec2 hash22(vec2 p)" "{" @@ -141,6 +176,31 @@ const char *level_pack[] = GLuint tex_tile_data; GLuint tex_tile_detail; GLuint tex_wood; +GLuint tex_ball; + +sfx_system_t audio_system_sfx = +{ + .vol = 1.f, + .spd = 1.f, + .ch = 1, + .cur = 0, + .vol_src = &g_vol_sfx, + .flags = 0x00, + .fadeout = FADEOUT_LENGTH, + .name = "sfx" +}; + +sfx_set_t audio_tile_mod = +{ + .sources = "\ +sound/mod_01.ogg\0\ +sound/mod_02.ogg\0\ +sound/mod_03.ogg\0\ +sound/mod_04.ogg\0\ +sound/mod_05.ogg\0\ +sound/mod_06.ogg\0", + .flags = 0 +}; m3x3f m_projection; m3x3f m_view; @@ -150,13 +210,34 @@ m3x3f m_mdl; #define FLAG_OUTPUT 0x2 #define FLAG_CANAL 0x4 #define FLAG_WALL 0x8 -#define FLAG_DROP_L 0x10 -#define FLAG_SPLIT 0x20 -#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 }, { 0.2f, 0.9f, 0.14f }, @@ -243,7 +324,7 @@ struct world u32 w, h; - struct mesh tile, circle, splitter_l, splitter_r; + struct mesh tile, circle; int selected; @@ -407,6 +488,7 @@ void vg_register(void) { SHADER_INIT( shader_tile_colour ); SHADER_INIT( shader_tile_main ); + SHADER_INIT( shader_ball ); } void vg_start(void) @@ -458,21 +540,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" ); @@ -489,6 +556,16 @@ void vg_start(void) vg_tex2d_mipmap(); vg_tex2d_linear_mipmap(); vg_tex2d_repeat(); + + tex_ball = vg_tex2d_rgba( "textures/ball.png" ); + vg_tex2d_mipmap(); + vg_tex2d_linear_mipmap(); + vg_tex2d_clamp(); + } + + // Audio + { + sfx_set_init( &audio_tile_mod, NULL ); } map_load( level_pack[ 0 ] ); @@ -498,14 +575,15 @@ 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 ); + + sfx_set_free( &audio_tile_mod ); } static int cell_interactive( v2i co ) @@ -593,6 +671,35 @@ static int cell_interactive( v2i co ) return 1; } +// Entire world: 2 -> worldx/y-2 + +static void fl_world_update( v2i start, v2i end ) +{ + for( int y = start[1]; y < end[1]; y ++ ) + { + for( int x = start[0]; x < end[0]; x ++ ) + { + v2i dirs[] = {{1,0},{0,1},{-1,0},{0,-1}}; + + u8 config = 0x00; + + if( pcell((v2i){x,y})->state & FLAG_CANAL ) + { + for( int i = 0; i < vg_list_size( dirs ); i ++ ) + { + struct cell *neighbour = pcell((v2i){x+dirs[i][0], y+dirs[i][1]}); + if( neighbour->state & (FLAG_CANAL|FLAG_INPUT|FLAG_OUTPUT) ) + config |= 0x1 << i; + } + + + } else config = 0xF; + + pcell((v2i){x,y})->config = config; + } + } +} + void vg_update(void) { static int curlevel = 0; @@ -648,6 +755,11 @@ void vg_update(void) if( vg_get_button_down("primary") ) { world.data[ world.selected ].state ^= FLAG_CANAL; + + if( world.data[ world.selected ].state & FLAG_CANAL ) + sfx_set_playrnd( &audio_tile_mod, &audio_system_sfx, 3, 6 ); + else + sfx_set_playrnd( &audio_tile_mod, &audio_system_sfx, 0, 3 ); } } else @@ -686,56 +798,10 @@ void vg_update(void) world.io[i].recv_count = 0; } } + + // There was world reconfiguarion here previously... + fl_world_update( (v2i){2,2}, (v2i){world.w-2,world.h-2} ); - // Simulation stuff - // ======================================================== - - for( int y = 2; y < world.h-2; y ++ ) - { - for( int x = 2; x < world.w-2; x ++ ) - { - v2i dirs[] = {{1,0},{0,1},{-1,0},{0,-1}}; - - u8 config = 0x00; - - if( pcell((v2i){x,y})->state & FLAG_CANAL ) - { - for( int i = 0; i < vg_list_size( dirs ); i ++ ) - { - struct cell *neighbour = pcell((v2i){x+dirs[i][0], y+dirs[i][1]}); - if( neighbour->state & (FLAG_CANAL|FLAG_INPUT|FLAG_OUTPUT) ) - config |= 0x1 << i; - } - } else config = 0xF; - - pcell((v2i){x,y})->config = config; - pcell((v2i){x,y})->state &= ~(FLAG_DROP_L|FLAG_DROP_R|FLAG_SPLIT|FLAG_MERGER); - } - } - - for( int y = 2; y < world.h-2; y ++ ) - for( int x = 2; x < world.w-2; x ++ ) - { - // R,D,L,- 1110 (splitter, 1 drop created) - - // R,-,L,U - 1011 (merger, 2 drop created) - - u8 config = pcell((v2i){x,y})->config; - - if( config == 0x7 ) // splitter - { - struct cell *cell = pcell((v2i){x,y}); - - cell->state |= (FLAG_SPLIT | FLAG_DROP_L | FLAG_DROP_R); - } - else if( config == 0xD ) - { - world.data[y*world.w+x-1].state |= FLAG_DROP_R; - world.data[y*world.w+x+1].state |= FLAG_DROP_L; - world.data[y*world.w+x].state |= FLAG_MERGER; - } - } - // Fish ticks if( world.simulating ) { @@ -782,7 +848,7 @@ void vg_update(void) for( int i = 0; i < world.h*world.w; i ++ ) { struct cell *cell = &world.data[i]; - if( cell->config == 0x7 ) + if( cell->config == k_cell_type_split ) { cell->state &= ~FLAG_FLIP_ROTATING; } @@ -814,56 +880,53 @@ void vg_update(void) continue; } - if( !(cell_current->state & (FLAG_INPUT|FLAG_CANAL)) ) + if( cell_current->config == k_cell_type_split ) { - fish->alive = 0; + // Flip flop L/R + fish->dir[0] = cell_current->state&FLAG_FLIP_FLOP?1:-1; + fish->dir[1] = 0; + + cell_current->state ^= FLAG_FLIP_FLOP; + } + else if( cell_current->config == k_cell_type_merge ) + { + // Can only move up + fish->dir[0] = 0; + fish->dir[1] = -1; } else { - if( cell_current->state & FLAG_SPLIT ) + struct cell *cell_next = pcell( (v2i){ fish->pos[0]+fish->dir[0], fish->pos[1]+fish->dir[1] } ); + if( !(cell_next->state & (FLAG_CANAL|FLAG_OUTPUT)) ) { - // Flip flop L/R - fish->dir[0] = cell_current->state&FLAG_FLIP_FLOP?1:-1; - fish->dir[1] = 0; + // Try other directions for valid, so down, left, right.. + v2i dirs[] = {{1,0},{-1,0},{0,-1}}; + vg_info( "Trying some other directions...\n" ); - cell_current->state ^= FLAG_FLIP_FLOP; - } - else if( cell_current->state & FLAG_MERGER ) - { - // Can only move up - fish->dir[0] = 0; - fish->dir[1] = -1; - } - 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|FLAG_OUTPUT)) ) + for( int j = 0; j < vg_list_size(dirs); j ++ ) { - // Try other directions for valid, so down, left, right.. - v2i dirs[] = {{1,0},{-1,0},{0,-1}}; - vg_info( "Trying some other directions...\n" ); - - for( int j = 0; j < vg_list_size(dirs); j ++ ) + 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|FLAG_OUTPUT) ) { - 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|FLAG_OUTPUT) ) - { - fish->dir[0] = dirs[j][0]; - fish->dir[1] = dirs[j][1]; - } + fish->dir[0] = dirs[j][0]; + fish->dir[1] = dirs[j][1]; } } } - - fish->pos[0] += fish->dir[0]; - fish->pos[1] += fish->dir[1]; - - struct cell *cell_entry = pcell( fish->pos ); - if( cell_entry->config == 0x7 ) - cell_entry->state |= FLAG_FLIP_ROTATING; } + + fish->pos[0] += fish->dir[0]; + fish->pos[1] += fish->dir[1]; + + struct cell *cell_entry = pcell( fish->pos ); + + if( !(cell_entry->state & (FLAG_INPUT|FLAG_CANAL|FLAG_OUTPUT) )) + fish->alive = 0; + else + if( cell_entry->config == k_cell_type_split ) + cell_entry->state |= FLAG_FLIP_ROTATING; } world.sim_frame ++; @@ -871,6 +934,37 @@ void vg_update(void) } } +static void render_tiles(void) +{ + 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 ) + { + uv[0] = tile_offsets[ cell->config ][0]; + uv[1] = tile_offsets[ cell->config ][1]; + } + + glUniform4f( SHADER_UNIFORM( shader_tile_main, "uOffset" ), (float)x, (float)y, uv[0], uv[1] ); + draw_mesh( 0, 2 ); + } + } +} + void vg_render(void) { glViewport( 0,0, vg_window_x, vg_window_y ); @@ -887,73 +981,6 @@ void vg_render(void) frame_lerp = scaled_time - (float)world.sim_frame; } - // 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 ); - } - - 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_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 ); - } - } - */ - 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}}; @@ -964,9 +991,16 @@ void vg_render(void) float const curve_7_linear_section = 0.1562f; - // TILE SET RENDERING + // TILE SET RENDERING + // todo: just slam everything into a mesh... + // when user modifies a tile the neighbours can be easily uploaded to gpu mem + // in ~3 subBuffers + // Currently we're uploading a fair amount of data every frame anyway. + // NOTE: this is for final optimisations ONLY! // ====================================================================== + use_mesh( &world.tile ); + SHADER_USE( shader_tile_main ); m2x2f subtransform; @@ -983,65 +1017,118 @@ void vg_render(void) glBindTexture( GL_TEXTURE_2D, tex_wood ); glUniform1i( SHADER_UNIFORM( shader_tile_main, "uTexWood" ), 1 ); - for( int y = 0; y < world.h; y ++ ) + render_tiles(); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation(GL_FUNC_ADD); + + 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 x = 0; x < world.w; x ++ ) + for( int i = 0; i < world.num_fishes; i ++ ) { - struct cell *cell = pcell((v2i){x,y}); - int selected = world.selected == y*world.w + x; + struct fish *fish = &world.fishes[i]; - /* - 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= - | | | | | | | - */ + if( !fish->alive ) + continue; + + // Evaluate position + struct cell *cell = pcell(fish->pos); + v2f fish_pos; - 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} - }; + v2f const *curve; - int uv[2] = { 3, 0 }; + float t = frame_lerp; - if( cell->state & FLAG_CANAL ) + switch( cell->config ) { - uv[0] = tile_offsets[ cell->config ][0]; - uv[1] = tile_offsets[ cell->config ][1]; + 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; } - glUniform4f( SHADER_UNIFORM( shader_tile_main, "uOffset" ), (float)x, (float)y, uv[0], uv[1] ); - draw_mesh( 0, 2 ); + 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 + { + 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 ); } } + + + SHADER_USE( shader_tile_main ); - // Draw splitters - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBlendEquation(GL_FUNC_ADD); + // 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 ); + + render_tiles(); + + // Draw splitters + 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_SPLIT ) + if( cell->config == k_cell_type_split ) { float rotation = cell->state & FLAG_FLIP_FLOP? vg_rad( -45.0f ): vg_rad( 45.0f ); @@ -1072,6 +1159,8 @@ void vg_render(void) } } + //glDisable(GL_BLEND); + glDisable(GL_BLEND); SHADER_USE( shader_tile_colour ); @@ -1127,87 +1216,6 @@ void vg_render(void) } } - // Draw 'fish' - if( world.simulating ) - { - 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; - - float t = frame_lerp; - - 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( curve ) - { - // bezier thing - 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 - { - 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; - } - - vg_line_box( (v2f){fish->pos[0],fish->pos[1]}, - (v2f){ (float)fish->pos[0]+1.0f, (float)fish->pos[1]+1.0f }, 0xffffffff ); - - glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), fish_pos[0], fish_pos[1], 0.125f ); - draw_mesh( 0, 32 ); - } - } - if( world.simulating ) { glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 0.0f, 0.0f, 0.0f, 1.0f );