1 // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
6 #define CELL_SHEET_X 512
7 #define CELL_SHEET_Y 512
9 SHADER_DEFINE( colour_shader
,
12 "layout (location=0) in vec2 a_co;"
18 " gl_Position = vec4( uPv * uMdl * vec3( a_co, 1.0 ), 1.0 );"
23 "uniform vec4 uColour;"
27 " FragColor = uColour;"
30 UNIFORMS({ "uPv", "uMdl", "uColour" })
33 SHADER_DEFINE( tilemap_shader
,
36 "layout (location=0) in vec2 a_co;" // XY
37 "layout (location=1) in vec2 a_offset;" // XY offset
38 "layout (location=2) in vec4 a_data;" // atlas-uv, amt, other
40 "uniform vec2 uOrigin;"
46 "vec2 world_coord = a_co+a_offset+uOrigin;"
47 "gl_Position = vec4( uPv * vec3( world_coord, 1.0 ), 1.0 );"
48 "aCoords = vec4( (a_co*0.98+0.01 + vec2(a_data.x,0.0)) * 0.0625, a_data.zw );"
52 "uniform sampler2D uTexTiles;"
53 "uniform sampler2D uTexRipples;"
54 "uniform float uTime;"
61 "vec4 glyph = texture( uTexTiles, aCoords.xy );"
62 "vec4 ripple = texture( uTexRipples, vec2( glyph.x - uTime, glyph.y ));"
63 "float wstatus = -((aCoords.z/128.0)-1.0);" // -1 := None in, 0.0 := Hold middle, 1.0 := All out
64 "float dev = step( 0.0, glyph.x+wstatus ) * step( glyph.x+wstatus, 1.0 );" //+ abs(glyph.y-0.5) );"
66 // TODO: Fix texture instead of doing this..
67 "float shadow = mix( 1.0, glyph.z, step(0.25, glyph.w) );"
68 "vec3 composite = mix( vec3(0.5,0.5,0.5), vec3(0.3,0.6,1.0) + ripple.xyz * 0.05, step( 0.75, glyph.a )*dev ) * shadow;"
69 "FragColor = vec4( composite, 1.0 );"
72 UNIFORMS({ "uPv", "uTexTiles", "uTexRipples", "uTime", "uOrigin" })
75 SHADER_DEFINE( fish_shader
,
77 "layout (location=0) in vec2 a_co;" // XY UV
80 "uniform vec3 uPosition;"
82 "out vec2 aTexCoords;"
86 "vec2 world_coord = a_co + uPosition.xy;"
87 "gl_Position = vec4( uPv * vec3( world_coord, 1.0 ), 1.0 );"
88 "aTexCoords = vec2( (a_co.x + uPosition.z) * 0.125, a_co.y );"
91 "uniform sampler2D uTexMain;"
92 "uniform vec3 uColour;"
99 "vec4 maintex = texture( uTexMain, aTexCoords );"
100 "vec3 comp = mix( vec3(0.0,0.03,0.04), uColour, maintex.r );"
101 "FragColor = vec4( comp, maintex.g * 0.7 );"
103 UNIFORMS({ "uPv", "uTexMain", "uColour", "uPosition" })
110 int main( int argc
, char *argv
[] )
112 vg_init( argc
, argv
, "FishLadder" );
115 #define CELL_FLAG_INPUT 0x1
116 #define CELL_FLAG_OUTPUT 0x2
117 #define CELL_FLAG_IO (CELL_FLAG_INPUT|CELL_FLAG_OUTPUT)
118 #define CELL_FLAG_WALL 0x4
119 #define CELL_FLAG_HOVER 0x8
120 #define CELL_FLAG_ITER 0x10
121 #define CELL_FLAG_CANAL 0x20
122 #define CELL_FLAG_SPLIT 0x40 /* Does this cell split and have an incoming vertical connection? */
123 #define CELL_FLAG_WALKABLE (CELL_FLAG_IO|CELL_FLAG_CANAL)
124 #define CELL_FLAG_VISITED 0x80
125 #define CELL_FLAG_UPLVL 0x100
126 #define CELL_FLAG_MERGE 0x200
129 // | byte 3 | byte 2 | byte 1 | byte 0 |
130 // | anim | cfg | rflags | sflags |
133 // Level defined (fixed)
134 #define FLAG_WALL 0x1 /* Border cells // non passable */
135 #define FLAG_LEVEL_CANAL 0x2 /* Used to mark canals from IO or static sections etc */
138 #define FLAG_CANAL 0x4 /* Cell is marked as canal and can transport water */
139 #define FLAG_UNUSED0 0x8
140 #define FLAG_UNUSED1 0x10
141 #define FLAG_UNUSED2 0x20
142 #define FLAG_UNUSED3 0x40
143 #define FLAG_UNUSED4 0x80
146 #define FLAG_WATER 0x100 /* Does this cell have water flowing through it */
147 #define FLAG_UNUSED5 0x200
148 #define FLAG_UNUSED6 0x400
149 #define FLAG_UNUSED7 0x800
151 #define FLAG_UNUSED8 0x1000
152 #define FLAG_UNUSED9 0x2000
153 #define FLAG_UNUSEDA 0x4000
154 #define FLAG_UNUSEDB 0x8000
157 #define CFLAG_TRANSPORT (FLAG_CANAL|FLAG_LEVEL_CANAL)
164 struct cell
*selected
;
193 static void map_free(void)
195 for( int i
= 0; i
< arrlen( map
.io
); i
++ )
197 arrfree( map
.cells
[ map
.io
[i
] ].conditions
);
200 arrfree( map
.cells
);
208 static struct cell
*ptile( int pos
[2] )
210 return map
.cells
+ pos
[1]*map
.x
+ pos
[0];
213 static struct cell
*gettile( int pos
[2] )
215 if( pos
[0] >= 0 && pos
[0] < map
.x
&& pos
[1] >= 0 && pos
[1] < map
.y
)
216 return map
.cells
+ pos
[1]*map
.x
+ pos
[0];
220 static struct cell
*getiftile( int pos
[2], u32 flags
)
222 struct cell
*cell
= gettile( pos
);
223 if( cell
&& (cell
->flags
& flags
) )
229 static void map_tile_coords_from_index( int i
, int coords
[2] )
231 coords
[0] = i
% map
.x
;
232 coords
[1] = (i
- coords
[0])/map
.x
;
235 static void map_stack_refresh(void)
237 for( int i
= 0; i
< map
.x
*map
.y
; i
++ )
238 map
.cells
[i
].flags
&= ~CELL_FLAG_VISITED
;
241 static void map_stack_init( int coords
[2] )
244 map
.stack
.frames
[0].i
= 0;
245 map
.stack
.frames
[0].x
= coords
[0];
246 map
.stack
.frames
[0].y
= coords
[1];
249 static struct cell
*map_stack_next(void)
251 struct cell
*tile
= NULL
;
255 struct vframe
*frame
= &map
.stack
.frames
[ map
.stack
.level
];
257 int output_dirs
[][2] = { {0,1}, {-1,0}, {1,0} };
261 int *dir
= output_dirs
[ frame
->i
];
262 tile
= getiftile( (int[2]){frame
->x
+dir
[0], frame
->y
+dir
[1]}, CELL_FLAG_WALKABLE
);
264 if( tile
&& !(tile
->flags
& CELL_FLAG_VISITED
) )
268 frame
[1].x
= frame
[0].x
+dir
[0];
269 frame
[1].y
= frame
[0].y
+dir
[1];
271 tile
->flags
|= CELL_FLAG_VISITED
;
286 if( map
.stack
.level
< 0 )
294 static void map_stack_current_coords( int coords
[2], int offset
)
296 coords
[0] = map
.stack
.frames
[ map
.stack
.level
+offset
].x
;
297 coords
[1] = map
.stack
.frames
[ map
.stack
.level
+offset
].y
;
300 static void map_reclassify( v4i bounds
)
304 for( int y
= bounds
[1]; y
< bounds
[3]; y
++ )
306 for( int x
= bounds
[0]; x
< bounds
[2]; x
++ )
308 struct cell
*cur
= map
.cells
+ y
*map
.x
+ x
;
309 u8
*cellbytes
= celldata
+ (y
*map
.x
+x
)*4;
311 if( cur
->flags
& CELL_FLAG_WALKABLE
)
313 struct cell
*a
, *b
, *c
, *d
;
315 a
= getiftile( (int[2]){ x
,y
+1 }, CELL_FLAG_WALKABLE
);
316 b
= getiftile( (int[2]){ x
+1,y
}, CELL_FLAG_WALKABLE
);
317 c
= getiftile( (int[2]){ x
,y
-1 }, CELL_FLAG_WALKABLE
);
318 d
= getiftile( (int[2]){ x
-1,y
}, CELL_FLAG_WALKABLE
);
320 u32 config
= (a
?0x1:0x0) | (b
?0x2:0x0) | (c
?0x4:0x0) | (d
?0x8:0x0);
321 cellbytes
[ 0 ] = config
;
325 // TODO: Random background tiles
329 cellbytes
[ 1 ] = 0x00;
330 cellbytes
[ 2 ] = 0x00;
331 cellbytes
[ 3 ] = 0x00;
335 glBindBuffer( GL_ARRAY_BUFFER
, map
.tiles_vbo
);
336 glBufferSubData( GL_ARRAY_BUFFER
, 16*sizeof(float) + 1024*2*sizeof(float), map
.x
*map
.y
*4, celldata
);
339 static int map_load( const char *str
)
348 if( str
[map
.x
] == ';' )
350 else if( !str
[map
.x
] )
352 vg_error( "Unexpected EOF when parsing level!\n" );
357 struct cell
*row
= arraddnptr( map
.cells
, map
.x
);
359 int reg_start
= 0, reg_end
= 0;
375 if( reg_start
< reg_end
)
377 if( *c
>= 'a' && *c
<= 'z' )
379 arrpush( map
.cells
[ map
.io
[ reg_start
] ].conditions
, *c
);
383 if( *c
== ',' || *c
== '\n' )
392 vg_error( "Unkown attrib '%c' (row: %u)\n", *c
, map
.y
);
399 vg_error( "Over-assigned values (row: %u)\n", map
.y
);
407 if( reg_start
!= reg_end
)
409 vg_error( "Not enough values assigned (row: %u, %u of %u)\n", map
.y
, reg_start
, reg_end
);
415 vg_error( "Map row underflow (row: %u, %u<%u)\n", map
.y
, cx
, map
.x
);
419 row
= arraddnptr( map
.cells
, map
.x
);
422 reg_end
= reg_start
= arrlen( map
.io
);
428 vg_error( "Map row overflow (row: %u, %u>%u)\n", map
.y
, cx
, map
.x
);
432 row
[ cx
].conditions
= NULL
;
434 // Parse the various cell types
435 if( *c
== '+' || *c
== '-' )
437 arrpush( map
.io
, cx
+ map
.y
*map
.x
);
438 row
[ cx
++ ].flags
= *c
== '+'? CELL_FLAG_INPUT
: CELL_FLAG_OUTPUT
;
443 row
[ cx
++ ].flags
= CELL_FLAG_WALL
;
447 row
[ cx
++ ].flags
= 0x00;
454 // Origin top left corner
455 map
.origin
[0] = -((float)map
.x
) * 0.5f
;
456 map
.origin
[1] = -((float)map
.y
) * 0.5f
;
458 float *offset_array
= (float *)malloc( map
.x
*map
.y
*2*sizeof(float) );
460 for( int y
= 0; y
< map
.y
; y
++ )
462 for( int x
= 0; x
< map
.x
; x
++ )
464 float *coord
= offset_array
+ (y
*map
.x
+x
)*2;
470 glBindBuffer( GL_ARRAY_BUFFER
, map
.tiles_vbo
);
471 glBufferSubData( GL_ARRAY_BUFFER
, 16*sizeof(float), map
.x
*map
.y
*2*sizeof(float), offset_array
);
473 free( offset_array
);
474 vg_success( "Map loaded! (%u:%u)\n", map
.x
, map
.y
);
478 // Determines if tile at position co is contentious
479 static int map_tile_availible( int co
[2] )
481 // Extract tiles area of influence as a 5x5 grid in bitfield format
483 for( int y
= vg_max( co
[1]-2, 0 ); y
< vg_min( map
.y
, co
[1]+3 ); y
++ )
484 for( int x
= vg_max( co
[0]-2, 0 ); x
< vg_min( map
.x
, co
[0]+3 ); x
++ )
485 if( getiftile( (int[2]){ x
, y
}, CELL_FLAG_WALKABLE
) )
486 blob
|= 0x1 << ((y
-(co
[1]-2))*5 + x
-(co
[0]-2));
488 // Run filter over center 3x3 grid to check for invalid configurations
489 // The kernel codes both the offsets for reaching each tile inside it, as well as the offsets
490 // to move the kernel as a whole.
491 int kernel
[] = { 0, 1, 2, 5, 6, 7, 10, 11, 12 };
492 for( int i
= 0; i
< vg_list_size(kernel
); i
++ )
493 if( blob
& (0x1 << (6+kernel
[i
])) )
495 // Illegal moves list in bitfield format
496 u32 invalid
[] = { 0x8E2, 0x63, 0xC6, 0xC60, 0x18C0, 0x862, 0x8C2 };
497 u32 window
= blob
>> kernel
[i
];
499 for( int j
= 0; j
< vg_list_size(invalid
); j
++ )
500 if((window
& invalid
[j
]) == invalid
[j
])
510 float ratio
= (float)vg_window_y
/ (float)vg_window_x
;
511 float const size
= 7.5f
;
513 m3x3_projection( m_projection
, -size
, size
, size
*ratio
, -size
*ratio
);
514 m3x3_identity( m_view
);
515 m3x3_mul( m_projection
, m_view
, vg_pv
);
516 vg_projection_update();
518 // Compute map update
519 for( int y
= 0; y
< map
.y
; y
++ )
521 for( int x
= 0; x
< map
.x
; x
++ )
523 struct cell
*tile
, *upper
, *lower
, *l
, *r
;
524 tile
= gettile( (int [2]){ x
, y
} );
525 tile
->flags
&= ~(CELL_FLAG_SPLIT
|CELL_FLAG_MERGE
|CELL_FLAG_UPLVL
);
527 if( tile
->flags
& CELL_FLAG_WALKABLE
)
529 r
= getiftile( (int[2]){ x
+1, y
}, CELL_FLAG_WALKABLE
);
530 l
= getiftile( (int[2]){ x
-1, y
}, CELL_FLAG_WALKABLE
);
534 upper
= getiftile( (int[2]){ x
, y
-1 }, CELL_FLAG_WALKABLE
);
535 lower
= getiftile( (int[2]){ x
, y
+1 }, CELL_FLAG_WALKABLE
);
539 tile
->flags
|= CELL_FLAG_MERGE
| CELL_FLAG_UPLVL
;
544 tile
->flags
|= CELL_FLAG_SPLIT
;
545 l
->flags
|= CELL_FLAG_UPLVL
;
546 r
->flags
|= CELL_FLAG_UPLVL
;
556 v2_copy( vg_mouse_ws
, tile_pos
);
557 v2_sub( tile_pos
, map
.origin
, tile_pos
);
559 int tile_x
= floorf( tile_pos
[0] );
560 int tile_y
= floorf( tile_pos
[1] );
562 map
.selected
= ptile( (int [2]){tile_x
, tile_y
} );
566 if( vg_get_button_down( "go" ) )
569 vg_info( "Ending!\n" );
574 if( vg_get_button_down( "go" ) )
577 vg_info( "Starting!\n" );
582 map
.select_valid
= map_tile_availible( (int[2]){ tile_x
, tile_y
} );
584 if( map
.select_valid
)
586 if( vg_get_button_down("primary") )
588 if( map
.selected
->flags
& CELL_FLAG_CANAL
)
590 map
.selected
->flags
&= ~(CELL_FLAG_CANAL
);
594 map
.selected
->flags
|= CELL_FLAG_CANAL
;
608 glViewport( 0,0, vg_window_x
, vg_window_y
);
610 //glEnable( GL_DEPTH_TEST );
611 glClearColor( 0.94f
, 0.94f
, 0.94f
, 1.0f
);
612 glClear( GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
614 glBindVertexArray( map
.tiles_vao
);
616 //map_update_visual();
618 SHADER_USE( tilemap_shader
);
619 glUniformMatrix3fv( SHADER_UNIFORM( tilemap_shader
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
621 glUniform1i( SHADER_UNIFORM( tilemap_shader
, "uTexTiles" ), 0 );
622 glActiveTexture( GL_TEXTURE0
);
623 glBindTexture( GL_TEXTURE_2D
, map
.tile_texture
);
625 glUniform1i( SHADER_UNIFORM( tilemap_shader
, "uTexRipples" ), 1 );
626 glActiveTexture( GL_TEXTURE1
);
627 glBindTexture( GL_TEXTURE_2D
, map
.flow_texture
);
629 glUniform2fv( SHADER_UNIFORM( tilemap_shader
, "uOrigin" ), 1, map
.origin
);
630 glUniform1f( SHADER_UNIFORM( tilemap_shader
, "uTime" ), vg_time
* 0.5f
);
632 glDrawArraysInstanced( GL_TRIANGLES
, 0, 6, map
.x
*map
.y
);
635 SHADER_USE( fish_shader );
636 glUniformMatrix3fv( SHADER_UNIFORM( fish_shader, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
637 glUniform1i( SHADER_UNIFORM( fish_shader, "uTexMain" ), 0 );
638 glActiveTexture( GL_TEXTURE0 );
639 glBindTexture( GL_TEXTURE_2D, fish_texture );
640 glUniform3f( SHADER_UNIFORM( fish_shader, "uPosition" ), 0.0f, 0.0f, floorf( vg_time*20.0f ) );
641 glUniform3f( SHADER_UNIFORM( fish_shader, "uColour" ), 1.0f, 0.1f, 0.1f );
643 glEnable( GL_BLEND );
644 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
645 glBlendEquation( GL_FUNC_ADD );
647 glDrawArrays( GL_TRIANGLES, 0, 6 );
649 glDisable( GL_BLEND );
653 void vg_register(void)
655 SHADER_INIT( colour_shader
);
656 SHADER_INIT( tilemap_shader
);
657 SHADER_INIT( fish_shader
);
662 glGenVertexArrays( 1, &tile_vao
);
663 glGenBuffers( 1, &tile_vbo
);
667 0.0f
, 0.0f
, 0.0f
, 1.0f
, 1.0f
, 1.0f
,
668 0.0f
, 0.0f
, 1.0f
, 1.0f
, 1.0f
, 0.0f
,
671 0.0f
, 0.0f
, 0.0f
, 0.0f
674 glBindVertexArray( tile_vao
);
675 glBindBuffer( GL_ARRAY_BUFFER
, tile_vbo
);
684 glVertexAttribPointer( 0, 2, GL_FLOAT
, GL_FALSE
, 2 * sizeof(float), (void*)0 );
685 glEnableVertexAttribArray( 0 );
689 // Create map buffers
690 glGenVertexArrays( 1, &map
.tiles_vao
);
691 glGenBuffers( 1, &map
.tiles_vbo
);
693 glBindVertexArray( map
.tiles_vao
);
694 glBindBuffer( GL_ARRAY_BUFFER
, map
.tiles_vbo
);
695 glBufferData( GL_ARRAY_BUFFER
,
696 sizeof( quad_mesh
) +
697 sizeof( float )*2 * 1024 +
698 sizeof( u8
)*4 * 1024,
703 glBufferSubData( GL_ARRAY_BUFFER
, 0, sizeof( quad_mesh
), quad_mesh
);
706 glVertexAttribPointer( 0, 2, GL_FLOAT
, GL_FALSE
, 2*sizeof(float), (void*)0 );
707 glEnableVertexAttribArray( 0 );
709 // Offset, data arrays (instancing)
710 glVertexAttribPointer( 1, 2, GL_FLOAT
, GL_FALSE
, 2*sizeof(float), (void*)(sizeof(quad_mesh
)) );
711 glEnableVertexAttribArray( 1 );
712 glVertexAttribDivisor( 1, 1 );
714 glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE
, GL_FALSE
, 4, (void*)(sizeof(quad_mesh
)+sizeof(float)*2*1024) );
715 glEnableVertexAttribArray( 2 );
716 glVertexAttribDivisor( 2, 1 );
718 map
.tile_texture
= vg_tex2d_rgba( "textures/rivertiles_flowm.tga" );
722 map
.flow_texture
= vg_tex2d_rgba( "textures/rivertiles_ripple.tga" );
726 fish_texture
= vg_tex2d_rgba( "textures/fishe_swim.tga" );
732 "##-#####-##;aaa,aa\n"
739 "##+#####+##;aa,aaa\n"
747 glDeleteVertexArrays( 1, &tile_vao
);
748 glDeleteVertexArrays( 1, &map
.tiles_vao
);
750 glDeleteBuffers( 1, &tile_vbo
);
751 glDeleteBuffers( 1, &map
.tiles_vbo
);
753 glDeleteTextures( 1, &map
.tile_texture
);
754 glDeleteTextures( 1, &map
.flow_texture
);