[VID] flowing water
[fishladder.git] / fishladder.c
1 // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
2
3 //#define VG_STEAM
4 #include "vg/vg.h"
5
6 SHADER_DEFINE( shader_tile_colour,
7
8 // VERTEX
9 "layout (location=0) in vec2 a_co;"
10 "uniform mat3 uPv;"
11 "uniform vec2 uOffset;"
12 ""
13 "void main()"
14 "{"
15 "gl_Position = vec4( uPv * vec3( a_co + uOffset, 1.0 ), 1.0 );"
16 "}",
17
18 // FRAGMENT
19 "out vec4 FragColor;"
20 "uniform vec4 uColour;"
21 ""
22 "void main()"
23 "{"
24 "FragColor = uColour;"
25 "}"
26 ,
27 UNIFORMS({ "uPv", "uOffset", "uColour" })
28 )
29
30 m3x3f m_projection;
31 m3x3f m_view;
32 m3x3f m_mdl;
33
34 #define FLAG_INPUT 0x1
35 #define FLAG_OUTPUT 0x2
36 #define FLAG_CANAL 0x4
37 #define FLAG_WALL 0x8
38
39 struct world
40 {
41 struct cell
42 {
43 u32 state;
44 u8 water[2];
45 }
46 *data;
47
48 u32 frame;
49
50 struct cell_terminal
51 {
52 char *conditions;
53 int id;
54 }
55 *io;
56
57 u32 w, h;
58
59 GLuint tile_vao;
60 GLuint tile_vbo;
61
62 int selected;
63 } world = {};
64
65 static void map_free(void)
66 {
67 for( int i = 0; i < arrlen( world.io ); i ++ )
68 arrfree( world.io[ i ].conditions );
69
70 arrfree( world.data );
71 arrfree( world.io );
72
73 world.w = 0;
74 world.h = 0;
75 world.data = NULL;
76 world.io = NULL;
77 }
78
79 static int map_load( const char *str )
80 {
81 map_free();
82
83 char const *c = str;
84
85 // Scan for width
86 for(;; world.w ++)
87 {
88 if( str[world.w] == ';' )
89 break;
90 else if( !str[world.w] )
91 {
92 vg_error( "Unexpected EOF when parsing level\n" );
93 return 0;
94 }
95 }
96
97 struct cell *row = arraddnptr( world.data, world.w );
98 int cx = 0;
99 int reg_start = 0, reg_end = 0;
100
101 for(;;)
102 {
103 if( !*c )
104 break;
105
106 if( *c == ';' )
107 {
108 c ++;
109
110 // Parse attribs
111 if( *c != '\n' )
112 {
113 while( *c )
114 {
115 if( reg_start < reg_end )
116 {
117 if( *c >= 'a' && *c <= 'z' )
118 {
119 arrpush( world.io[ reg_start ].conditions, *c );
120 }
121 else
122 {
123 if( *c == ',' || *c == '\n' )
124 {
125 reg_start ++;
126
127 if( *c == '\n' )
128 break;
129 }
130 else
131 {
132 vg_error( "Unkown attribute '%c' (row: %u)\n", *c, world.h );
133 return 0;
134 }
135 }
136 }
137 else
138 {
139 vg_error( "Too many values to assign (row: %u)\n", world.h );
140 return 0;
141 }
142
143 c ++;
144 }
145 }
146
147 if( reg_start != reg_end )
148 {
149 vg_error( "Not enough values assigned (row: %u, %u of %u)\n", world.h, reg_start, reg_end );
150 return 0;
151 }
152
153 if( cx != world.w )
154 {
155 vg_error( "Not enough cells to match previous row definition (row: %u, %u<%u)\n", world.h, cx, world.w );
156 return 0;
157 }
158
159 row = arraddnptr( world.data, world.w );
160 cx = 0;
161 world.h ++;
162 reg_end = reg_start = arrlen( world.io );
163 }
164 else
165 {
166 if( cx == world.w )
167 {
168 vg_error( "Too many cells to match previous row definition (row: %u, %u>%u)\n", world.h, cx, world.w );
169 return 0;
170 }
171
172 // Tile initialization
173 // row[ cx ] .. etc
174 row[ cx ].water[0] = 0;
175 row[ cx ].water[1] = 0;
176
177 if( *c == '+' || *c == '-' )
178 {
179 struct cell_terminal term = { .id = cx + world.h*world.w };
180 arrpush( world.io, term );
181 row[ cx ++ ].state = *c == '+'? FLAG_INPUT: FLAG_OUTPUT;
182 reg_end ++;
183 }
184 else if( *c == '#' )
185 {
186 row[ cx ++ ].state = FLAG_WALL;
187 }
188 else
189 {
190 row[ cx ++ ].state = 0x00;
191 }
192 }
193
194 c ++;
195 }
196
197 vg_success( "Map loaded! (%u:%u)\n", world.w, world.h );
198 return 1;
199 }
200
201 int main( int argc, char *argv[] )
202 {
203 vg_init( argc, argv, "FishLadder" );
204 }
205
206 void vg_register(void)
207 {
208 SHADER_INIT( shader_tile_colour );
209 }
210
211 void vg_start(void)
212 {
213 glGenVertexArrays( 1, &world.tile_vao );
214 glGenBuffers( 1, &world.tile_vbo );
215
216 float quad_mesh[] =
217 {
218 0.05f, 0.05f, 0.05f, 0.95f, 0.95f, 0.95f,
219 0.05f, 0.05f, 0.95f, 0.95f, 0.95f, 0.05f
220 };
221
222 glBindVertexArray( world.tile_vao );
223 glBindBuffer( GL_ARRAY_BUFFER, world.tile_vbo );
224 glBufferData
225 (
226 GL_ARRAY_BUFFER,
227 sizeof( quad_mesh ),
228 quad_mesh,
229 GL_STATIC_DRAW
230 );
231
232 glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0 );
233 glEnableVertexAttribArray( 0 );
234
235 VG_CHECK_GL();
236
237 map_load
238 (
239 "#############;\n"
240 "###-#####-###;aaa,aa\n"
241 "## ##;\n"
242 "## ##;\n"
243 "## ##;\n"
244 "## ##;\n"
245 "## ##;\n"
246 "## ##;\n"
247 "###+#####+###;aa,aaa\n"
248 "#############;\n"
249 );
250 }
251
252 void vg_free(void)
253 {
254 glDeleteVertexArrays( 1, &world.tile_vao );
255 glDeleteBuffers( 1, &world.tile_vbo );
256
257 map_free();
258 }
259
260 void vg_update(void)
261 {
262 float ratio = (float)vg_window_y / (float)vg_window_x;
263 float const size = 9.5f;
264
265 v3f origin;
266 origin[0] = -0.5f * world.w;
267 origin[1] = -0.5f * world.h;
268 origin[2] = 0.0f;
269
270 m3x3_projection( m_projection, -size, size, size*ratio, -size*ratio );
271 m3x3_identity( m_view );
272 m3x3_translate( m_view, origin );
273 m3x3_mul( m_projection, m_view, vg_pv );
274 vg_projection_update();
275
276 v2f tile_pos;
277 v2_copy( vg_mouse_ws, tile_pos );
278
279 int tile_x = floorf( tile_pos[0] );
280 int tile_y = floorf( tile_pos[1] );
281
282 if( tile_x >= 2 && tile_x < world.w-2 && tile_y >= 2 && tile_y <= world.h-2 )
283 {
284 world.selected = tile_y * world.w + tile_x;
285
286 struct cell *cell = &world.data[tile_y*world.w+tile_x];
287 if( cell->state & (FLAG_WALL|FLAG_INPUT|FLAG_OUTPUT) )
288 {
289 world.selected = -1;
290 }
291 else
292 {
293 if( vg_get_button_down("primary") )
294 {
295 cell->state ^= FLAG_CANAL;
296 }
297 }
298 }
299 else
300 world.selected = -1;
301
302 // Simulate world
303 static int update_tick = 0;
304 update_tick ++;
305
306 if( update_tick > 5 )
307 {
308 update_tick = 0;
309
310 u32 buffer_id = world.frame & 0x1;
311 u32 buffer_next = buffer_id ^ 0x1;
312
313 for( int y = 1; y < world.h-1; y ++ )
314 {
315 for( int x = 1; x < world.w-1; x ++ )
316 {
317 struct cell *cell = &world.data[y*world.w+x];
318
319 if( cell->state & FLAG_INPUT )
320 cell->water[ buffer_next ] = 8;
321 else
322 {
323 int has_source = 0;
324 cell->water[ buffer_next ] = 0;
325
326 if( cell->state & FLAG_CANAL )
327 {
328 v2i dirs[] = {{1,0},{0,1},{-1,0},{0,-1}};
329
330 for( int i = 0; i < vg_list_size( dirs ); i ++ )
331 {
332 struct cell *neighbour = &world.data[(y+dirs[i][1])*world.w+x+dirs[i][0]];
333
334 if( neighbour->water[ buffer_id ] > cell->water[ buffer_next ]+1 )
335 {
336 has_source = 1;
337 cell->water[ buffer_next ] = neighbour->water[ buffer_id ]-1;
338 }
339 }
340 }
341
342 if( !has_source && cell->water[ buffer_id ] )
343 cell->water[ buffer_next ] = cell->water[ buffer_id ]-1;
344 }
345 }
346 }
347
348 world.frame ++;
349 }
350 }
351
352 void vg_render(void)
353 {
354 glViewport( 0,0, vg_window_x, vg_window_y );
355
356 glDisable( GL_DEPTH_TEST );
357 glClearColor( 0.01f, 0.01f, 0.01f, 1.0f );
358 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
359
360 glBindVertexArray( world.tile_vao );
361 SHADER_USE( shader_tile_colour );
362 glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_colour, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
363
364 for( int y = 0; y < world.h; y ++ )
365 {
366 for( int x = 0; x < world.w; x ++ )
367 {
368 glUniform2f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)x, (float)y );
369
370 v4f colour;
371
372 struct cell *cell = &world.data[y*world.w+x];
373
374 if( cell->state & FLAG_WALL ) { v4_copy( (v4f){ 0.2f, 0.2f, 0.2f, 1.0f }, colour ); }
375 else if( cell->state & FLAG_CANAL ) { v4_copy( (v4f){ 0.6f, 0.6f, 0.6f, 1.0f }, colour ); }
376 else if( cell->state & FLAG_INPUT ) { v4_copy( (v4f){ 0.2f, 0.3f, 0.7f, 1.0f }, colour ); }
377 else if( cell->state & FLAG_OUTPUT ) { v4_copy( (v4f){ 0.2f, 0.7f, 0.3f, 1.0f }, colour ); }
378 else v4_copy( (v4f){ 0.9f, 0.9f, 0.9f, 1.0f }, colour );
379
380 if( cell->water[world.frame&0x1] )
381 v4_copy( (v4f){ 0.2f, 0.3f, 0.7f * (float)(cell->water[world.frame&0x1]) * (1.0f/8.0f), 1.0f }, colour );
382
383 if( world.selected == y*world.w + x )
384 v3_muls( colour, sinf( vg_time )*0.25f + 0.5f, colour );
385
386 glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, colour );
387
388 glDrawArrays( GL_TRIANGLES, 0, 6 );
389 }
390 }
391 }
392
393 void vg_ui(void){}