bf9adc85549d761ecbd833c5df438aa1ad1c3b31
1 // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
3 SHADER_DEFINE( shader_ui
,
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
11 "out vec2 aTexCoords;"
16 "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
17 "aTexCoords = a_uv * 0.01388888888;"
22 "uniform sampler2D uTexGlyphs;"
30 "vec4 glyph = texture( uTexGlyphs, aTexCoords );"
31 "FragColor = aColour * vec4( 1.0, 1.0, 1.0, glyph.r );"
34 UNIFORMS({ "uPv", "uTexGlyphs" })
37 #define UI_AUTO_FILL 0
41 // ===========================================================================================================
44 typedef u32 ui_colour
;
45 typedef ui_px ui_rect
[4];
46 typedef struct ui_ctx ui_ctx
;
83 int click_state
; // 0: released, 1: on down, 2: pressed, 3: on release
87 // ===========================================================================================================
90 GLuint ui_glyph_texture
;
95 #define UI_BUFFER_SIZE 30000
96 #define UI_INDEX_SIZE 20000
98 ui_ctx ui_global_ctx
= { .padding
= 8 };
102 // ===========================================================================================================
104 static void ui_default_init(void)
109 #include "fonts/weiholmir.h"
112 u32 pixels
= 0, total
= 72*72, data
= 0;
113 u8
*image
= malloc( total
);
115 while( pixels
< total
)
117 for( int b
= 31; b
>= 0; b
-- )
119 image
[ pixels
++ ] = (compressed
[data
] & (0x1 << b
))? 0xff: 0x00;
121 if( pixels
>= total
)
130 glGenTextures( 1, &ui_glyph_texture
);
131 glBindTexture( GL_TEXTURE_2D
, ui_glyph_texture
);
133 glTexImage2D( GL_TEXTURE_2D
, 0, GL_R8
, 72, 72, 0, GL_RED
, GL_UNSIGNED_BYTE
, image
);
141 // Setup OpenGL memory
143 SHADER_INIT( shader_ui
);
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
);
151 glBindBuffer( GL_ARRAY_BUFFER
, ui_vbo
);
153 glBufferData( GL_ARRAY_BUFFER
, UI_BUFFER_SIZE
* sizeof( struct ui_vert
), NULL
, GL_DYNAMIC_DRAW
);
154 glBindVertexArray( ui_vao
);
156 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, ui_ebo
);
157 glBufferData( GL_ELEMENT_ARRAY_BUFFER
, UI_INDEX_SIZE
* sizeof( u16
), NULL
, GL_DYNAMIC_DRAW
);
159 u32
const stride
= sizeof( struct ui_vert
);
162 glVertexAttribPointer( 0, 2, GL_UNSIGNED_SHORT
, GL_FALSE
, stride
, (void *)offsetof( struct ui_vert
, co
) );
163 glEnableVertexAttribArray( 0 );
166 glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE
, GL_FALSE
, stride
, (void *)offsetof( struct ui_vert
, uv
) );
167 glEnableVertexAttribArray( 1 );
170 glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE
, GL_TRUE
, stride
, (void *)offsetof( struct ui_vert
, colour
) );
171 glEnableVertexAttribArray( 2 );
174 // Initialize default context
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
) );
181 static void ui_default_free(void)
183 glDeleteTextures( 1, &ui_glyph_texture
);
185 glDeleteVertexArrays( 1, &ui_vao
);
186 glDeleteBuffers( 1, &ui_vbo
);
187 glDeleteBuffers( 1, &ui_ebo
);
189 free( ui_global_ctx
.verts
);
190 free( ui_global_ctx
.indices
);
193 static void ui_draw( ui_ctx
*ctx
)
195 glBindVertexArray( ui_vao
);
197 glBindBuffer( GL_ARRAY_BUFFER
, ui_vbo
);
198 glBufferSubData( GL_ARRAY_BUFFER
, 0, ctx
->num_verts
* sizeof( struct ui_vert
), ctx
->verts
);
200 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, ui_ebo
);
201 glBufferSubData( GL_ELEMENT_ARRAY_BUFFER
, 0, ctx
->num_indices
* sizeof( u16
), ctx
->indices
);
204 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
205 glBlendEquation(GL_FUNC_ADD
);
207 SHADER_USE( shader_ui
);
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
);
214 glActiveTexture( GL_TEXTURE0
);
215 glBindTexture( GL_TEXTURE_2D
, ui_glyph_texture
);
216 glUniform1i( SHADER_UNIFORM( shader_ui
, "uTexGlyphs" ), 0 );
218 glDrawElements( GL_TRIANGLES
, ctx
->num_indices
, GL_UNSIGNED_SHORT
, (void*)(0) );
220 //vg_info( "Verts: %u, Indices: %u\n", ctx->num_verts, ctx->num_indices );
226 // ===========================================================================================================
228 static void ui_rect_copy( ui_rect src
, ui_rect dst
)
236 static void ui_rect_pad( ui_rect rect
, ui_px pad
)
244 static void ui_vis_rect( ui_rect rect
, u32 colour
)
252 p1
[0] = rect
[0]+rect
[2];
253 p1
[1] = rect
[1]+rect
[3];
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
);
263 // ===========================================================================================================
265 static struct ui_qnode
*ui_current( ui_ctx
*ctx
)
267 return &ctx
->stack
[ ctx
->stack_count
-1 ];
270 static void ui_new_node( ui_ctx
*ctx
)
272 if( ctx
->stack_count
== vg_list_size( ctx
->stack
) )
273 vg_exiterr( "[UI] Stack overflow while creating box!" );
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
);
279 if( parent
->mouse_over
)
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;
285 node
->mouse_over
= 0;
289 node
->mouse_over
= 0;
293 static int ui_hasmouse( ui_ctx
*ctx
)
295 struct ui_qnode
*node
= ui_current( ctx
);
296 return (node
->mouse_over
&& (node
->capture_id
== ctx
->capture_mouse_id
));
299 static void ui_end( ui_ctx
*ctx
)
301 struct ui_qnode
*node
= &ctx
->stack
[ --ctx
->stack_count
];
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 );
308 static void ui_end_down( ui_ctx
*ctx
)
310 ui_px height
= ui_current( ctx
)->rect
[3];
312 ctx
->cursor
[1] += height
;
315 static void ui_end_right( ui_ctx
*ctx
)
317 ui_px width
= ui_current( ctx
)->rect
[2];
319 ctx
->cursor
[0] += width
;
322 static void ui_fill_y( ui_ctx
*ctx
)
324 struct ui_qnode
*node
= ui_current( ctx
);
325 ctx
->cursor
[3] = node
->rect
[3] - (ctx
->cursor
[1]-node
->rect
[1]);
328 static void ui_fill_x( ui_ctx
*ctx
)
330 struct ui_qnode
*node
= ui_current( ctx
);
331 ctx
->cursor
[2] = node
->rect
[2] - (ctx
->cursor
[0]-node
->rect
[0]);
334 // Alignment: | [] | -> | []|
335 static void ui_align_bottom( ui_ctx
*ctx
)
337 struct ui_qnode
*node
= ui_current( ctx
);
338 ctx
->cursor
[1] = node
->rect
[1] + node
->rect
[3] - ctx
->cursor
[3];
341 static void ui_align_right( ui_ctx
*ctx
)
343 struct ui_qnode
*node
= ui_current( ctx
);
344 ctx
->cursor
[0] = node
->rect
[0] + node
->rect
[2] - ctx
->cursor
[2];
347 static void ui_align_top( ui_ctx
*ctx
)
349 ctx
->cursor
[1] = ui_current( ctx
)->rect
[1];
352 static void ui_align_left( ui_ctx
*ctx
)
354 ctx
->cursor
[0] = ui_current( ctx
)->rect
[0];
357 static void ui_clamp_rect( ui_rect parent
, ui_rect dest
)
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] );
365 static u32
ui_group_id( ui_ctx
*ctx
, u32 lesser_unique
)
367 return ctx
->id_base
| lesser_unique
;
370 static void ui_capture_mouse( ui_ctx
*ctx
, u32 id
)
372 u32 group_uid
= ui_group_id(ctx
,id
);
374 struct ui_qnode
*node
= &ctx
->stack
[ ctx
->stack_count
-1 ];
375 node
->capture_id
= group_uid
;
377 if( !ctx
->capture_lock
&& node
->mouse_over
)
379 ctx
->capture_mouse_id
= group_uid
;
384 // ===========================================================================================================
386 static struct ui_vert
*ui_fill_rect_uv( ui_ctx
*ctx
, ui_rect rect
, u32 colour
, ui_px uv
[4] )
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
];
412 indices
[0] = ind_start
+0;
413 indices
[1] = ind_start
+2;
414 indices
[2] = ind_start
+1;
416 indices
[3] = ind_start
+0;
417 indices
[4] = ind_start
+3;
418 indices
[5] = ind_start
+2;
420 ctx
->num_indices
+= 6;
426 static struct ui_vert
*ui_fill_rect( ui_ctx
*ctx
, ui_rect rect
, u32 colour
)
428 return ui_fill_rect_uv( ctx
, rect
, colour
, (ui_px
[4]){ 66, 66, 66, 66 } );
431 static void ui_text( ui_ctx
*ctx
, const char *str
, ui_px scale
, int alignment
)
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
;
440 u32 current_colour
= 0xffffffff;
442 const char *_c
= str
;
444 while( (c
= *(_c
++)) )
448 text_cursor
[1] += 10*scale
;
449 text_cursor
[0] = ctx
->cursor
[0];
452 else if( c
>= 33 && c
<= 126 )
455 u8 glyph_index
= c
- 32;
456 glyph_base
[0] = glyph_index
%10;
457 glyph_base
[1] = (glyph_index
-glyph_base
[0])/10;
462 ui_fill_rect_uv( ctx
, text_cursor
, current_colour
, (ui_px
[4]){glyph_base
[0],glyph_base
[1],glyph_base
[0]+7,glyph_base
[1]+7} );
464 else if( c
== '\x1B' )
468 for( int i
= 0; i
< 3; i
++ )
478 case '0': current_colour
= 0xffffffff; break;
479 case '3'|'1'<<8: current_colour
= 0xff201fee; break;
480 case '3'|'2'<<8: current_colour
= 0xff37e420; break;
481 case '3'|'3'<<8: current_colour
= 0xff0ed8e2; break;
482 case '3'|'4'<<8: current_colour
= 0xfff15010; break;
483 case '3'|'5'<<8: current_colour
= 0xffee20ee; break;
484 case '3'|'6'<<8: current_colour
= 0xffeeee20; break;
485 case '3'|'7'<<8: current_colour
= 0xffffffff; break;
491 colour_id
|= _c
[i
] << (i
*8);
501 text_cursor
[0] += 6*scale
;
506 // ====================================================================
508 static void ui_begin( ui_ctx
*ctx
, ui_px res_x
, ui_px res_y
)
512 ctx
->cursor
[2] = res_x
;
513 ctx
->cursor
[3] = res_y
;
515 ui_rect_copy( ctx
->cursor
, ctx
->stack
[0].rect
);
516 ctx
->stack
[0].mouse_over
= 1;
518 ctx
->stack_count
= 1;
521 ctx
->num_indices
= 0;
524 static void ui_resolve( ui_ctx
*ctx
)
526 if( ctx
->stack_count
-1 )
527 vg_exiterr( "[UI] Mismatched node create/drestroy!" );
529 if( ctx
->click_state
== 3 || ctx
->click_state
== 0 )
531 ctx
->capture_lock
= 0;
536 // ====================================================================
538 static void ui_set_mouse( ui_ctx
*ctx
, int x
, int y
, int click_state
)
543 ctx
->click_state
= click_state
;
546 // High level controls
547 // ====================================================================
555 ui_px drag_offset
[2];
558 static int ui_button( ui_ctx
*ctx
, u32 id
)
562 ui_capture_mouse( ctx
, id
);
564 if( ui_hasmouse(ctx
) )
566 ui_fill_rect( ctx
, ctx
->cursor
, 0xffcccccc );
568 if( ctx
->click_state
== 1 )
569 ctx
->capture_lock
= 1;
570 else if( ctx
->capture_lock
&& ctx
->click_state
== 3 )
576 ui_fill_rect( ctx
, ctx
->cursor
, 0xff999999 );
582 static int ui_window( ui_ctx
*ctx
, struct ui_window
*window
, u32 control_group
)
584 ctx
->id_base
= control_group
<< 16;
588 window
->transform
[0] = ctx
->mouse
[0]+window
->drag_offset
[0];
589 window
->transform
[1] = ctx
->mouse
[1]+window
->drag_offset
[1];
591 ui_clamp_rect( ctx
->stack
[0].rect
, window
->transform
);
593 if( ctx
->click_state
== 0 || ctx
->click_state
== 3 )
599 ui_rect_copy( window
->transform
, ctx
->cursor
);
603 ui_capture_mouse( ctx
, __COUNTER__
);
605 //ui_fill_rect( ctx, ctx->cursor, 0xff333333 );
611 ui_capture_mouse( ctx
, __COUNTER__
);
613 struct ui_vert
*drag_bar
= ui_fill_rect( ctx
, ctx
->cursor
, 0xff555555 );
618 ui_text( ctx
, window
->title
, 2, 0 );
623 ui_align_right( ctx
);
625 ui_rect_pad( ctx
->cursor
, 4 );
627 if( ui_button( ctx
, __COUNTER__
) )
629 vg_info( "Click clacked\n" );
632 ui_text( ctx
, "x", 2, 0 );
635 if( ui_hasmouse( ctx
) )
637 drag_bar
[0].colour
= 0xff777777;
638 drag_bar
[1].colour
= 0xff777777;
639 drag_bar
[2].colour
= 0xff777777;
640 drag_bar
[3].colour
= 0xff777777;
643 if( ctx
->click_state
== 1 )
646 window
->drag_offset
[0] = window
->transform
[0]-ctx
->mouse
[0];
647 window
->drag_offset
[1] = window
->transform
[1]-ctx
->mouse
[1];
657 static void ui_test(void)
660 +------------------------------------------------------+
661 | Central Market [x]|
662 +------+--------------+-+------------------------------+
663 | Buy | Balance |#| [filters] [favorites] |
664 | <>_ | () 2,356 |#|----------------------------+-+
665 |------|--------------|#| [] potion of madness 4 |#|
666 | Sell | \ Main sword |#|----------------------------|#|
667 | _*^ |--------------|#| [] Balance of time 23 | |
668 |------| * Side arm |#|----------------------------| |
669 | 235 |--------------| | [] Strength 5,300 | |
670 | | () Sheild | |----------------------------| |
671 | |--------------| | [] Bewilder 2,126 | |
672 | [ & Spells ] |----------------------------| |
673 | |--------------| | [] Eternal flames 6 | |
674 +------+--------------+-+----------------------------+-+
677 ui_begin( &ui_global_ctx
, vg_window_x
, vg_window_y
);
679 // TODO: Find a more elegent form for this
681 if( vg_get_button( "primary" ) ) mouse_state
= 2;
682 if( vg_get_button_down( "primary" ) ) mouse_state
= 1;
683 if( vg_get_button_up( "primary" ) ) mouse_state
= 3;
685 ui_set_mouse( &ui_global_ctx
, vg_mouse
[0], vg_mouse
[1], mouse_state
);
688 static struct ui_window window =
690 .transform = { 20, 20, 500, 350 },
691 .title = "Central Market"
694 if( ui_window( &ui_global_ctx, &window, __COUNTER__ ) )
697 //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 );
698 ui_global_ctx.cursor[2] = 75;
699 ui_fill_y( &ui_global_ctx );
701 ui_new_node( &ui_global_ctx );
703 ui_global_ctx.cursor[3] = 75;
705 if( ui_button( &ui_global_ctx, __COUNTER__ ) )
708 ui_rect_pad( ui_global_ctx.cursor, 4 );
709 ui_text( &ui_global_ctx, "Buy", 2, 0 );
711 ui_end_down( &ui_global_ctx );
713 if( ui_button( &ui_global_ctx, __COUNTER__ ) )
716 ui_rect_pad( ui_global_ctx.cursor, 4 );
717 ui_text( &ui_global_ctx, "Sell", 2, 0 );
719 ui_end_down( &ui_global_ctx );
721 ui_end_right( &ui_global_ctx );
723 ui_global_ctx.cursor[2] = 200;
724 ui_new_node( &ui_global_ctx );
728 ui_end_right( &ui_global_ctx );
730 ui_end( &ui_global_ctx );
734 ui_rect_copy( ui_global_ctx
.cursor
, rbf
);
736 ui_global_ctx
.cursor
[0] = 6;
737 ui_global_ctx
.cursor
[3] = 21;
738 ui_fill_x( &ui_global_ctx
);
739 ui_align_bottom( &ui_global_ctx
);
740 ui_text( &ui_global_ctx
, "Marble computer build 2 -- development version -- (C) Harry Godden 2021\nAknowledgements: 'credits' in console (`)", 1, 0 );
742 ui_resolve( &ui_global_ctx
);
744 m3x3f view
= M3X3_IDENTITY
;
745 m3x3_translate( view
, (v3f
){ -1.0f
, 1.0f
, 0.0f
} );
746 m3x3_scale( view
, (v3f
){ 1.0f
/((float)vg_window_x
*0.5f
), -1.0f
/((float)vg_window_y
*0.5f
), 1.0f
} );
747 //vg_lines_drawall( (float*)view );
749 ui_draw( &ui_global_ctx
);
751 ui_rect_copy( rbf
, ui_global_ctx
.cursor
);