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
9 "layout (location=3) in vec4 a_clip;" // i16, i16, i16, i16
12 "out vec2 aTexCoords;"
19 "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
20 "aTexCoords = a_uv * 0.0078125;"
28 "uniform sampler2D uTexGlyphs;"
39 "float clip_blend = step( aWsp.x, aClip.z ) * step( aWsp.y, aClip.w ) * step( aClip.x, aWsp.x ) * step( aClip.y, aWsp.y );"
41 "vec4 glyph = texture( uTexGlyphs, aTexCoords );"
42 "FragColor = aColour * vec4( 1.0, 1.0, 1.0, glyph.r * clip_blend );"
45 UNIFORMS({ "uPv", "uTexGlyphs" })
48 #define UI_AUTO_FILL 0
52 // ===========================================================================================================
55 typedef u32 ui_colour
;
56 typedef ui_px ui_rect
[4];
57 typedef struct ui_ctx ui_ctx
;
98 int click_state
; // 0: released, 1: on down, 2: pressed, 3: on release
102 // ===========================================================================================================
105 int ui_glyph_override
= 0;
106 ui_px ui_glyph_spacing_x
= 6;
107 GLuint ui_glyph_texture
= 0;
112 #define UI_BUFFER_SIZE 30000
113 #define UI_INDEX_SIZE 20000
115 ui_ctx ui_global_ctx
= { .padding
= 8 };
119 // ===========================================================================================================
121 static void ui_override_font( GLuint new_tex
, ui_px space_x
)
123 if( ui_glyph_texture
)
124 glDeleteTextures( 1, &ui_glyph_texture
);
126 ui_glyph_texture
= new_tex
;
127 ui_glyph_override
= 1;
128 ui_glyph_spacing_x
= space_x
;
131 static void ui_default_init(void)
134 if( !ui_glyph_override
)
137 #include "fonts/weiholmir.h"
140 u32 pixels
= 0, total
= 72*72, data
= 0;
141 u8
*image
= malloc( total
);
143 while( pixels
< total
)
145 for( int b
= 31; b
>= 0; b
-- )
147 image
[ pixels
++ ] = (compressed
[data
] & (0x1 << b
))? 0xff: 0x00;
149 if( pixels
>= total
)
158 glGenTextures( 1, &ui_glyph_texture
);
159 glBindTexture( GL_TEXTURE_2D
, ui_glyph_texture
);
161 glTexImage2D( GL_TEXTURE_2D
, 0, GL_R8
, 72, 72, 0, GL_RED
, GL_UNSIGNED_BYTE
, image
);
169 // Setup OpenGL memory
171 SHADER_INIT( shader_ui
);
173 // Generate the buffer we are gonna be drawing to
174 glGenVertexArrays(1, &ui_vao
);
175 glGenBuffers( 1, &ui_vbo
);
176 glGenBuffers( 1, &ui_ebo
);
177 glBindVertexArray( ui_vao
);
179 glBindBuffer( GL_ARRAY_BUFFER
, ui_vbo
);
181 glBufferData( GL_ARRAY_BUFFER
, UI_BUFFER_SIZE
* sizeof( struct ui_vert
), NULL
, GL_DYNAMIC_DRAW
);
182 glBindVertexArray( ui_vao
);
184 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, ui_ebo
);
185 glBufferData( GL_ELEMENT_ARRAY_BUFFER
, UI_INDEX_SIZE
* sizeof( u16
), NULL
, GL_DYNAMIC_DRAW
);
187 u32
const stride
= sizeof( struct ui_vert
);
190 glVertexAttribPointer( 0, 2, GL_SHORT
, GL_FALSE
, stride
, (void *)offsetof( struct ui_vert
, co
) );
191 glEnableVertexAttribArray( 0 );
194 glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE
, GL_FALSE
, stride
, (void *)offsetof( struct ui_vert
, uv
) );
195 glEnableVertexAttribArray( 1 );
198 glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE
, GL_TRUE
, stride
, (void *)offsetof( struct ui_vert
, colour
) );
199 glEnableVertexAttribArray( 2 );
202 glVertexAttribPointer( 3, 4, GL_SHORT
, GL_FALSE
, stride
, (void *)offsetof( struct ui_vert
, clip
) );
203 glEnableVertexAttribArray( 3 );
206 // Initialize default context
208 ui_global_ctx
.verts
= (struct ui_vert
*)malloc( UI_BUFFER_SIZE
* sizeof(struct ui_vert
) );
209 ui_global_ctx
.indices
= (u16
*)malloc( UI_INDEX_SIZE
* sizeof(u16
) );
213 static void ui_default_free(void)
215 if( !ui_glyph_override
)
216 glDeleteTextures( 1, &ui_glyph_texture
);
218 glDeleteVertexArrays( 1, &ui_vao
);
219 glDeleteBuffers( 1, &ui_vbo
);
220 glDeleteBuffers( 1, &ui_ebo
);
222 free( ui_global_ctx
.verts
);
223 free( ui_global_ctx
.indices
);
226 static void ui_draw( ui_ctx
*ctx
, m3x3f view_override
)
228 glBindVertexArray( ui_vao
);
230 glBindBuffer( GL_ARRAY_BUFFER
, ui_vbo
);
231 glBufferSubData( GL_ARRAY_BUFFER
, 0, ctx
->num_verts
* sizeof( struct ui_vert
), ctx
->verts
);
233 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, ui_ebo
);
234 glBufferSubData( GL_ELEMENT_ARRAY_BUFFER
, 0, ctx
->num_indices
* sizeof( u16
), ctx
->indices
);
237 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
238 glBlendEquation(GL_FUNC_ADD
);
240 SHADER_USE( shader_ui
);
242 m3x3f view
= M3X3_IDENTITY
;
246 view_override
= view
;
248 m3x3_translate( view
, (v3f
){ -1.0f
, 1.0f
, 0.0f
} );
249 m3x3_scale( view
, (v3f
){ 1.0f
/((float)vg_window_x
*0.5f
), -1.0f
/((float)vg_window_y
*0.5f
), 1.0f
} );
252 glUniformMatrix3fv( SHADER_UNIFORM( shader_ui
, "uPv" ), 1, GL_FALSE
, (float *)view_override
);
254 glActiveTexture( GL_TEXTURE0
);
255 glBindTexture( GL_TEXTURE_2D
, ui_glyph_texture
);
256 glUniform1i( SHADER_UNIFORM( shader_ui
, "uTexGlyphs" ), 0 );
258 glDrawElements( GL_TRIANGLES
, ctx
->num_indices
, GL_UNSIGNED_SHORT
, (void*)(0) );
260 //vg_info( "Verts: %u, Indices: %u\n", ctx->num_verts, ctx->num_indices );
266 // ===========================================================================================================
268 static void ui_rect_copy( ui_rect src
, ui_rect dst
)
276 static void ui_rect_pad( ui_rect rect
, ui_px pad
)
284 static void ui_vis_rect( ui_rect rect
, u32 colour
)
292 p1
[0] = rect
[0]+rect
[2];
293 p1
[1] = rect
[1]+rect
[3];
295 vg_line( p0
, (v2f
){p1
[0],p0
[1]}, colour
);
296 vg_line( (v2f
){p1
[0],p0
[1]}, p1
, colour
);
297 vg_line( p1
, (v2f
){p0
[0],p1
[1]}, colour
);
298 vg_line( (v2f
){p0
[0],p1
[1]}, p0
, colour
);
303 // ===========================================================================================================
305 static struct ui_qnode
*ui_current( ui_ctx
*ctx
)
307 return &ctx
->stack
[ ctx
->stack_count
-1 ];
310 static void ui_new_node( ui_ctx
*ctx
)
312 if( ctx
->stack_count
== vg_list_size( ctx
->stack
) )
313 vg_exiterr( "[UI] Stack overflow while creating box!" );
315 struct ui_qnode
*parent
= &ctx
->stack
[ ctx
->stack_count
-1 ];
316 struct ui_qnode
*node
= &ctx
->stack
[ ctx
->stack_count
++ ];
317 ui_rect_copy( ctx
->cursor
, node
->rect
);
319 if( parent
->mouse_over
)
321 if( ctx
->mouse
[0] >= node
->rect
[0] && ctx
->mouse
[0] <= node
->rect
[0]+node
->rect
[2] &&
322 ctx
->mouse
[1] >= node
->rect
[1] && ctx
->mouse
[1] <= node
->rect
[1]+node
->rect
[3] )
323 node
->mouse_over
= 1;
325 node
->mouse_over
= 0;
329 node
->mouse_over
= 0;
333 static int ui_hasmouse( ui_ctx
*ctx
)
335 struct ui_qnode
*node
= ui_current( ctx
);
336 return (node
->mouse_over
&& (node
->capture_id
== ctx
->capture_mouse_id
));
339 static void ui_end( ui_ctx
*ctx
)
341 struct ui_qnode
*node
= &ctx
->stack
[ --ctx
->stack_count
];
343 ui_rect_copy( node
->rect
, ctx
->cursor
);
344 ui_vis_rect( ctx
->cursor
,
345 (node
->mouse_over
&& (node
->capture_id
== ctx
->capture_mouse_id
))? 0xffff0000: 0xff0000ff );
348 static void ui_end_down( ui_ctx
*ctx
)
350 ui_px height
= ui_current( ctx
)->rect
[3];
352 ctx
->cursor
[1] += height
;
355 static void ui_end_right( ui_ctx
*ctx
)
357 ui_px width
= ui_current( ctx
)->rect
[2];
359 ctx
->cursor
[0] += width
;
362 static void ui_fill_y( ui_ctx
*ctx
)
364 struct ui_qnode
*node
= ui_current( ctx
);
365 ctx
->cursor
[3] = node
->rect
[3] - (ctx
->cursor
[1]-node
->rect
[1]);
368 static void ui_fill_x( ui_ctx
*ctx
)
370 struct ui_qnode
*node
= ui_current( ctx
);
371 ctx
->cursor
[2] = node
->rect
[2] - (ctx
->cursor
[0]-node
->rect
[0]);
374 // Alignment: | [] | -> | []|
375 static void ui_align_bottom( ui_ctx
*ctx
)
377 struct ui_qnode
*node
= ui_current( ctx
);
378 ctx
->cursor
[1] = node
->rect
[1] + node
->rect
[3] - ctx
->cursor
[3];
381 static void ui_align_right( ui_ctx
*ctx
)
383 struct ui_qnode
*node
= ui_current( ctx
);
384 ctx
->cursor
[0] = node
->rect
[0] + node
->rect
[2] - ctx
->cursor
[2];
387 static void ui_align_top( ui_ctx
*ctx
)
389 ctx
->cursor
[1] = ui_current( ctx
)->rect
[1];
392 static void ui_align_left( ui_ctx
*ctx
)
394 ctx
->cursor
[0] = ui_current( ctx
)->rect
[0];
397 static void ui_clamp_rect( ui_rect parent
, ui_rect dest
)
399 dest
[0] = vg_min( parent
[0] + parent
[2] - dest
[2], dest
[0] );
400 dest
[1] = vg_min( parent
[1] + parent
[3] - dest
[3], dest
[1] );
401 dest
[0] = vg_max( parent
[0], dest
[0] );
402 dest
[1] = vg_max( parent
[1], dest
[1] );
405 static u32
ui_group_id( ui_ctx
*ctx
, u32 lesser_unique
)
407 return ctx
->id_base
| lesser_unique
;
410 static void ui_capture_mouse( ui_ctx
*ctx
, u32 id
)
412 u32 group_uid
= ui_group_id(ctx
,id
);
414 struct ui_qnode
*node
= &ctx
->stack
[ ctx
->stack_count
-1 ];
415 node
->capture_id
= group_uid
;
417 if( !ctx
->capture_lock
&& node
->mouse_over
)
419 ctx
->capture_mouse_id
= group_uid
;
423 static void ui_set_clip( ui_ctx
*ctx
, ui_rect clip
)
425 ctx
->clipping
[0] = clip
[0];
426 ctx
->clipping
[1] = clip
[1];
427 ctx
->clipping
[2] = clip
[0] + clip
[2];
428 ctx
->clipping
[3] = clip
[1] + clip
[3];
431 static void ui_release_clip( ui_ctx
*ctx
)
433 ctx
->clipping
[0] = -32000;
434 ctx
->clipping
[1] = -32000;
435 ctx
->clipping
[2] = 32000;
436 ctx
->clipping
[3] = 32000;
440 // ===========================================================================================================
442 static struct ui_vert
*ui_fill_rect_uv( ui_ctx
*ctx
, ui_rect rect
, u32 colour
, ui_px uv
[4] )
444 struct ui_vert
*vertices
= &ctx
->verts
[ ctx
->num_verts
];
445 vertices
[0].co
[0] = rect
[0];
446 vertices
[0].co
[1] = rect
[1];
447 vertices
[0].uv
[0] = uv
[0];
448 vertices
[0].uv
[1] = uv
[1];
449 vertices
[0].colour
= colour
;
450 vertices
[1].co
[0] = rect
[0]+rect
[2];
451 vertices
[1].co
[1] = rect
[1];
452 vertices
[1].uv
[0] = uv
[2];
453 vertices
[1].uv
[1] = uv
[1];
454 vertices
[1].colour
= colour
;
455 vertices
[2].co
[0] = rect
[0]+rect
[2];
456 vertices
[2].co
[1] = rect
[1]+rect
[3];
457 vertices
[2].uv
[0] = uv
[2];
458 vertices
[2].uv
[1] = uv
[3];
459 vertices
[2].colour
= colour
;
460 vertices
[3].co
[0] = rect
[0];
461 vertices
[3].co
[1] = rect
[1]+rect
[3];
462 vertices
[3].uv
[0] = uv
[0];
463 vertices
[3].uv
[1] = uv
[3];
464 vertices
[3].colour
= colour
;
465 u16 ind_start
= ctx
->num_verts
;
466 u16
*indices
= &ctx
->indices
[ ctx
->num_indices
];
468 ui_rect_copy( ctx
->clipping
, vertices
[0].clip
);
469 ui_rect_copy( ctx
->clipping
, vertices
[1].clip
);
470 ui_rect_copy( ctx
->clipping
, vertices
[2].clip
);
471 ui_rect_copy( ctx
->clipping
, vertices
[3].clip
);
473 indices
[0] = ind_start
+0;
474 indices
[1] = ind_start
+2;
475 indices
[2] = ind_start
+1;
477 indices
[3] = ind_start
+0;
478 indices
[4] = ind_start
+3;
479 indices
[5] = ind_start
+2;
481 ctx
->num_indices
+= 6;
487 static struct ui_vert
*ui_fill_rect( ui_ctx
*ctx
, ui_rect rect
, u32 colour
)
489 return ui_fill_rect_uv( ctx
, rect
, colour
, (ui_px
[4]){ 4,124,4,124 } );
492 static void ui_text( ui_ctx
*ctx
, const char *str
, ui_px scale
, int alignment
)
496 text_cursor
[0] = ctx
->cursor
[0];
497 text_cursor
[1] = ctx
->cursor
[1];
498 text_cursor
[2] = scale
*8;
499 text_cursor
[3] = scale
*8;
501 u32 current_colour
= ctx
->override_colour
;
503 const char *_c
= str
;
505 while( (c
= *(_c
++)) )
509 text_cursor
[1] += 10*scale
;
510 text_cursor
[0] = ctx
->cursor
[0];
513 else if( c
>= 33 && c
<= 126 )
516 u8 glyph_index
= c
- 32;
517 glyph_base
[0] = glyph_index
&0xf;
518 glyph_base
[1] = (glyph_index
-glyph_base
[0])>>4;
523 ui_fill_rect_uv( ctx
, text_cursor
, current_colour
,
528 128-(glyph_base
[1]+8)
531 else if( c
== '\x1B' )
535 for( int i
= 0; i
< 3; i
++ )
545 case '0': current_colour
= 0xffffffff; break;
546 case '3'|'1'<<8: current_colour
= 0xff201fee; break;
547 case '3'|'2'<<8: current_colour
= 0xff37e420; break;
548 case '3'|'3'<<8: current_colour
= 0xff0ed8e2; break;
549 case '3'|'4'<<8: current_colour
= 0xfff15010; break;
550 case '3'|'5'<<8: current_colour
= 0xffee20ee; break;
551 case '3'|'6'<<8: current_colour
= 0xffeeee20; break;
552 case '3'|'7'<<8: current_colour
= 0xffffffff; break;
558 colour_id
|= _c
[i
] << (i
*8);
569 text_cursor
[0] += (ui_glyph_spacing_x
*scale
)/2;
574 // ====================================================================
576 static void ui_begin( ui_ctx
*ctx
, ui_px res_x
, ui_px res_y
)
580 ctx
->cursor
[2] = res_x
;
581 ctx
->cursor
[3] = res_y
;
583 ui_rect_copy( ctx
->cursor
, ctx
->stack
[0].rect
);
584 ctx
->stack
[0].mouse_over
= 1;
586 ctx
->stack_count
= 1;
589 ctx
->num_indices
= 0;
591 ui_release_clip( ctx
);
594 static void ui_resolve( ui_ctx
*ctx
)
596 if( ctx
->stack_count
-1 )
597 vg_exiterr( "[UI] Mismatched node create/drestroy!" );
599 if( ctx
->click_state
== 3 || ctx
->click_state
== 0 )
601 ctx
->capture_lock
= 0;
606 // ====================================================================
608 static void ui_set_mouse( ui_ctx
*ctx
, int x
, int y
, int click_state
)
613 ctx
->click_state
= click_state
;
616 // High level controls
617 // ====================================================================
625 ui_px drag_offset
[2];
628 static int ui_button( ui_ctx
*ctx
, u32 id
)
632 ui_capture_mouse( ctx
, id
);
634 if( ui_hasmouse(ctx
) )
636 ui_fill_rect( ctx
, ctx
->cursor
, 0xffcccccc );
638 if( ctx
->click_state
== 1 )
639 ctx
->capture_lock
= 1;
640 else if( ctx
->capture_lock
&& ctx
->click_state
== 3 )
646 ui_fill_rect( ctx
, ctx
->cursor
, 0xff999999 );
652 static int ui_window( ui_ctx
*ctx
, struct ui_window
*window
, u32 control_group
)
654 ctx
->id_base
= control_group
<< 16;
658 window
->transform
[0] = ctx
->mouse
[0]+window
->drag_offset
[0];
659 window
->transform
[1] = ctx
->mouse
[1]+window
->drag_offset
[1];
661 ui_clamp_rect( ctx
->stack
[0].rect
, window
->transform
);
663 if( ctx
->click_state
== 0 || ctx
->click_state
== 3 )
669 ui_rect_copy( window
->transform
, ctx
->cursor
);
673 ui_capture_mouse( ctx
, __COUNTER__
);
679 ui_capture_mouse( ctx
, __COUNTER__
);
681 struct ui_vert
*drag_bar
= ui_fill_rect( ctx
, ctx
->cursor
, 0xff555555 );
686 ui_text( ctx
, window
->title
, 2, 0 );
691 ui_align_right( ctx
);
693 ui_rect_pad( ctx
->cursor
, 4 );
695 if( ui_button( ctx
, __COUNTER__
) )
697 vg_info( "Click clacked\n" );
700 ui_text( ctx
, "x", 2, 0 );
703 if( ui_hasmouse( ctx
) )
705 drag_bar
[0].colour
= 0xff777777;
706 drag_bar
[1].colour
= 0xff777777;
707 drag_bar
[2].colour
= 0xff777777;
708 drag_bar
[3].colour
= 0xff777777;
711 if( ctx
->click_state
== 1 )
714 window
->drag_offset
[0] = window
->transform
[0]-ctx
->mouse
[0];
715 window
->drag_offset
[1] = window
->transform
[1]-ctx
->mouse
[1];