basic tile editing
[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( colour_shader,
7
8 // VERTEX
9 "layout (location=0) in vec3 a_co;"
10 "uniform mat4 uPv;"
11 "uniform mat4 uMdl;"
12 ""
13 "void main()"
14 "{"
15 " vec4 vert_pos = uPv * uMdl * vec4( a_co, 1.0 );"
16 " gl_Position = vert_pos;"
17 "}",
18
19 // FRAGMENT
20 "out vec4 FragColor;"
21 "uniform vec4 uColour;"
22 ""
23 "void main()"
24 "{"
25 " FragColor = uColour;"
26 "}"
27 ,
28 UNIFORMS({ "uPv", "uMdl", "uColour" })
29 )
30
31 mat4 m_projection;
32 mat4 m_view;
33 mat4 m_mdl;
34
35 int main( int argc, char *argv[] )
36 {
37 vg_init( argc, argv, "FishLadder" );
38 }
39
40 #define CELL_FLAG_INPUT 0x1
41 #define CELL_FLAG_OUTPUT 0x2
42 #define CELL_FLAG_IO (CELL_FLAG_INPUT|CELL_FLAG_OUTPUT)
43 #define CELL_FLAG_WALL 0x4
44 #define CELL_FLAG_HOVER 0x8
45 #define CELL_FLAG_ITER 0x10
46 #define CELL_FLAG_CANAL 0x20
47 #define CELL_FLAG_CONNECTOR 0x40 /* Does this cell split and have an incoming vertical connection? */
48
49 static struct
50 {
51 u32 x,y;
52
53 struct cell
54 {
55 u32 flags;
56 u32 model_id;
57
58 char *conditions;
59
60 int level;
61 int diff[2];
62 }
63 * cells;
64
65 vec3 origin;
66 struct cell *selected;
67 int select_valid;
68 u32 frame;
69
70 u32 *io;
71
72 struct vstack
73 {
74 struct vframe
75 {
76 int x, y;
77 int i;
78 }
79 frames[ 64 ];
80
81 int level;
82 u32 flags;
83 }
84 stack;
85 }
86 map;
87
88 static void map_free(void)
89 {
90 for( int i = 0; i < arrlen( map.io ); i ++ )
91 {
92 arrfree( map.cells[ map.io[i] ].conditions );
93 }
94
95 arrfree( map.cells );
96 arrfree( map.io );
97 map.x = 0;
98 map.y = 0;
99 map.cells = NULL;
100 map.io = NULL;
101 }
102
103 static struct cell *map_tile_at( int pos[2] )
104 {
105 if( pos[0] >= 0 && pos[0] < map.x && pos[1] >= 0 && pos[1] < map.y )
106 return map.cells + pos[1]*map.x + pos[0];
107 return NULL;
108 }
109
110 static int map_load( const char *str )
111 {
112 map_free();
113
114 char *c = str;
115
116 // Scan for width
117 for(;; map.x ++)
118 {
119 if( str[map.x] == ';' )
120 break;
121 else if( !str[map.x] )
122 {
123 vg_error( "Unexpected EOF when parsing level!\n" );
124 return 0;
125 }
126 }
127
128 struct cell *row = arraddnptr( map.cells, map.x );
129 int cx = 0;
130 int reg_start = 0, reg_end = 0;
131
132 for(;;)
133 {
134 if( !*c )
135 break;
136
137 if( *c == ';' )
138 {
139 c ++;
140
141 // Parse attribs
142 if( *c != '\n' )
143 {
144 while( *c )
145 {
146 if( reg_start < reg_end )
147 {
148 if( *c >= 'a' && *c <= 'z' )
149 {
150 arrpush( map.cells[ map.io[ reg_start ] ].conditions, *c );
151 }
152 else
153 {
154 if( *c == ',' || *c == '\n' )
155 {
156 reg_start ++;
157
158 if( *c == '\n' )
159 break;
160 }
161 else
162 {
163 vg_error( "Unkown attrib '%c' (row: %u)\n", *c, map.y );
164 return 0;
165 }
166 }
167 }
168 else
169 {
170 vg_error( "Over-assigned values (row: %u)\n", map.y );
171 return 0;
172 }
173
174 c ++;
175 }
176 }
177
178 if( reg_start != reg_end )
179 {
180 vg_error( "Not enough values assigned (row: %u, %u of %u)\n", map.y, reg_start, reg_end );
181 return 0;
182 }
183
184 if( cx != map.x )
185 {
186 vg_error( "Map row underflow (row: %u, %u<%u)\n", map.y, cx, map.x );
187 return 0;
188 }
189
190 row = arraddnptr( map.cells, map.x );
191 cx = 0;
192 map.y ++;
193 reg_end = reg_start = arrlen( map.io );
194 }
195 else
196 {
197 if( cx == map.x )
198 {
199 vg_error( "Map row overflow (row: %u, %u>%u)\n", map.y, cx, map.x );
200 return 0;
201 }
202
203 row[ cx ].conditions = NULL;
204
205 // Parse the various cell types
206 if( *c == '+' || *c == '-' )
207 {
208 arrpush( map.io, cx + map.y*map.x );
209 row[ cx ++ ].flags = *c == '+'? CELL_FLAG_INPUT: CELL_FLAG_OUTPUT;
210 reg_end ++;
211 }
212 else if( *c == '#' )
213 {
214 row[ cx ++ ].flags = CELL_FLAG_WALL;
215 }
216 else
217 {
218 row[ cx ++ ].flags = 0x00;
219 }
220 }
221
222 c ++;
223 }
224
225 // Origin top left corner
226 map.origin[0] = -((float)map.x) * 0.5f;
227 map.origin[2] = -((float)map.y) * 0.5f;
228
229 vg_success( "Map loaded! (%u:%u)\n", map.x, map.y );
230 return 1;
231 }
232
233 void vg_update(void)
234 {
235 // Update camera
236 float ratio = (float)vg_window_y / (float)vg_window_x;
237 float const size = 7.5f;
238 glm_ortho( -size, size, -size*ratio, size*ratio, 0.1f, 100.f, m_projection );
239
240 glm_mat4_identity( m_view );
241 glm_translate_z( m_view, -10.f );
242 glm_rotate_x( m_view, 1.0f, m_view );
243
244 glm_mat4_mul( m_projection, m_view, vg_pv );
245
246 // Compute map update
247 map.frame ^= 0x1;
248 for( int y = 0; y < map.y; y ++ )
249 {
250 for( int x = 0; x < map.x; x ++ )
251 {
252 // Cell is a connector if it has at least 3 connections
253 int output_dirs[][2] = { {0,-1}, {-1,0}, {1,0}, {0,1} };
254 u32 output_count = 0;
255 struct cell *tile, *thistile;
256 thistile = map_tile_at( (int [2]){x,y} );
257
258 if( thistile->flags & CELL_FLAG_CANAL )
259 {
260 for( int i = 0; i < vg_list_size( output_dirs ); i ++ )
261 {
262 tile = map_tile_at( (int [2]){ x+output_dirs[i][0], y+output_dirs[i][1] } );
263
264 if( tile && tile->flags & CELL_FLAG_CANAL )
265 output_count ++;
266 }
267
268 if( output_count >= 3 )
269 thistile->flags |= CELL_FLAG_CONNECTOR;
270 else
271 thistile->flags &= ~CELL_FLAG_CONNECTOR;
272 }
273 }
274 }
275
276 // Get mouse ray
277 vec3 ray_origin;
278 vec3 ray_dir;
279
280 mat4 pv_inverse;
281 vec4 vp = { 0.f, 0.f, vg_window_x, vg_window_y };
282 glm_mat4_inv( vg_pv, pv_inverse );
283 glm_unprojecti( (vec3){ vg_mouse_x, vg_window_y-vg_mouse_y, -1.f }, pv_inverse, vp, ray_dir );
284 glm_unprojecti( (vec3){ vg_mouse_x, vg_window_y-vg_mouse_y, 0.f }, pv_inverse, vp, ray_origin );
285 glm_vec3_sub( ray_dir, ray_origin, ray_dir );
286
287 // Get floor tile intersection
288 float ray_t = -ray_origin[1] / ray_dir[1];
289
290 vec3 tile_pos;
291 glm_vec3_copy( ray_origin, tile_pos );
292 glm_vec3_muladds( ray_dir, ray_t, tile_pos );
293 glm_vec3_sub( tile_pos, map.origin, tile_pos );
294
295 int tile_x = floorf( tile_pos[0] );
296 int tile_y = floorf( tile_pos[2] );
297
298 map.selected = map_tile_at( (int [2]){tile_x, tile_y} );
299
300 if( map.selected )
301 {
302 // Check if valid
303 int validation[][2] = { {1,1}, {-1,1}, {-1,-1}, {1,-1} };
304 struct cell *a, *b, *c;
305
306 map.select_valid = 1;
307 for( int i = 0; i < vg_list_size( validation ); i ++ )
308 {
309 a = map_tile_at( (int [2]){ tile_x+validation[i][0], tile_y } );
310 b = map_tile_at( (int [2]){ tile_x, tile_y+validation[i][1] } );
311
312 if( a && b && (a->flags & b->flags & CELL_FLAG_CANAL) )
313 {
314 c = map_tile_at( (int [2]){ tile_x+validation[i][0], tile_y+validation[i][1] } );
315
316 if( c && (c->flags & CELL_FLAG_CANAL ) )
317 {
318 map.select_valid = 0;
319 break;
320 }
321 }
322 }
323
324 if( map.select_valid )
325 {
326 if( vg_get_button_down("primary") )
327 {
328 if( map.selected->flags & CELL_FLAG_CANAL )
329 {
330 map.selected->flags &= ~(CELL_FLAG_CANAL | CELL_FLAG_CONNECTOR);
331 }
332 else
333 {
334 map.selected->flags |= CELL_FLAG_CANAL;
335 }
336 }
337 }
338 }
339 }
340
341 GLuint tile_vao;
342 GLuint tile_vbo;
343
344 void vg_render(void)
345 {
346 glViewport( 0,0, vg_window_x, vg_window_y );
347
348 glEnable( GL_DEPTH_TEST );
349 glClearColor( 0.94f, 0.94f, 0.94f, 1.0f );
350 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
351
352 glBindVertexArray( tile_vao );
353
354 SHADER_USE( colour_shader );
355 glUniformMatrix4fv( SHADER_UNIFORM( colour_shader, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
356
357 for( int y = 0; y < map.y; y ++ )
358 {
359 for( int x = 0; x < map.x; x ++ )
360 {
361 glm_mat4_identity( m_mdl );
362 glm_translate( m_mdl,
363 (vec3){
364 map.origin[0] + (float)x + 0.5f,
365 0.f,
366 map.origin[2] + (float)y + 0.5f
367 }
368 );
369 glUniformMatrix4fv( SHADER_UNIFORM( colour_shader, "uMdl" ), 1, GL_FALSE, (float *)m_mdl );
370
371 struct cell *cell = &map.cells[ y*map.x+x ];
372
373 vec4 colour = { 0.7f, 0.7f, 0.7f, 1.f };
374
375 if( cell->flags & CELL_FLAG_INPUT ) glm_vec3_copy( (vec3){ 0.9f,0.5f,0.5f }, colour );
376 else if( cell->flags & CELL_FLAG_OUTPUT ) glm_vec3_copy( (vec3){ 0.5f,0.9f,0.5f }, colour );
377 else if( cell->flags & CELL_FLAG_WALL ) glm_vec3_copy( (vec3){ 0.1f,0.1f,0.1f }, colour );
378 else if( cell->flags & CELL_FLAG_CANAL ) glm_vec3_copy( (vec3){ 0.5f,0.5f,0.8f }, colour );
379
380 if( cell->flags & CELL_FLAG_CONNECTOR )
381 glm_vec3_copy( (vec3){ 0.6f, 0.f, 0.9f }, colour );
382
383 if( map.selected == cell )
384 {
385 if( !map.select_valid )
386 glm_vec3_copy( (vec3){ 1.f, 0.f, 0.f }, colour );
387
388 float flash = sinf( vg_time*2.5f ) * 0.25f + 0.75f;
389 glm_vec3_scale( colour, flash, colour );
390 }
391
392 glUniform4fv( SHADER_UNIFORM( colour_shader, "uColour" ), 1, colour );
393 glDrawArrays( GL_TRIANGLES, 0, 6 );
394 }
395 }
396 }
397
398 void vg_start(void)
399 {
400 SHADER_INIT( colour_shader );
401
402 glGenVertexArrays( 1, &tile_vao );
403 glGenBuffers( 1, &tile_vbo );
404
405 float quad_mesh[] =
406 {
407 -0.5f, 0.f, -0.5f,
408 -0.5f, 0.f, 0.5f,
409 0.5f, 0.f, 0.5f,
410 -0.5f, 0.f, -0.5f,
411 0.5f, 0.f, 0.5f,
412 0.5f, 0.f, -0.5f
413 };
414
415 glBindVertexArray( tile_vao );
416 glBindBuffer( GL_ARRAY_BUFFER, tile_vbo );
417 glBufferData
418 (
419 GL_ARRAY_BUFFER,
420 sizeof( quad_mesh ),
421 quad_mesh,
422 GL_STATIC_DRAW
423 );
424
425 glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0 );
426 glEnableVertexAttribArray( 0 );
427
428 VG_CHECK_GL();
429
430 map_load
431 (
432 "#####-#####;aa\n"
433 "# #;\n"
434 "# #;\n"
435 "# -;bb\n"
436 "# #;\n"
437 "# #;\n"
438 "#####+#####;abab\n"
439 );
440 }
441
442 void vg_free(void)
443 {
444 map_free();
445 }
446
447 void vg_ui(void)
448 {
449
450 }