45120fd8b03b0740d234459f876e043da28e345a
[fishladder.git] / vg / vg_ui.h
1 // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
2
3 SHADER_DEFINE( shader_ui,
4
5 // VERTEX
6 "layout (location=0) in vec2 a_co;" // i16, i16, .. ?
7 "layout (location=1) in vec2 a_uv;" // i8, i8
8 "layout (location=2) in vec4 a_colour;" // u32
9 "uniform mat3 uPv;"
10 ""
11 "out vec2 aTexCoords;"
12 "out vec4 aColour;"
13 ""
14 "void main()"
15 "{"
16 "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
17 "aTexCoords = a_uv * 0.01388888888;"
18 "aColour = a_colour;"
19 "}",
20
21 // FRAGMENT
22 "uniform sampler2D uTexGlyphs;"
23 "out vec4 FragColor;"
24 ""
25 "in vec2 aTexCoords;"
26 "in vec4 aColour;"
27 ""
28 "void main()"
29 "{"
30 "vec4 glyph = texture( uTexGlyphs, aTexCoords );"
31 "FragColor = aColour * vec4( 1.0, 1.0, 1.0, glyph.r );"
32 "}"
33 ,
34 UNIFORMS({ "uPv", "uTexGlyphs" })
35 )
36
37 #define UI_AUTO_FILL 0
38 #define UI_DEBUG
39
40 // Types
41 // ===========================================================================================================
42
43 typedef i16 ui_px;
44 typedef u32 ui_colour;
45 typedef ui_px ui_rect[4];
46 typedef struct ui_ctx ui_ctx;
47
48 struct ui_ctx
49 {
50 ui_px padding;
51
52 struct ui_qnode
53 {
54 ui_rect rect;
55 ui_colour colour;
56 int mouse_over;
57 int capture_id;
58 }
59 stack[ 32 ];
60
61 #pragma pack(push,1)
62 struct ui_vert
63 {
64 ui_px co[2];
65 u8 uv[2];
66 u32 colour;
67 }
68 *verts;
69 #pragma pack(pop)
70
71 u32 num_verts;
72 u16 *indices;
73 u32 num_indices;
74
75 ui_rect cursor;
76 u32 stack_count;
77 u32 capture_mouse_id;
78 int capture_lock;
79 u32 id_base;
80
81 // User input
82 ui_px mouse[2];
83 int click_state; // 0: released, 1: on down, 2: pressed, 3: on release
84 };
85
86 // Globals
87 // ===========================================================================================================
88
89 // Opengl
90 GLuint ui_glyph_texture;
91 GLuint ui_vao;
92 GLuint ui_vbo;
93 GLuint ui_ebo;
94
95 #define UI_BUFFER_SIZE 30000
96 #define UI_INDEX_SIZE 20000
97
98 ui_ctx ui_global_ctx = { .padding = 8 };
99
100
101 // Initialization
102 // ===========================================================================================================
103
104 static void ui_default_init(void)
105 {
106 // Load font
107 {
108 u32 compressed[] = {
109 #include "fonts/weiholmir.h"
110 };
111
112 u32 pixels = 0, total = 72*72, data = 0;
113 u8 *image = malloc( total );
114
115 while( pixels < total )
116 {
117 for( int b = 31; b >= 0; b-- )
118 {
119 image[ pixels ++ ] = (compressed[data] & (0x1 << b))? 0xff: 0x00;
120
121 if( pixels >= total )
122 {
123 total = 0;
124 break;
125 }
126 }
127 data++;
128 }
129
130 glGenTextures( 1, &ui_glyph_texture );
131 glBindTexture( GL_TEXTURE_2D, ui_glyph_texture );
132
133 glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, 72, 72, 0, GL_RED, GL_UNSIGNED_BYTE, image );
134
135 vg_tex2d_clamp();
136 vg_tex2d_nearest();
137
138 free( image );
139 }
140
141 // Setup OpenGL memory
142 {
143 SHADER_INIT( shader_ui );
144
145 // Generate the buffer we are gonna be drawing to
146 glGenVertexArrays(1, &ui_vao);
147 glGenBuffers( 1, &ui_vbo );
148 glGenBuffers( 1, &ui_ebo );
149 glBindVertexArray( ui_vao );
150
151 glBindBuffer( GL_ARRAY_BUFFER, ui_vbo );
152
153 glBufferData( GL_ARRAY_BUFFER, UI_BUFFER_SIZE * sizeof( struct ui_vert ), NULL, GL_DYNAMIC_DRAW );
154 glBindVertexArray( ui_vao );
155
156 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ui_ebo );
157 glBufferData( GL_ELEMENT_ARRAY_BUFFER, UI_INDEX_SIZE * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW );
158
159 u32 const stride = sizeof( struct ui_vert );
160
161 // XY
162 glVertexAttribPointer( 0, 2, GL_UNSIGNED_SHORT, GL_FALSE, stride, (void *)offsetof( struct ui_vert, co ) );
163 glEnableVertexAttribArray( 0 );
164
165 // UV
166 glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE, GL_FALSE, stride, (void *)offsetof( struct ui_vert, uv ) );
167 glEnableVertexAttribArray( 1 );
168
169 // COLOUR
170 glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void *)offsetof( struct ui_vert, colour ) );
171 glEnableVertexAttribArray( 2 );
172 }
173
174 // Initialize default context
175 {
176 ui_global_ctx.verts = (struct ui_vert *)malloc( UI_BUFFER_SIZE * sizeof(struct ui_vert) );
177 ui_global_ctx.indices = (u16*)malloc( UI_INDEX_SIZE * sizeof(u16) );
178 }
179 }
180
181 static void ui_default_free(void)
182 {
183 glDeleteTextures( 1, &ui_glyph_texture );
184
185 glDeleteVertexArrays( 1, &ui_vao );
186 glDeleteBuffers( 1, &ui_vbo );
187 glDeleteBuffers( 1, &ui_ebo );
188
189 free( ui_global_ctx.verts );
190 free( ui_global_ctx.indices );
191 }
192
193 static void ui_draw( ui_ctx *ctx )
194 {
195 glBindVertexArray( ui_vao );
196
197 glBindBuffer( GL_ARRAY_BUFFER, ui_vbo );
198 glBufferSubData( GL_ARRAY_BUFFER, 0, ctx->num_verts * sizeof( struct ui_vert ), ctx->verts );
199
200 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ui_ebo );
201 glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, 0, ctx->num_indices * sizeof( u16 ), ctx->indices );
202
203 glEnable(GL_BLEND);
204 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
205 glBlendEquation(GL_FUNC_ADD);
206
207 SHADER_USE( shader_ui );
208
209 m3x3f view = M3X3_IDENTITY;
210 m3x3_translate( view, (v3f){ -1.0f, 1.0f, 0.0f } );
211 m3x3_scale( view, (v3f){ 1.0f/((float)vg_window_x*0.5f), -1.0f/((float)vg_window_y*0.5f), 1.0f } );
212 glUniformMatrix3fv( SHADER_UNIFORM( shader_ui, "uPv" ), 1, GL_FALSE, (float *)view );
213
214 glActiveTexture( GL_TEXTURE0 );
215 glBindTexture( GL_TEXTURE_2D, ui_glyph_texture );
216 glUniform1i( SHADER_UNIFORM( shader_ui, "uTexGlyphs" ), 0 );
217
218 glDrawElements( GL_TRIANGLES, ctx->num_indices, GL_UNSIGNED_SHORT, (void*)(0) );
219
220 //vg_info( "Verts: %u, Indices: %u\n", ctx->num_verts, ctx->num_indices );
221
222 glDisable(GL_BLEND);
223 }
224
225 // Rect controls
226 // ===========================================================================================================
227
228 static void ui_rect_copy( ui_rect src, ui_rect dst )
229 {
230 dst[0] = src[0];
231 dst[1] = src[1];
232 dst[2] = src[2];
233 dst[3] = src[3];
234 }
235
236 static void ui_rect_pad( ui_rect rect, ui_px pad )
237 {
238 rect[0] += pad;
239 rect[1] += pad;
240 rect[2] -= pad*2;
241 rect[3] -= pad*2;
242 }
243
244 static void ui_vis_rect( ui_rect rect, u32 colour )
245 {
246 #ifdef UI_DEBUG
247 v2f p0;
248 v2f p1;
249
250 p0[0] = rect[0];
251 p0[1] = rect[1];
252 p1[0] = rect[0]+rect[2];
253 p1[1] = rect[1]+rect[3];
254
255 vg_line( p0, (v2f){p1[0],p0[1]}, colour );
256 vg_line( (v2f){p1[0],p0[1]}, p1, colour );
257 vg_line( p1, (v2f){p0[0],p1[1]}, colour );
258 vg_line( (v2f){p0[0],p1[1]}, p0, colour );
259 #endif
260 }
261
262 // Stack control
263 // ===========================================================================================================
264
265 static struct ui_qnode *ui_current( ui_ctx *ctx )
266 {
267 return &ctx->stack[ ctx->stack_count-1 ];
268 }
269
270 static void ui_new_node( ui_ctx *ctx )
271 {
272 if( ctx->stack_count == vg_list_size( ctx->stack ) )
273 vg_exiterr( "[UI] Stack overflow while creating box!" );
274
275 struct ui_qnode *parent = &ctx->stack[ ctx->stack_count-1 ];
276 struct ui_qnode *node = &ctx->stack[ ctx->stack_count++ ];
277 ui_rect_copy( ctx->cursor, node->rect );
278
279 if( parent->mouse_over )
280 {
281 if( ctx->mouse[0] >= node->rect[0] && ctx->mouse[0] <= node->rect[0]+node->rect[2] &&
282 ctx->mouse[1] >= node->rect[1] && ctx->mouse[1] <= node->rect[1]+node->rect[3] )
283 node->mouse_over = 1;
284 else
285 node->mouse_over = 0;
286 }
287 else
288 {
289 node->mouse_over = 0;
290 }
291 }
292
293 static int ui_hasmouse( ui_ctx *ctx )
294 {
295 struct ui_qnode *node = ui_current( ctx );
296 return (node->mouse_over && (node->capture_id == ctx->capture_mouse_id));
297 }
298
299 static void ui_end( ui_ctx *ctx )
300 {
301 struct ui_qnode *node = &ctx->stack[ --ctx->stack_count ];
302
303 ui_rect_copy( node->rect, ctx->cursor );
304 ui_vis_rect( ctx->cursor,
305 (node->mouse_over && (node->capture_id == ctx->capture_mouse_id))? 0xffff0000: 0xff0000ff );
306 }
307
308 static void ui_end_down( ui_ctx *ctx )
309 {
310 ui_px height = ui_current( ctx )->rect[3];
311 ui_end( ctx );
312 ctx->cursor[1] += height;
313 }
314
315 static void ui_end_right( ui_ctx *ctx )
316 {
317 ui_px width = ui_current( ctx )->rect[2];
318 ui_end( ctx );
319 ctx->cursor[0] += width;
320 }
321
322 static void ui_fill_y( ui_ctx *ctx )
323 {
324 struct ui_qnode *node = ui_current( ctx );
325 ctx->cursor[3] = node->rect[3] - (ctx->cursor[1]-node->rect[1]);
326 }
327
328 static void ui_fill_x( ui_ctx *ctx )
329 {
330 struct ui_qnode *node = ui_current( ctx );
331 ctx->cursor[2] = node->rect[2] - (ctx->cursor[0]-node->rect[0]);
332 }
333
334 // Alignment: | [] | -> | []|
335 static void ui_align_bottom( ui_ctx *ctx )
336 {
337 struct ui_qnode *node = ui_current( ctx );
338 ctx->cursor[1] = node->rect[1] + node->rect[3] - ctx->cursor[3];
339 }
340
341 static void ui_align_right( ui_ctx *ctx )
342 {
343 struct ui_qnode *node = ui_current( ctx );
344 ctx->cursor[0] = node->rect[0] + node->rect[2] - ctx->cursor[2];
345 }
346
347 static void ui_align_top( ui_ctx *ctx )
348 {
349 ctx->cursor[1] = ui_current( ctx )->rect[1];
350 }
351
352 static void ui_align_left( ui_ctx *ctx )
353 {
354 ctx->cursor[0] = ui_current( ctx )->rect[0];
355 }
356
357 static void ui_clamp_rect( ui_rect parent, ui_rect dest )
358 {
359 dest[0] = vg_min( parent[0] + parent[2] - dest[2], dest[0] );
360 dest[1] = vg_min( parent[1] + parent[3] - dest[3], dest[1] );
361 dest[0] = vg_max( parent[0], dest[0] );
362 dest[1] = vg_max( parent[1], dest[1] );
363 }
364
365 static u32 ui_group_id( ui_ctx *ctx, u32 lesser_unique )
366 {
367 return ctx->id_base | lesser_unique;
368 }
369
370 static void ui_capture_mouse( ui_ctx *ctx, u32 id )
371 {
372 u32 group_uid = ui_group_id(ctx,id);
373
374 struct ui_qnode *node = &ctx->stack[ ctx->stack_count-1 ];
375 node->capture_id = group_uid;
376
377 if( !ctx->capture_lock && node->mouse_over )
378 {
379 ctx->capture_mouse_id = group_uid;
380 }
381 }
382
383 // Drawing
384 // ===========================================================================================================
385
386 static struct ui_vert *ui_fill_rect_uv( ui_ctx *ctx, ui_rect rect, u32 colour, ui_px uv[4] )
387 {
388 struct ui_vert *vertices = &ctx->verts[ ctx->num_verts ];
389 vertices[0].co[0] = rect[0];
390 vertices[0].co[1] = rect[1];
391 vertices[0].uv[0] = uv[0];
392 vertices[0].uv[1] = uv[1];
393 vertices[0].colour = colour;
394 vertices[1].co[0] = rect[0]+rect[2];
395 vertices[1].co[1] = rect[1];
396 vertices[1].uv[0] = uv[2];
397 vertices[1].uv[1] = uv[1];
398 vertices[1].colour = colour;
399 vertices[2].co[0] = rect[0]+rect[2];
400 vertices[2].co[1] = rect[1]+rect[3];
401 vertices[2].uv[0] = uv[2];
402 vertices[2].uv[1] = uv[3];
403 vertices[2].colour = colour;
404 vertices[3].co[0] = rect[0];
405 vertices[3].co[1] = rect[1]+rect[3];
406 vertices[3].uv[0] = uv[0];
407 vertices[3].uv[1] = uv[3];
408 vertices[3].colour = colour;
409 u16 ind_start = ctx->num_verts;
410 u16 *indices = &ctx->indices[ ctx->num_indices ];
411
412 indices[0] = ind_start+0;
413 indices[1] = ind_start+2;
414 indices[2] = ind_start+1;
415
416 indices[3] = ind_start+0;
417 indices[4] = ind_start+3;
418 indices[5] = ind_start+2;
419
420 ctx->num_indices += 6;
421 ctx->num_verts += 4;
422
423 return vertices;
424 }
425
426 static struct ui_vert *ui_fill_rect( ui_ctx *ctx, ui_rect rect, u32 colour )
427 {
428 return ui_fill_rect_uv( ctx, rect, colour, (ui_px[4]){ 66, 66, 66, 66 } );
429 }
430
431 static void ui_text( ui_ctx *ctx, const char *str, ui_px scale, int alignment )
432 {
433 ui_rect text_cursor;
434
435 text_cursor[0] = ctx->cursor[0];
436 text_cursor[1] = ctx->cursor[1];
437 text_cursor[2] = 7*scale;
438 text_cursor[3] = 7*scale;
439
440 const char *_c = str;
441 char c;
442 while( (c = *(_c ++)) )
443 {
444 if( c == '\n' )
445 {
446 text_cursor[1] += 10*scale;
447 text_cursor[0] = ctx->cursor[0];
448 continue;
449 }
450 else if( c >= 33 && c <= 126 )
451 {
452 u8 glyph_base[2];
453 u8 glyph_index = c - 32;
454 glyph_base[0] = glyph_index%10;
455 glyph_base[1] = (glyph_index-glyph_base[0])/10;
456
457 glyph_base[0] *= 7;
458 glyph_base[1] *= 7;
459
460 ui_fill_rect_uv( ctx, text_cursor, 0xffffffff, (ui_px[4]){glyph_base[0],glyph_base[1],glyph_base[0]+7,glyph_base[1]+7} );
461 }
462
463 text_cursor[0] += 6*scale;
464 }
465 }
466
467 // API control
468 // ====================================================================
469
470 static void ui_begin( ui_ctx *ctx, ui_px res_x, ui_px res_y )
471 {
472 ctx->cursor[0] = 0;
473 ctx->cursor[1] = 0;
474 ctx->cursor[2] = res_x;
475 ctx->cursor[3] = res_y;
476
477 ui_rect_copy( ctx->cursor, ctx->stack[0].rect );
478 ctx->stack[0].mouse_over = 1;
479
480 ctx->stack_count = 1;
481
482 ctx->num_verts = 0;
483 ctx->num_indices = 0;
484 }
485
486 static void ui_resolve( ui_ctx *ctx )
487 {
488 if( ctx->stack_count-1 )
489 vg_exiterr( "[UI] Mismatched node create/drestroy!" );
490
491 if( ctx->click_state == 3 || ctx->click_state == 0 )
492 {
493 ctx->capture_lock = 0;
494 }
495 }
496
497 // User Input piping
498 // ====================================================================
499
500 static void ui_set_mouse( ui_ctx *ctx, int x, int y, int click_state )
501 {
502 ctx->mouse[0] = x;
503 ctx->mouse[1] = y;
504
505 ctx->click_state = click_state;
506 }
507
508 // High level controls
509 // ====================================================================
510
511 struct ui_window
512 {
513 const char *title;
514 ui_rect transform;
515
516 int drag;
517 ui_px drag_offset[2];
518 };
519
520 static int ui_button( ui_ctx *ctx, u32 id )
521 {
522 ui_new_node( ctx );
523 {
524 ui_capture_mouse( ctx, id );
525
526 if( ui_hasmouse(ctx) )
527 {
528 ui_fill_rect( ctx, ctx->cursor, 0xffcccccc );
529
530 if( ctx->click_state == 1 )
531 ctx->capture_lock = 1;
532 else if( ctx->capture_lock && ctx->click_state == 3 )
533 {
534 return 1;
535 }
536 }
537 else
538 ui_fill_rect( ctx, ctx->cursor, 0xff999999 );
539 }
540
541 return 0;
542 }
543
544 static int ui_window( ui_ctx *ctx, struct ui_window *window, u32 control_group )
545 {
546 ctx->id_base = control_group << 16;
547
548 if( window->drag )
549 {
550 window->transform[0] = ctx->mouse[0]+window->drag_offset[0];
551 window->transform[1] = ctx->mouse[1]+window->drag_offset[1];
552
553 ui_clamp_rect( ctx->stack[0].rect, window->transform );
554
555 if( ctx->click_state == 0 || ctx->click_state == 3 )
556 {
557 window->drag = 0;
558 }
559 }
560
561 ui_rect_copy( window->transform, ctx->cursor );
562
563 ui_new_node( ctx );
564 {
565 ui_capture_mouse( ctx, __COUNTER__ );
566
567 //ui_fill_rect( ctx, ctx->cursor, 0xff333333 );
568
569 // Drag bar
570 ctx->cursor[3] = 25;
571 ui_new_node( ctx );
572 {
573 ui_capture_mouse( ctx, __COUNTER__ );
574
575 struct ui_vert *drag_bar = ui_fill_rect( ctx, ctx->cursor, 0xff555555 );
576
577 // title..
578 ctx->cursor[0] += 2;
579 ctx->cursor[1] += 2;
580 ui_text( ctx, window->title, 2, 0 );
581
582 // Close button
583 ctx->cursor[3] = 25;
584 ctx->cursor[2] = 25;
585 ui_align_right( ctx );
586 ui_align_top( ctx );
587 ui_rect_pad( ctx->cursor, 4 );
588
589 if( ui_button( ctx, __COUNTER__ ) )
590 {
591 vg_info( "Click clacked\n" );
592 }
593 ctx->cursor[0] += 2;
594 ui_text( ctx, "x", 2, 0 );
595 ui_end( ctx );
596
597 if( ui_hasmouse( ctx ) )
598 {
599 drag_bar[0].colour = 0xff777777;
600 drag_bar[1].colour = 0xff777777;
601 drag_bar[2].colour = 0xff777777;
602 drag_bar[3].colour = 0xff777777;
603
604 // start drag
605 if( ctx->click_state == 1 )
606 {
607 window->drag = 1;
608 window->drag_offset[0] = window->transform[0]-ctx->mouse[0];
609 window->drag_offset[1] = window->transform[1]-ctx->mouse[1];
610 }
611 }
612 }
613 ui_end_down( ctx );
614 }
615
616 return 1;
617 }
618
619 static void ui_test(void)
620 {
621 /*
622 +------------------------------------------------------+
623 | Central Market [x]|
624 +------+--------------+-+------------------------------+
625 | Buy | Balance |#| [filters] [favorites] |
626 | <>_ | () 2,356 |#|----------------------------+-+
627 |------|--------------|#| [] potion of madness 4 |#|
628 | Sell | \ Main sword |#|----------------------------|#|
629 | _*^ |--------------|#| [] Balance of time 23 | |
630 |------| * Side arm |#|----------------------------| |
631 | 235 |--------------| | [] Strength 5,300 | |
632 | | () Sheild | |----------------------------| |
633 | |--------------| | [] Bewilder 2,126 | |
634 | [ & Spells ] |----------------------------| |
635 | |--------------| | [] Eternal flames 6 | |
636 +------+--------------+-+----------------------------+-+
637 */
638
639 ui_begin( &ui_global_ctx, vg_window_x, vg_window_y );
640
641 // TODO: Find a more elegent form for this
642 int mouse_state = 0;
643 if( vg_get_button( "primary" ) ) mouse_state = 2;
644 if( vg_get_button_down( "primary" ) ) mouse_state = 1;
645 if( vg_get_button_up( "primary" ) ) mouse_state = 3;
646
647 ui_set_mouse( &ui_global_ctx, vg_mouse[0], vg_mouse[1], mouse_state );
648
649 static struct ui_window window =
650 {
651 .transform = { 20, 20, 500, 350 },
652 .title = "Central Market"
653 };
654
655 if( ui_window( &ui_global_ctx, &window, __COUNTER__ ) )
656 {
657 // Contents
658 //ui_text( &ui_global_ctx, "A slice of heaven. O for awesome, this chocka \nfull cuzzie is as rip-off as a cracker.\nMeanwhile, in behind the bicycle shed, Hercules Morse,\nas big as a horse and Mrs Falani were up to no \ngood with a bunch of crook pikelets. Meanwhile, \nat the black singlet woolshed party, not even au,\nsort your drinking out.", 1, 0 );
659 ui_global_ctx.cursor[2] = 75;
660 ui_fill_y( &ui_global_ctx );
661
662 ui_new_node( &ui_global_ctx );
663 {
664 ui_global_ctx.cursor[3] = 75;
665
666 if( ui_button( &ui_global_ctx, __COUNTER__ ) )
667 vg_info( "Buy\n" );
668 {
669 ui_rect_pad( ui_global_ctx.cursor, 4 );
670 ui_text( &ui_global_ctx, "Buy", 2, 0 );
671 }
672 ui_end_down( &ui_global_ctx );
673
674 if( ui_button( &ui_global_ctx, __COUNTER__ ) )
675 vg_info( "Sell\n" );
676 {
677 ui_rect_pad( ui_global_ctx.cursor, 4 );
678 ui_text( &ui_global_ctx, "Sell", 2, 0 );
679 }
680 ui_end_down( &ui_global_ctx );
681 }
682 ui_end_right( &ui_global_ctx );
683
684 ui_global_ctx.cursor[2] = 200;
685 ui_new_node( &ui_global_ctx );
686 {
687
688 }
689 ui_end_right( &ui_global_ctx );
690 }
691 ui_end( &ui_global_ctx );
692
693 ui_resolve( &ui_global_ctx );
694
695 m3x3f view = M3X3_IDENTITY;
696 m3x3_translate( view, (v3f){ -1.0f, 1.0f, 0.0f } );
697 m3x3_scale( view, (v3f){ 1.0f/((float)vg_window_x*0.5f), -1.0f/((float)vg_window_y*0.5f), 1.0f } );
698 vg_lines_drawall( (float*)view );
699
700 ui_draw( &ui_global_ctx );
701 }