imgui stuff
[vg.git] / vg_imgui.h
1 /* Copyright (C) 2021-2023 Harry Godden (hgn) - All Rights Reserved */
2
3 /*
4 * Principles:
5 *
6 * 1. layout is defined by subdividing
7 * 2. a parent node should never be resized by the content after creation
8 * 3. when the ui is in an interactive state, no controls should ever move
9 */
10
11
12 #ifndef VG_IMGUI_H
13 #define VG_IMGUI_H
14
15 #define VG_GAME
16 #include "vg/vg.h"
17 #include "vg/vg_tex.h"
18 #include "vg/vg_shader.h"
19
20 typedef i16 ui_px;
21 typedef u32 ui_colour;
22 typedef ui_px ui_rect[4];
23 typedef struct ui_vert ui_vert;
24
25 enum ui_axis {
26 k_ui_axis_h = 0x0u,
27 k_ui_axis_v = 0x1u,
28 };
29
30 /* Relative to cursor p0 */
31 enum ui_align
32 { /* DC BA */
33 k_ui_align_left = 0x0000| 0x00,
34 k_ui_align_right = 0x0000| 0x01,
35 k_ui_align_center = 0x0000| 0x02,
36
37 k_ui_align_middle = 0x0100,
38 k_ui_align_middle_left = 0x0100| 0x00,
39 k_ui_align_middle_right = 0x0100| 0x01,
40 k_ui_align_middle_center = 0x0100| 0x02,
41
42 k_ui_align_bottom = 0x0200,
43 k_ui_align_bottom_left = 0x0200| 0x00,
44 k_ui_align_bottom_right = 0x0200| 0x01,
45 k_ui_align_bottom_center = 0x0200| 0x02,
46 };
47
48 #pragma pack(push,1)
49 struct ui_vert
50 {
51 ui_px co[2];
52 u8 uv[2];
53 u32 colour;
54 };
55 #pragma pack(pop)
56
57 struct
58 {
59 struct ui_vert *vertex_buffer;
60 u16 *indice_buffer;
61 u32 max_verts, max_indices,
62 cur_vert, cur_indice,
63 vert_start, indice_start;
64
65 GLuint tex_glyphs, vao, vbo, ebo;
66
67 ui_px mouse[2], mouse_click[2];
68 u32 mouse_state[2];
69 u32 ignore_input_frames;
70
71 ui_rect click_fader;
72 float click_fade_opacity;
73 }
74
75 static vg_uictx;
76 static struct vg_shader _shader_ui =
77 {
78 .name = "[vg] ui",
79 .link = NULL,
80 .vs =
81 {
82 .orig_file = NULL,
83 .static_src =
84 "layout (location=0) in vec2 a_co;"
85 "layout (location=1) in vec2 a_uv;"
86 "layout (location=2) in vec4 a_colour;"
87 "uniform mat3 uPv;"
88 ""
89 "out vec2 aTexCoords;"
90 "out vec4 aColour;"
91 "out vec2 aWsp;"
92 ""
93 "void main()"
94 "{"
95 "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
96 "aTexCoords = a_uv * 0.0078125;"
97 "aColour = a_colour;"
98
99 "aWsp = a_co;"
100 "}",
101 },
102 .fs =
103 {
104 .orig_file = NULL,
105 .static_src =
106 "uniform sampler2D uTexGlyphs;"
107 "out vec4 FragColor;"
108 ""
109 "in vec2 aTexCoords;"
110 "in vec4 aColour;"
111 ""
112 "in vec2 aWsp;"
113 ""
114 "void main()"
115 "{"
116
117 "vec4 glyph = vec4(1.0,1.0,1.0,1.0);"
118
119 "if( aColour.a == 0.0 )"
120 "{"
121 "glyph = texture( uTexGlyphs, aTexCoords );"
122 "glyph.a = smoothstep( 0.47, 0.53, glyph.r );"
123 "}"
124 "else"
125 "{"
126 "glyph.a = aColour.a;"
127 "}"
128
129 "FragColor = vec4( aColour.rgb, glyph.a );"
130 "}"
131 }
132 };
133
134 static struct vg_shader _shader_ui_image =
135 {
136 .name = "[vg] ui_image",
137 .link = NULL,
138 .vs =
139 {
140 .orig_file = NULL,
141 .static_src =
142 "layout (location=0) in vec2 a_co;"
143 "layout (location=1) in vec2 a_uv;"
144 "layout (location=2) in vec4 a_colour;"
145 "uniform mat3 uPv;"
146
147 "out vec2 aTexCoords;"
148 "out vec4 aColour;"
149 "out vec2 aWsp;"
150
151 "void main()"
152 "{"
153 "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );"
154 "aTexCoords = a_uv * 0.0078125;"
155 "aColour = a_colour;"
156
157 "aWsp = a_co;"
158 "}",
159 },
160 .fs =
161 {
162 .orig_file = NULL,
163 .static_src =
164 "uniform sampler2D uTexImage;"
165 "out vec4 FragColor;"
166
167 "in vec2 aTexCoords;"
168 "in vec4 aColour;"
169 "in vec2 aWsp;"
170
171 "void main()"
172 "{"
173 "vec4 colour = texture( uTexImage, aTexCoords );"
174
175 /* wtf is this?? */
176 #if 0
177 "float value = dot(vec4(1.0),colour)*0.25;"
178
179 "vec3 col = vec3(pow(cos(value*3.14159265*2.0)*0.5+0.5,0.5))"
180 "* vec3(step(value,0.5),0.3,step(1.0-value,0.5));"
181 "FragColor = vec4( col*4.0, 1.0 );"
182 #endif
183
184 "FragColor = colour;"
185 "}"
186 }
187 };
188 #define UI_GLYPH_SPACING_X 8
189
190 VG_STATIC void _vg_ui_init(void)
191 {
192 if( !vg_shader_compile( &_shader_ui ) ||
193 !vg_shader_compile( &_shader_ui_image ) )
194 vg_fatal_error( "Failed to compile ui shader" );
195
196 /*
197 * Vertex buffer
198 * ----------------------------------------
199 */
200
201 vg_uictx.max_indices = 20000;
202 vg_uictx.max_verts = 30000;
203
204 /* Generate the buffer we are gonna be drawing to */
205 glGenVertexArrays( 1, &vg_uictx.vao );
206 glGenBuffers( 1, &vg_uictx.vbo );
207 glGenBuffers( 1, &vg_uictx.ebo );
208
209 glBindVertexArray( vg_uictx.vao );
210 glBindBuffer( GL_ARRAY_BUFFER, vg_uictx.vbo );
211
212 glBufferData( GL_ARRAY_BUFFER,
213 vg_uictx.max_verts * sizeof( struct ui_vert ),
214 NULL, GL_DYNAMIC_DRAW );
215 glBindVertexArray( vg_uictx.vao );
216
217 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vg_uictx.ebo );
218 glBufferData( GL_ELEMENT_ARRAY_BUFFER,
219 vg_uictx.max_indices * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW );
220
221 VG_CHECK_GL_ERR();
222
223 /* Set pointers */
224 u32 const stride = sizeof( struct ui_vert );
225
226 /* XY */
227 glVertexAttribPointer( 0, 2, GL_SHORT, GL_FALSE, stride,
228 (void *)offsetof( struct ui_vert, co ) );
229 glEnableVertexAttribArray( 0 );
230
231 /* UV */
232 glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE, GL_FALSE, stride,
233 (void *)offsetof( struct ui_vert, uv ) );
234 glEnableVertexAttribArray( 1 );
235
236 /* COLOUR */
237 glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride,
238 (void *)offsetof( struct ui_vert, colour ) );
239 glEnableVertexAttribArray( 2 );
240
241 VG_CHECK_GL_ERR();
242
243 /* Alloc RAM default context */
244 u32 vert_size = vg_uictx.max_verts*sizeof(struct ui_vert),
245 inds_size = vg_align8( vg_uictx.max_indices*sizeof(u16) );
246
247 vg_uictx.vertex_buffer = vg_linear_alloc( vg_mem.rtmemory, vert_size );
248 vg_uictx.indice_buffer = vg_linear_alloc( vg_mem.rtmemory, inds_size );
249
250 /* font
251 * -----------------------------------------------------
252 */
253
254 /* Load default font */
255 u32 compressed[] = {
256 #include "vg/vg_pxfont_thin.h"
257 };
258
259 u32 pixels = 0, total = 256*256, data = 0;
260 u8 image[256*256];
261
262 while( pixels < total ){
263 for( int b = 31; b >= 0; b-- ){
264 image[ pixels ++ ] = (compressed[data] & (0x1u << b))? 0xffu: 0x00u;
265
266 if( pixels >= total ){
267 total = 0;
268 break;
269 }
270 }
271 data++;
272 }
273
274 glGenTextures( 1, &vg_uictx.tex_glyphs );
275 glBindTexture( GL_TEXTURE_2D, vg_uictx.tex_glyphs );
276 glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, 256, 256, 0,
277 GL_RED, GL_UNSIGNED_BYTE, image );
278
279 VG_CHECK_GL_ERR();
280 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
281 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
282 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
283 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
284 }
285
286 enum ui_shader {
287 k_ui_shader_colour,
288 k_ui_shader_image
289 };
290
291 static void rect_copy( ui_rect a, ui_rect b )
292 {
293 for( int i=0; i<4; i++ )
294 b[i] = a[i];
295 }
296
297 VG_STATIC void ui_flush( enum ui_shader shader )
298 {
299 u32 vertex_offset = vg_uictx.vert_start*sizeof(ui_vert),
300 vertex_count = vg_uictx.cur_vert-vg_uictx.vert_start,
301 vertex_size = vertex_count*sizeof(ui_vert),
302
303 indice_offset = vg_uictx.indice_start*sizeof(u16),
304 indice_count = vg_uictx.cur_indice-vg_uictx.indice_start,
305 indice_size = indice_count * sizeof(u16);
306
307 if( !vertex_size || !indice_size )
308 return;
309
310 glBindVertexArray( vg_uictx.vao );
311 glBindBuffer( GL_ARRAY_BUFFER, vg_uictx.vbo );
312 glBufferSubData( GL_ARRAY_BUFFER, vertex_offset, vertex_size,
313 vg_uictx.vertex_buffer+vg_uictx.vert_start );
314
315 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vg_uictx.ebo );
316 glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, indice_offset, indice_size,
317 vg_uictx.indice_buffer+vg_uictx.indice_start );
318
319 glEnable( GL_BLEND );
320 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
321 glBlendEquation( GL_FUNC_ADD );
322
323 m3x3f view = M3X3_IDENTITY;
324 m3x3_translate( view, (v3f){ -1.0f, 1.0f, 0.0f } );
325 m3x3_scale( view, (v3f){ 1.0f/((float)vg.window_x*0.5f),
326 -1.0f/((float)vg.window_y*0.5f), 1.0f } );
327
328 if( shader == k_ui_shader_colour ){
329 glUseProgram( _shader_ui.id );
330
331 glUniformMatrix3fv( glGetUniformLocation( _shader_ui.id, "uPv" ), 1,
332 GL_FALSE, (float *)view );
333
334 glActiveTexture( GL_TEXTURE0 );
335 glBindTexture( GL_TEXTURE_2D, vg_uictx.tex_glyphs );
336 glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexGlyphs" ), 0 );
337 }
338 else if( shader == k_ui_shader_image ){
339 glUseProgram( _shader_ui_image.id );
340 glUniformMatrix3fv( glGetUniformLocation( _shader_ui_image.id, "uPv" ), 1,
341 GL_FALSE, (float *)view );
342 glUniform1i( glGetUniformLocation(_shader_ui_image.id,"uTexImage"), 0 );
343 }
344 else
345 vg_fatal_error( "Invalid UI shader (%d)\n", shader );
346
347 glDrawElements( GL_TRIANGLES, indice_count, GL_UNSIGNED_SHORT,
348 (void *)(vg_uictx.indice_start*sizeof(u16)) );
349
350 glDisable( GL_BLEND );
351
352 vg_uictx.indice_start = vg_uictx.cur_indice;
353 vg_uictx.vert_start = vg_uictx.cur_vert;
354 }
355
356 static void ui_fill_rect( ui_rect rect, u32 colour, ui_px uv[4] )
357 {
358 /* this if far from ideal but stops us from crashing */
359 if( (vg_uictx.cur_vert + 4 > vg_uictx.max_verts) ||
360 (vg_uictx.cur_indice + 6 > vg_uictx.max_indices))
361 return;
362
363 struct ui_vert *vertices = &vg_uictx.vertex_buffer[ vg_uictx.cur_vert ];
364 u16 *indices = &vg_uictx.indice_buffer[ vg_uictx.cur_indice ];
365
366 for( int i=0; i<4; i++ ){
367 vertices[i].colour = colour;
368 }
369
370 vertices[0].co[0] = rect[0];
371 vertices[0].co[1] = rect[1];
372 vertices[0].uv[0] = uv[0];
373 vertices[0].uv[1] = uv[1];
374 vertices[1].co[0] = rect[0]+rect[2];
375 vertices[1].co[1] = rect[1];
376 vertices[1].uv[0] = uv[2];
377 vertices[1].uv[1] = uv[1];
378 vertices[2].co[0] = rect[0]+rect[2];
379 vertices[2].co[1] = rect[1]+rect[3];
380 vertices[2].uv[0] = uv[2];
381 vertices[2].uv[1] = uv[3];
382 vertices[3].co[0] = rect[0];
383 vertices[3].co[1] = rect[1]+rect[3];
384 vertices[3].uv[0] = uv[0];
385 vertices[3].uv[1] = uv[3];
386 u16 ind_start = vg_uictx.cur_vert;
387
388 u16 start = vg_uictx.cur_vert;
389 u32 mesh[] = { 0,2,1, 0,3,2 };
390
391 for( u32 i=0; i<vg_list_size(mesh); i++ ){
392 indices[i] = start+mesh[i];
393 }
394
395 vg_uictx.cur_indice += 6;
396 vg_uictx.cur_vert += 4;
397 }
398
399 static void ui_fill( ui_rect rect, u32 colour )
400 {
401 ui_fill_rect( rect, colour, (ui_px[4]){ 4,4,4,4 } );
402 }
403
404 static void ui_outline( ui_rect rect, ui_px thickness, u32 colour )
405 {
406 /* this if far from ideal but stops us from crashing */
407 if( (vg_uictx.cur_vert + 8 > vg_uictx.max_verts) ||
408 (vg_uictx.cur_indice + 24 > vg_uictx.max_indices))
409 return;
410
411 struct ui_vert *vertices = &vg_uictx.vertex_buffer[ vg_uictx.cur_vert ];
412 u16 *indices = &vg_uictx.indice_buffer[ vg_uictx.cur_indice ];
413
414 for( int i=0; i<8; i++ ){
415 vertices[i].uv[0] = 4;
416 vertices[i].uv[1] = 4;
417 vertices[i].colour = colour;
418 }
419
420 vertices[0].co[0] = rect[0];
421 vertices[0].co[1] = rect[1];
422 vertices[1].co[0] = rect[0]+rect[2];
423 vertices[1].co[1] = rect[1];
424 vertices[2].co[0] = rect[0]+rect[2];
425 vertices[2].co[1] = rect[1]+rect[3];
426 vertices[3].co[0] = rect[0];
427 vertices[3].co[1] = rect[1]+rect[3];
428 vertices[4].co[0] = vertices[0].co[0]-thickness;
429 vertices[4].co[1] = vertices[0].co[1]-thickness;
430 vertices[5].co[0] = vertices[1].co[0]+thickness;
431 vertices[5].co[1] = vertices[1].co[1]-thickness;
432 vertices[6].co[0] = vertices[2].co[0]+thickness;
433 vertices[6].co[1] = vertices[2].co[1]+thickness;
434 vertices[7].co[0] = vertices[3].co[0]-thickness;
435 vertices[7].co[1] = vertices[3].co[1]+thickness;
436
437 u16 start = vg_uictx.cur_vert;
438 u32 mesh[] = { 0,5,4, 0,1,5, 1,6,5, 1,2,6, 2,7,6, 2,3,7, 3,4,7, 3,0,4 };
439
440 for( u32 i=0; i<vg_list_size(mesh); i++ ){
441 indices[i] = start+mesh[i];
442 }
443
444 vg_uictx.cur_indice += 24;
445 vg_uictx.cur_vert += 8;
446 }
447
448 static void ui_split_px( ui_rect rect,
449 enum ui_axis other, ui_px width, ui_px pad,
450 ui_rect l, ui_rect r )
451 {
452 enum ui_axis dir = other ^ 0x1;
453
454 ui_rect temp;
455 rect_copy( rect, temp );
456
457 l[ dir ] = temp[ dir ] + pad;
458 r[ dir ] = temp[ dir ] + width + (pad/2);
459 l[ other ] = temp[ other ] + pad;
460 r[ other ] = temp[ other ] + pad;
461 l[ 2+dir ] = width - ((3*pad)/2);
462 r[ 2+dir ] = temp[ 2+dir ] - width - ((3*pad)/2);
463 l[ 2+other ] = temp[ 2+other ] - pad*2;
464 r[ 2+other ] = temp[ 2+other ] - pad*2;
465 }
466
467 static void ui_rect_center( ui_rect parent, ui_rect rect )
468 {
469 rect[0] = parent[0] + (parent[2]-rect[2])/2;
470 rect[1] = parent[1] + (parent[3]-rect[3])/2;
471 }
472
473 static void ui_fit_item( ui_rect rect, ui_px size[2], ui_rect d )
474 {
475 i32 rp = (i32)rect[2] * (i32)size[1],
476 rc = (i32)size[0] * (i32)rect[3];
477
478 enum ui_axis dir, other;
479 if( rc > rp ) dir = k_ui_axis_h;
480 else dir = k_ui_axis_v;
481 other = dir ^ 0x1;
482
483 d[2+dir] = rect[2+dir];
484 d[2+other] = (rect[2+dir] * size[other]) / size[dir];
485
486 ui_rect_center( rect, d );
487 }
488
489 static void ui_split_ratio( ui_rect rect, enum ui_axis dir, float ratio,
490 ui_px pad, ui_rect l, ui_rect r )
491 {
492 ui_px width = (float)rect[ 2+(dir^0x1) ] * ratio;
493 ui_split_px( rect, dir, width, pad, l, r );
494 }
495
496 static void ui_rect_pad( ui_rect rect, ui_px pad )
497 {
498 rect[0] += pad;
499 rect[1] += pad;
500 rect[2] -= pad*2;
501 rect[3] -= pad*2;
502 }
503
504 static ui_px ui_text_line_width( const char *str )
505 {
506 int length = 0;
507 const char *_c = str;
508 char c;
509
510 while( (c = *(_c ++)) ){
511 if( c >= 32 && c <= 126 )
512 length ++;
513 else if( c == '\n' )
514 break;
515 }
516
517 return length * 8;
518 }
519
520 static ui_px ui_text_string_height( const char *str )
521 {
522 int height = 1;
523 const char *_c = str;
524 char c;
525
526 while( (c = *(_c ++)) ){
527 if( c == '\n' ) height ++;
528 }
529
530 return height * 14;
531 }
532
533 static ui_px ui_text_aligned_x( const char *str, ui_rect rect, ui_px scale,
534 enum ui_align align )
535 {
536 if( align == k_ui_align_left ){
537 return rect[0];
538 }
539 else{
540 ui_px width = ui_text_line_width( str ) * scale;
541
542 if( align == k_ui_align_right )
543 return rect[0] + rect[2]-width;
544 else
545 return rect[0] + (rect[2]-width)/2;
546 }
547 }
548
549 static ui_px ui_min( ui_px a, ui_px b ){ return a<b?a:b; }
550 static ui_px ui_max( ui_px a, ui_px b ){ return a>b?a:b; }
551 static ui_px ui_clamp( ui_px a, ui_px min, ui_px max )
552 {
553 return ui_min( max, ui_max( a, min ) );
554 }
555
556 static int ui_clip( ui_rect parent, ui_rect child, ui_rect clipped )
557 {
558 ui_px parent_max[2], child_max[2];
559 parent_max[0] = parent[0]+parent[2];
560 parent_max[1] = parent[1]+parent[3];
561 child_max[0] = child[0]+child[2];
562 child_max[1] = child[1]+child[3];
563
564 clipped[0] = ui_clamp( child[0], parent[0], parent_max[0] );
565 clipped[1] = ui_clamp( child[1], parent[1], parent_max[1] );
566 clipped[2] = ui_clamp( child_max[0], parent[0], parent_max[0] );
567 clipped[3] = ui_clamp( child_max[1], parent[1], parent_max[1] );
568
569 if( clipped[0] == clipped[2] ||
570 clipped[1] == clipped[3] )
571 return 0;
572
573 clipped[2] -= clipped[0];
574 clipped[3] -= clipped[1];
575
576 return 1;
577 }
578
579 static int ui_inside_rect( ui_rect rect, ui_px co[2] )
580 {
581 if( co[0] >= rect[0] &&
582 co[1] >= rect[1] &&
583 co[0] <= rect[0]+rect[2] &&
584 co[1] <= rect[1]+rect[3] )
585 {
586 return 1;
587 }
588 else
589 return 0;
590 }
591
592 static int ui_click_down(void)
593 {
594 if( vg_uictx.ignore_input_frames ) return 0;
595
596 if( (vg_uictx.mouse_state[0] & SDL_BUTTON(SDL_BUTTON_LEFT)) &&
597 !(vg_uictx.mouse_state[1] & SDL_BUTTON(SDL_BUTTON_LEFT)) )
598 return 1;
599 else
600 return 0;
601 }
602
603 static int ui_clicking(void)
604 {
605 if( vg_uictx.ignore_input_frames ) return 0;
606 return vg_uictx.mouse_state[0] & SDL_BUTTON(SDL_BUTTON_LEFT);
607 }
608
609 static int ui_click_up(void)
610 {
611 if( vg_uictx.ignore_input_frames ) return 0;
612 if( (vg_uictx.mouse_state[1] & SDL_BUTTON(SDL_BUTTON_LEFT)) &&
613 !(vg_uictx.mouse_state[0] & SDL_BUTTON(SDL_BUTTON_LEFT)) )
614 return 1;
615 else
616 return 0;
617 }
618
619 static void ui_prerender(void)
620 {
621 int x, y;
622 vg_uictx.mouse_state[1] = vg_uictx.mouse_state[0];
623 vg_uictx.mouse_state[0] = SDL_GetMouseState( &x, &y );
624 vg_uictx.mouse[0] = x;
625 vg_uictx.mouse[1] = y;
626
627 vg_uictx.cur_vert = 0;
628 vg_uictx.cur_indice = 0;
629 vg_uictx.vert_start = 0;
630 vg_uictx.indice_start = 0;
631
632 if( vg_uictx.ignore_input_frames ){
633 vg_uictx.ignore_input_frames --;
634 return;
635 }
636
637 if( ui_click_down() ){
638 vg_uictx.mouse_click[0] = vg_uictx.mouse[0];
639 vg_uictx.mouse_click[1] = vg_uictx.mouse[1];
640 }
641 }
642
643 static void ui_text( ui_rect rect, const char *str, ui_px scale,
644 enum ui_align align )
645 {
646 ui_rect text_cursor;
647 u32 current_colour = 0x00ffffff;
648
649 const char *_c = str;
650 u8 c;
651
652 text_cursor[0] = ui_text_aligned_x( str, rect, scale, align );
653 text_cursor[1] = rect[1];
654 text_cursor[2] = 8*scale;
655 text_cursor[3] = 14*scale;
656
657 if( align & (k_ui_align_middle|k_ui_align_bottom) ){
658 ui_px height = ui_text_string_height( str ) * scale;
659
660 if( align & k_ui_align_bottom )
661 text_cursor[1] += rect[3]-height;
662 else
663 text_cursor[1] += (rect[3]-height)/2;
664 }
665
666 while( (c = *(_c ++)) ){
667 if( c == '\n' ){
668 text_cursor[1] += 14*scale;
669 text_cursor[0] = ui_text_aligned_x( _c, rect, scale, align );
670 continue;
671 }
672 else if( c >= 33 ){
673 u8 glyph_base[2];
674 u8 glyph_index = c;
675 glyph_base[0] = glyph_index & 0xf;
676 glyph_base[1] = (glyph_index-glyph_base[0])>>4;
677
678 glyph_base[0] *= 8;
679 glyph_base[1] *= 8;
680
681 ui_rect rect_char;
682
683 if( ui_clip( rect, text_cursor, rect_char ) ){
684 ui_fill_rect( rect_char, current_colour, (ui_px[4])
685 {
686 glyph_base[0]+2,
687 glyph_base[1]+1,
688 glyph_base[0]+6,
689 glyph_base[1]+8
690 });
691 }
692 }
693 else if( c == '\x1B' ){
694 /* vt codes */
695 _c ++;
696 u16 colour_id = 0;
697 for( int i=0; i<3; i ++ ){
698 if( _c[i] ){
699 if( _c[i] == 'm' ){
700 _c = _c + i + 1;
701
702 switch( colour_id ){
703 case '0': current_colour = 0x00ffffff; break;
704 case '3'|'1'<<8: current_colour = 0x00201fee; break;
705 case '3'|'2'<<8: current_colour = 0x0037e420; break;
706 case '3'|'3'<<8: current_colour = 0x000ed8e2; break;
707 case '3'|'4'<<8: current_colour = 0x00f15010; break;
708 case '3'|'5'<<8: current_colour = 0x00ee20ee; break;
709 case '3'|'6'<<8: current_colour = 0x00eeee20; break;
710 case '3'|'7'<<8: current_colour = 0x00ffffff; break;
711 }
712
713 break;
714 }
715
716 colour_id |= _c[i] << (i*8);
717 }
718 else{
719 _c = _c +i;
720 break;
721 }
722 }
723
724 continue;
725 }
726 else if( c == '\t' ){
727 text_cursor[0] += UI_GLYPH_SPACING_X*scale*4;
728 continue;
729 }
730
731 text_cursor[0] += UI_GLYPH_SPACING_X*scale;
732 }
733 }
734
735 static void ui_image( ui_rect rect, GLuint image )
736 {
737 ui_flush( k_ui_shader_colour );
738
739 glActiveTexture( GL_TEXTURE0 );
740 glBindTexture( GL_TEXTURE_2D, image );
741 ui_fill_rect( rect, 0xffffffff, (ui_px[4]){ 0,0, 255,255 } );
742 ui_flush( k_ui_shader_image );
743 }
744
745 static u32 v4f_u32_colour( v4f colour )
746 {
747 u32 r = colour[0] * 255.0f,
748 g = colour[1] * 255.0f,
749 b = colour[2] * 255.0f,
750 a = colour[3] * 255.0f;
751
752 return r | (g<<8) | (b<<16) | (a<<24);
753 }
754
755 static int ui_button( ui_rect rect, v4f colour )
756 {
757 int clickup= ui_click_up(),
758 click = ui_clicking() | clickup,
759 target = ui_inside_rect( rect, vg_uictx.mouse_click ) && click,
760 hover = ui_inside_rect( rect, vg_uictx.mouse );
761
762 u32 basecolour = v4f_u32_colour( colour );
763
764 if( click ){
765 if( target ){
766 if( hover ){
767 if( clickup ){
768 ui_fill( rect, 0xffffffff );
769 vg_uictx.ignore_input_frames = 2;
770 rect_copy( rect, vg_uictx.click_fader );
771 vg_uictx.click_fade_opacity = 1.0f;
772 return 1;
773 }
774 else{
775 ui_fill( rect, 0xffcccccc );
776 return 0;
777 }
778 }
779 else{
780 ui_fill( rect, 0xff505050 );
781 ui_outline( rect, 1, 0xffffffff );
782 return 0;
783 }
784 }
785 else{
786 ui_fill( rect, 0xff505050 );
787 return 0;
788 }
789 }
790 else{
791 if( hover ){
792 ui_fill( rect, 0xffa0a0a0 );
793 return 0;
794 }
795 else{
796 ui_fill( rect, 0xff505050 );
797 return 0;
798 }
799 }
800 }
801
802 static int ui_button_text( ui_rect rect, const char *string, ui_px scale,
803 v4f colour )
804 {
805 int result = ui_button( rect, colour );
806 ui_rect t = { 0,0, ui_text_line_width( string )*scale, 14*scale };
807 ui_rect_center( rect, t );
808 ui_text( t, string, scale, k_ui_align_left );
809 return result;
810 }
811
812 static void ui_postrender(void)
813 {
814 if( vg_uictx.click_fade_opacity > 0.0f ){
815
816 float scale = vg_uictx.click_fade_opacity;
817 scale = vg_maxf( 1.0f/255.0f, scale*scale );
818
819 vg_uictx.click_fade_opacity -= vg.time_frame_delta * 1.8f;
820 u32 colour = v4f_u32_colour( (v4f){ 1.0f,1.0f,1.0f, scale } );
821
822 ui_rect rect;
823 rect[3] = (float)(vg_uictx.click_fader[3]) * scale;
824 rect[2] = vg_uictx.click_fader[2];
825 ui_rect_center( vg_uictx.click_fader, rect );
826
827 ui_fill( rect, colour );
828 }
829 ui_flush( k_ui_shader_colour );
830 }
831
832 #endif /* VG_IMGUI_H */