1 // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
4 Concurrent UI buffer system
15 SHADER_DEFINE( shader_ui
,
18 "layout (location=0) in vec2 a_co;" // i16, i16, .. ?
19 "layout (location=1) in vec2 a_uv;" // i8, i8
20 "layout (location=2) in vec4 a_colour;" // u32
23 "out vec2 aTexCoords;"
28 "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
29 "aTexCoords = a_uv * 0.01388888888;"
34 "uniform sampler2D uTexGlyphs;"
42 "vec4 glyph = texture( uTexGlyphs, aTexCoords );"
43 "FragColor = aColour * vec4( 1.0, 1.0, 1.0, glyph.r );"
46 UNIFORMS({ "uPv", "uTexGlyphs" })
49 #define UI_AUTO_FILL 0
52 // ================================================================
54 typedef u32 ui_colour
;
55 typedef ui_px ui_rect
[4];
56 typedef struct ui_ctx ui_ctx
;
80 struct ui_vert
*verts
;
93 int click_state
; // 0: released, 1: on down, 2: pressed, 3: on release
97 GLuint ui_glyph_texture
;
104 #define UI_BUFFER_SIZE 30000
105 #define UI_INDEX_SIZE 20000
107 static void ui_glyphs_load( u32
*compressed
)
109 u32 pixels
= 0, total
= 72*72, data
= 0;
110 u8
*image
= malloc( total
);
112 while( pixels
< total
)
114 for( int b
= 31; b
>= 0; b
-- )
116 image
[ pixels
++ ] = (compressed
[data
] & (0x1 << b
))? 0xff: 0x00;
118 if( pixels
>= total
)
127 for( int j
= 0; j
< 70; j
++ )
129 for( int i
= 0; i
< 70; i
++ )
130 printf( "%s",image
[j
*72+i
]?"#":" " );
134 glGenTextures( 1, &ui_glyph_texture
);
135 glBindTexture( GL_TEXTURE_2D
, ui_glyph_texture
);
137 glTexImage2D( GL_TEXTURE_2D
, 0, GL_R8
, 72, 72, 0, GL_RED
, GL_UNSIGNED_BYTE
, image
);
145 static void ui_default_init(void)
147 ui_glyphs_load( (u32
[]){
148 #include "fonts/weiholmir.h"
151 SHADER_INIT( shader_ui
);
153 // Generate the buffer we are gonna be drawing to
154 glGenVertexArrays(1, &ui_vao
);
155 glGenBuffers( 1, &ui_vbo
);
156 glGenBuffers( 1, &ui_ebo
);
157 glBindVertexArray( ui_vao
);
159 glBindBuffer( GL_ARRAY_BUFFER
, ui_vbo
);
161 glBufferData( GL_ARRAY_BUFFER
, UI_BUFFER_SIZE
* sizeof( struct ui_vert
), NULL
, GL_DYNAMIC_DRAW
);
162 glBindVertexArray( ui_vao
);
164 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, ui_ebo
);
165 glBufferData( GL_ELEMENT_ARRAY_BUFFER
, UI_INDEX_SIZE
* sizeof( u16
), NULL
, GL_DYNAMIC_DRAW
);
168 glVertexAttribPointer( 0, 2, GL_UNSIGNED_SHORT
, GL_FALSE
, sizeof( struct ui_vert
), (void *)offsetof( struct ui_vert
, co
) );
169 glEnableVertexAttribArray( 0 );
172 glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE
, GL_FALSE
, sizeof( struct ui_vert
), (void *)offsetof( struct ui_vert
, uv
) );
173 glEnableVertexAttribArray( 1 );
176 glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE
, GL_TRUE
, sizeof( struct ui_vert
), (void *)offsetof( struct ui_vert
, colour
) );
177 glEnableVertexAttribArray( 2 );
180 static void ui_default_free(void)
182 glDeleteTextures( 1, &ui_glyph_texture
);
184 glDeleteVertexArrays( 1, &ui_vao
);
185 glDeleteBuffers( 1, &ui_vbo
);
186 glDeleteBuffers( 1, &ui_ebo
);
189 static void ui_draw( ui_ctx
*ctx
)
191 glBindVertexArray( ui_vao
);
193 glBindBuffer( GL_ARRAY_BUFFER
, ui_vbo
);
194 glBufferSubData( GL_ARRAY_BUFFER
, 0, ctx
->num_verts
* sizeof( struct ui_vert
), ctx
->verts
);
196 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, ui_ebo
);
197 glBufferSubData( GL_ELEMENT_ARRAY_BUFFER
, 0, ctx
->num_indices
* sizeof( u16
), ctx
->indices
);
200 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
201 glBlendEquation(GL_FUNC_ADD
);
203 SHADER_USE( shader_ui
);
205 m3x3f view
= M3X3_IDENTITY
;
206 m3x3_translate( view
, (v3f
){ -1.0f
, 1.0f
, 0.0f
} );
207 m3x3_scale( view
, (v3f
){ 1.0f
/((float)vg_window_x
*0.5f
), -1.0f
/((float)vg_window_y
*0.5f
), 1.0f
} );
208 glUniformMatrix3fv( SHADER_UNIFORM( shader_ui
, "uPv" ), 1, GL_FALSE
, (float *)view
);
210 glActiveTexture( GL_TEXTURE0
);
211 glBindTexture( GL_TEXTURE_2D
, ui_glyph_texture
);
212 glUniform1i( SHADER_UNIFORM( shader_ui
, "uTexGlyphs" ), 0 );
214 glDrawElements( GL_TRIANGLES
, ctx
->num_indices
, GL_UNSIGNED_SHORT
, (void*)(0) );
216 //vg_info( "Verts: %u, Indices: %u\n", ctx->num_verts, ctx->num_indices );
222 // ==========================================================
224 static void ui_rect_copy( ui_rect src
, ui_rect dst
)
232 static void ui_rect_pad( ui_ctx
*ctx
, ui_rect rect
, ui_px pad
)
240 static void ui_vis_rect( ui_rect rect
, u32 colour
)
247 p1
[0] = rect
[0]+rect
[2];
248 p1
[1] = rect
[1]+rect
[3];
250 vg_line( p0
, (v2f
){p1
[0],p0
[1]}, colour
);
251 vg_line( (v2f
){p1
[0],p0
[1]}, p1
, colour
);
252 vg_line( p1
, (v2f
){p0
[0],p1
[1]}, colour
);
253 vg_line( (v2f
){p0
[0],p1
[1]}, p0
, colour
);
256 static void ui_new_node( ui_ctx
*ctx
)
258 if( ctx
->stack_count
== vg_list_size( ctx
->stack
) )
259 vg_exiterr( "[UI] Stack overflow while creating box!" );
261 struct ui_qnode
*parent
= &ctx
->stack
[ ctx
->stack_count
-1 ];
262 struct ui_qnode
*node
= &ctx
->stack
[ ctx
->stack_count
++ ];
263 ui_rect_copy( ctx
->cursor
, node
->rect
);
265 if( parent
->mouse_over
)
267 if( ctx
->mouse
[0] >= node
->rect
[0] && ctx
->mouse
[0] <= node
->rect
[0]+node
->rect
[2] &&
268 ctx
->mouse
[1] >= node
->rect
[1] && ctx
->mouse
[1] <= node
->rect
[1]+node
->rect
[3] )
269 node
->mouse_over
= 1;
271 node
->mouse_over
= 0;
275 node
->mouse_over
= 0;
279 static int ui_hasmouse( ui_ctx
*ctx
)
281 struct ui_qnode
*node
= &ctx
->stack
[ ctx
->stack_count
-1 ];
282 return (node
->mouse_over
&& (node
->capture_id
== ctx
->capture_mouse_id
));
285 static void ui_end( ui_ctx
*ctx
)
287 struct ui_qnode
*node
= &ctx
->stack
[ --ctx
->stack_count
];
289 ui_rect_copy( node
->rect
, ctx
->cursor
);
290 ui_vis_rect( ctx
->cursor
, (node
->mouse_over
&& (node
->capture_id
== ctx
->capture_mouse_id
))? 0xffff0000: 0xff0000ff );
293 static void ui_end_down( ui_ctx
*ctx
)
295 ui_px height
= ctx
->stack
[ ctx
->stack_count
-1 ].rect
[3];
297 ctx
->cursor
[1] += height
;
300 static void ui_end_right( ui_ctx
*ctx
)
302 ui_px width
= ctx
->stack
[ ctx
->stack_count
-1 ].rect
[2];
304 ctx
->cursor
[0] += width
;
307 static void ui_align_right( ui_ctx
*ctx
)
309 struct ui_qnode
*node
= &ctx
->stack
[ ctx
->stack_count
-1 ];
310 ctx
->cursor
[0] = node
->rect
[0] + node
->rect
[2] - ctx
->cursor
[2];
313 static void ui_align_top( ui_ctx
*ctx
)
315 struct ui_qnode
*node
= &ctx
->stack
[ ctx
->stack_count
-1 ];
316 ctx
->cursor
[1] = node
->rect
[1] + node
->rect
[3] - ctx
->cursor
[3];
319 static void ui_clamp_rect( ui_rect parent
, ui_rect dest
)
321 dest
[0] = vg_min( parent
[0] + parent
[2] - dest
[2], dest
[0] );
322 dest
[1] = vg_min( parent
[1] + parent
[3] - dest
[3], dest
[1] );
323 dest
[0] = vg_max( parent
[0], dest
[0] );
324 dest
[1] = vg_max( parent
[1], dest
[1] );
327 static u32
ui_group_id( ui_ctx
*ctx
, u32 lesser_unique
)
329 return ctx
->id_base
| lesser_unique
;
332 static void ui_capture_mouse( ui_ctx
*ctx
, u32 id
)
334 u32 group_uid
= ui_group_id(ctx
,id
);
336 struct ui_qnode
*node
= &ctx
->stack
[ ctx
->stack_count
-1 ];
337 node
->capture_id
= group_uid
;
339 if( !ctx
->capture_lock
&& node
->mouse_over
)
341 ctx
->capture_mouse_id
= group_uid
;
345 static void ui_fill_rect( ui_ctx
*ctx
, ui_rect rect
, u32 colour
)
351 struct ui_vert
*vertices
= &ctx
->verts
[ ctx
->num_verts
];
352 vertices
[0].co
[0] = rect
[0];
353 vertices
[0].co
[1] = rect
[1];
354 vertices
[0].uv
[0] = uv
[0];
355 vertices
[0].uv
[1] = uv
[1];
356 vertices
[0].colour
= colour
;
357 vertices
[1].co
[0] = rect
[0]+rect
[2];
358 vertices
[1].co
[1] = rect
[1];
359 vertices
[1].uv
[0] = uv
[0];
360 vertices
[1].uv
[1] = uv
[1];
361 vertices
[1].colour
= colour
;
362 vertices
[2].co
[0] = rect
[0]+rect
[2];
363 vertices
[2].co
[1] = rect
[1]+rect
[3];
364 vertices
[2].uv
[0] = uv
[0];
365 vertices
[2].uv
[1] = uv
[1];
366 vertices
[2].colour
= colour
;
367 vertices
[3].co
[0] = rect
[0];
368 vertices
[3].co
[1] = rect
[1]+rect
[3];
369 vertices
[3].uv
[0] = uv
[0];
370 vertices
[3].uv
[1] = uv
[1];
371 vertices
[3].colour
= colour
;
372 u16 ind_start
= ctx
->num_verts
;
373 u16
*indices
= &ctx
->indices
[ ctx
->num_indices
];
375 indices
[0] = ind_start
+0;
376 indices
[1] = ind_start
+2;
377 indices
[2] = ind_start
+1;
379 indices
[3] = ind_start
+0;
380 indices
[4] = ind_start
+3;
381 indices
[5] = ind_start
+2;
383 ctx
->num_indices
+= 6;
387 static void ui_text( ui_ctx
*ctx
, const char *str
, ui_px scale
, int alignment
)
391 text_cursor
[0] = ctx
->cursor
[0];
392 text_cursor
[1] = ctx
->cursor
[1];
393 text_cursor
[2] = 7*scale
;
394 text_cursor
[3] = 7*scale
;
396 const char *_c
= str
;
398 while( (c
= *(_c
++)) )
402 text_cursor
[1] += 10*scale
;
403 text_cursor
[0] = ctx
->cursor
[0];
406 else if( c
>= 33 && c
<= 126 )
409 u8 glyph_index
= c
- 32;
410 glyph_base
[0] = glyph_index
%10;
411 glyph_base
[1] = (glyph_index
-glyph_base
[0])/10;
417 struct ui_vert
*vertices
= &ctx
->verts
[ctx
->num_verts
];
418 vertices
[0].co
[0] = text_cursor
[0];
419 vertices
[0].co
[1] = text_cursor
[1];
420 vertices
[0].uv
[0] = glyph_base
[0];
421 vertices
[0].uv
[1] = glyph_base
[1];
422 vertices
[0].colour
= 0xffffffff;
424 vertices
[1].co
[0] = text_cursor
[0]+text_cursor
[2];
425 vertices
[1].co
[1] = text_cursor
[1];
426 vertices
[1].uv
[0] = glyph_base
[0]+7;
427 vertices
[1].uv
[1] = glyph_base
[1];
428 vertices
[1].colour
= 0xffffffff;
430 vertices
[2].co
[0] = text_cursor
[0]+text_cursor
[2];
431 vertices
[2].co
[1] = text_cursor
[1]+text_cursor
[3];
432 vertices
[2].uv
[0] = glyph_base
[0]+7;
433 vertices
[2].uv
[1] = glyph_base
[1]+7;
434 vertices
[2].colour
= 0xffffffff;
436 vertices
[3].co
[0] = text_cursor
[0];
437 vertices
[3].co
[1] = text_cursor
[1]+text_cursor
[3];
438 vertices
[3].uv
[0] = glyph_base
[0];
439 vertices
[3].uv
[1] = glyph_base
[1]+7;
440 vertices
[3].colour
= 0xffffffff;
442 u16 ind_start
= ctx
->num_verts
;
443 u16
*indices
= &ctx
->indices
[ ctx
->num_indices
];
445 indices
[0] = ind_start
+0;
446 indices
[1] = ind_start
+2;
447 indices
[2] = ind_start
+1;
449 indices
[3] = ind_start
+0;
450 indices
[4] = ind_start
+3;
451 indices
[5] = ind_start
+2;
453 ctx
->num_indices
+= 6;
457 text_cursor
[0] += 6*scale
;
462 // ====================================================================
464 static void ui_begin( ui_ctx
*ctx
, ui_px res_x
, ui_px res_y
)
468 ctx
->cursor
[2] = res_x
;
469 ctx
->cursor
[3] = res_y
;
471 ui_rect_copy( ctx
->cursor
, ctx
->stack
[0].rect
);
472 ctx
->stack
[0].mouse_over
= 1;
474 ctx
->stack_count
= 1;
477 ctx
->num_indices
= 0;
480 static void ui_resolve( ui_ctx
*ctx
)
482 if( ctx
->stack_count
-1 )
483 vg_exiterr( "[UI] Mismatched node create/drestroy!" );
485 if( ctx
->click_state
== 3 || ctx
->click_state
== 0 )
487 ctx
->capture_lock
= 0;
492 // ====================================================================
494 static void ui_set_mouse( ui_ctx
*ctx
, int x
, int y
, int click_state
)
499 ctx
->click_state
= click_state
;
502 // High level controls
503 // ====================================================================
511 ui_px drag_offset
[2];
514 static int ui_button( ui_ctx
*ctx
, u32 id
)
518 ui_capture_mouse( ctx
, id
);
520 if( ui_hasmouse(ctx
) )
522 if( ctx
->click_state
== 1 )
523 ctx
->capture_lock
= 1;
524 else if( ctx
->capture_lock
&& ctx
->click_state
== 3 )
534 static int ui_window( ui_ctx
*ctx
, struct ui_window
*window
, u32 control_group
)
536 ctx
->id_base
= control_group
<< 16;
540 window
->transform
[0] = ctx
->mouse
[0]+window
->drag_offset
[0];
541 window
->transform
[1] = ctx
->mouse
[1]+window
->drag_offset
[1];
543 ui_clamp_rect( ctx
->stack
[0].rect
, window
->transform
);
545 if( ctx
->click_state
== 0 || ctx
->click_state
== 3 )
551 ui_rect_copy( window
->transform
, ctx
->cursor
);
555 ui_capture_mouse( ctx
, __COUNTER__
);
557 ui_fill_rect( ctx
, ctx
->cursor
, 0xff333333 );
563 ui_capture_mouse( ctx
, __COUNTER__
);
565 ui_fill_rect( ctx
, ctx
->cursor
, 0xff555555 );
570 ui_text( ctx
, window
->title
, 2, 0 );
575 ui_align_right( ctx
);
577 ui_rect_pad( ctx
, ctx
->cursor
, 4 );
579 if( ui_button( ctx
, __COUNTER__
) )
581 vg_info( "Click clacked\n" );
585 if( ui_hasmouse( ctx
) )
588 if( ctx
->click_state
== 1 )
591 window
->drag_offset
[0] = window
->transform
[0]-ctx
->mouse
[0];
592 window
->drag_offset
[1] = window
->transform
[1]-ctx
->mouse
[1];
602 ui_ctx test_ctx
= { .padding
= 8 };
604 static void ui_test_init(void)
606 test_ctx
.verts
= (struct ui_vert
*)malloc( UI_BUFFER_SIZE
* sizeof(struct ui_vert
) );
607 test_ctx
.indices
= (u16
*)malloc( UI_INDEX_SIZE
* sizeof(u16
) );
610 static void ui_test_free(void)
612 free( test_ctx
.verts
);
613 free( test_ctx
.indices
);
616 static void ui_test(void)
619 +------------------------------------------------------+
620 | Central Market [x]|
621 +------+--------------+-+------------------------------+
622 | Buy | Balance |#| [filters] [favorites] |
623 | <>_ | () 2,356 |#|----------------------------+-+
624 |------|--------------|#| [] potion of madness 4 |#|
625 | Sell | \ Main sword |#|----------------------------|#|
626 | _*^ |--------------|#| [] Balance of time 23 | |
627 |------| * Side arm |#|----------------------------| |
628 | 235 |--------------| | [] Strength 5,300 | |
629 | | () Sheild | |----------------------------| |
630 | |--------------| | [] Bewilder 2,126 | |
631 | [ & Spells ] |----------------------------| |
632 | |--------------| | [] Eternal flames 6 | |
633 +------+--------------+-+----------------------------+-+
636 ui_begin( &test_ctx
, vg_window_x
, vg_window_y
);
638 // TODO: Find a more elegent form for this
640 if( vg_get_button( "primary" ) ) mouse_state
= 2;
641 if( vg_get_button_down( "primary" ) ) mouse_state
= 1;
642 if( vg_get_button_up( "primary" ) ) mouse_state
= 3;
644 ui_set_mouse( &test_ctx
, vg_mouse
[0], vg_mouse
[1], mouse_state
);
646 static struct ui_window window
=
648 .transform
= { 20, 20, 500, 350 },
649 .title
= "Central Market"
652 if( ui_window( &test_ctx
, &window
, __COUNTER__
) )
655 ui_text( &test_ctx
, "Just say anything, George, say what ever's natural,\nthe first thing that comes to your mind.\nTake that you mutated son-of-a-bitch.", 1, 0 );
659 ui_resolve( &test_ctx
);
661 m3x3f view
= M3X3_IDENTITY
;
662 m3x3_translate( view
, (v3f
){ -1.0f
, 1.0f
, 0.0f
} );
663 m3x3_scale( view
, (v3f
){ 1.0f
/((float)vg_window_x
*0.5f
), -1.0f
/((float)vg_window_y
*0.5f
), 1.0f
} );
664 vg_lines_drawall( (float*)view
);
666 ui_draw( &test_ctx
);