map loader
[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 }
45 *data;
46
47 struct cell_terminal
48 {
49 char *conditions;
50 int id;
51 }
52 *io;
53
54 u32 w, h;
55
56 GLuint tile_vao;
57 GLuint tile_vbo;
58
59 int selected;
60 } world = {};
61
62 static void map_free(void)
63 {
64 for( int i = 0; i < arrlen( world.io ); i ++ )
65 arrfree( world.io[ i ].conditions );
66
67 arrfree( world.data );
68 arrfree( world.io );
69
70 world.w = 0;
71 world.h = 0;
72 world.data = NULL;
73 world.io = NULL;
74 }
75
76 static int map_load( const char *str )
77 {
78 map_free();
79
80 char const *c = str;
81
82 // Scan for width
83 for(;; world.w ++)
84 {
85 if( str[world.w] == ';' )
86 break;
87 else if( !str[world.w] )
88 {
89 vg_error( "Unexpected EOF when parsing level\n" );
90 return 0;
91 }
92 }
93
94 struct cell *row = arraddnptr( world.data, world.w );
95 int cx = 0;
96 int reg_start = 0, reg_end = 0;
97
98 for(;;)
99 {
100 if( !*c )
101 break;
102
103 if( *c == ';' )
104 {
105 c ++;
106
107 // Parse attribs
108 if( *c != '\n' )
109 {
110 while( *c )
111 {
112 if( reg_start < reg_end )
113 {
114 if( *c >= 'a' && *c <= 'z' )
115 {
116 arrpush( world.io[ reg_start ].conditions, *c );
117 }
118 else
119 {
120 if( *c == ',' || *c == '\n' )
121 {
122 reg_start ++;
123
124 if( *c == '\n' )
125 break;
126 }
127 else
128 {
129 vg_error( "Unkown attribute '%c' (row: %u)\n", *c, world.h );
130 return 0;
131 }
132 }
133 }
134 else
135 {
136 vg_error( "Too many values to assign (row: %u)\n", world.h );
137 return 0;
138 }
139
140 c ++;
141 }
142 }
143
144 if( reg_start != reg_end )
145 {
146 vg_error( "Not enough values assigned (row: %u, %u of %u)\n", world.h, reg_start, reg_end );
147 return 0;
148 }
149
150 if( cx != world.w )
151 {
152 vg_error( "Not enough cells to match previous row definition (row: %u, %u<%u)\n", world.h, cx, world.w );
153 return 0;
154 }
155
156 row = arraddnptr( world.data, world.w );
157 cx = 0;
158 world.h ++;
159 reg_end = reg_start = arrlen( world.io );
160 }
161 else
162 {
163 if( cx == world.w )
164 {
165 vg_error( "Too many cells to match previous row definition (row: %u, %u>%u)\n", world.h, cx, world.w );
166 return 0;
167 }
168
169 // Tile initialization
170 // row[ cx ] .. etc
171
172 if( *c == '+' || *c == '-' )
173 {
174 struct cell_terminal term = { .id = cx + world.h*world.w };
175 arrpush( world.io, term );
176 row[ cx ++ ].state = *c == '+'? FLAG_INPUT: FLAG_OUTPUT;
177 reg_end ++;
178 }
179 else if( *c == '#' )
180 {
181 row[ cx ++ ].state = FLAG_WALL;
182 }
183 else
184 {
185 row[ cx ++ ].state = 0x00;
186 }
187 }
188
189 c ++;
190 }
191
192 vg_success( "Map loaded! (%u:%u)\n", world.w, world.h );
193 return 1;
194 }
195
196 int main( int argc, char *argv[] )
197 {
198 vg_init( argc, argv, "FishLadder" );
199 }
200
201 void vg_register(void)
202 {
203 SHADER_INIT( shader_tile_colour );
204 }
205
206 void vg_start(void)
207 {
208 glGenVertexArrays( 1, &world.tile_vao );
209 glGenBuffers( 1, &world.tile_vbo );
210
211 float quad_mesh[] =
212 {
213 0.05f, 0.05f, 0.05f, 0.95f, 0.95f, 0.95f,
214 0.05f, 0.05f, 0.95f, 0.95f, 0.95f, 0.05f
215 };
216
217 glBindVertexArray( world.tile_vao );
218 glBindBuffer( GL_ARRAY_BUFFER, world.tile_vbo );
219 glBufferData
220 (
221 GL_ARRAY_BUFFER,
222 sizeof( quad_mesh ),
223 quad_mesh,
224 GL_STATIC_DRAW
225 );
226
227 glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0 );
228 glEnableVertexAttribArray( 0 );
229
230 VG_CHECK_GL();
231
232 map_load
233 (
234 "#############;\n"
235 "###-#####-###;aaa,aa\n"
236 "## ##;\n"
237 "## ##;\n"
238 "## ##;\n"
239 "## ##;\n"
240 "## ##;\n"
241 "## ##;\n"
242 "###+#####+###;aa,aaa\n"
243 "#############;\n"
244 );
245 }
246
247 void vg_free(void)
248 {
249 glDeleteVertexArrays( 1, &world.tile_vao );
250 glDeleteBuffers( 1, &world.tile_vbo );
251
252 map_free();
253 }
254
255 void vg_update(void)
256 {
257 float ratio = (float)vg_window_y / (float)vg_window_x;
258 float const size = 9.5f;
259
260 v3f origin;
261 origin[0] = -0.5f * world.w;
262 origin[1] = -0.5f * world.h;
263 origin[2] = 0.0f;
264
265 m3x3_projection( m_projection, -size, size, size*ratio, -size*ratio );
266 m3x3_identity( m_view );
267 m3x3_translate( m_view, origin );
268 m3x3_mul( m_projection, m_view, vg_pv );
269 vg_projection_update();
270
271 v2f tile_pos;
272 v2_copy( vg_mouse_ws, tile_pos );
273
274 int tile_x = floorf( tile_pos[0] );
275 int tile_y = floorf( tile_pos[1] );
276
277 if( tile_x >= 0 && tile_x < world.w && tile_y >= 0 && tile_y <= world.h )
278 world.selected = tile_y * world.h + tile_x;
279 else
280 world.selected = -1;
281 }
282
283 void vg_render(void)
284 {
285 glViewport( 0,0, vg_window_x, vg_window_y );
286
287 glDisable( GL_DEPTH_TEST );
288 glClearColor( 0.01f, 0.01f, 0.01f, 1.0f );
289 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
290
291 glBindVertexArray( world.tile_vao );
292 SHADER_USE( shader_tile_colour );
293 glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_colour, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
294
295 for( int y = 0; y < world.h; y ++ )
296 {
297 for( int x = 0; x < world.w; x ++ )
298 {
299 glUniform2f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)x, (float)y );
300
301 v4f colour;
302
303 struct cell *cell = &world.data[y*world.h+x];
304
305 if( cell->state & FLAG_WALL ) { v4_copy( (v4f){ 0.4f, 0.4f, 0.4f, 1.0f }, colour ); }
306 else if( cell->state & FLAG_CANAL ) { v4_copy( (v4f){ 0.6f, 0.6f, 0.6f, 1.0f }, colour ); }
307 else if( cell->state & FLAG_INPUT ) { v4_copy( (v4f){ 0.2f, 0.3f, 0.7f, 1.0f }, colour ); }
308 else if( cell->state & FLAG_OUTPUT ) { v4_copy( (v4f){ 0.2f, 0.7f, 0.3f, 1.0f }, colour ); }
309 else v4_copy( (v4f){ 1.0f, 0.0f, 0.0f, 1.0f }, colour );
310
311 if( world.selected == y*world.h + x )
312 v3_muls( colour, sinf( vg_time )*0.25f + 0.5f, colour );
313
314 glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, colour );
315
316 glDrawArrays( GL_TRIANGLES, 0, 6 );
317 }
318 }
319 }
320
321 void vg_ui(void){}