X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=vg_imgui.h;h=5c957ed2ba1b053b3002e4e64299b8a7d8b7c2d4;hb=d426b8b8935acf73d6152ce55abd3ddd47ce5f2d;hp=53d8472302f8f3f5e6acb07a81e1860d654a5084;hpb=aa880cfc4ba86b43338c21df307b484a5ce230c9;p=vg.git diff --git a/vg_imgui.h b/vg_imgui.h index 53d8472..5c957ed 100644 --- a/vg_imgui.h +++ b/vg_imgui.h @@ -22,6 +22,7 @@ typedef i16 ui_px; typedef ui_px ui_rect[4]; +typedef ui_px ui_point[2]; typedef struct ui_vert ui_vert; enum ui_axis { @@ -169,7 +170,8 @@ struct{ void (*enter)( char *, u32 ), (*up)( char *, u32 ), (*down)( char *, u32 ), - (*change)( char *, u32 ); + (*change)( char *, u32 ), + (*escape)( void ); } callbacks; } @@ -198,7 +200,8 @@ struct{ } modal; - GLuint tex_glyphs, vao, vbo, ebo; + GLuint tex_glyphs, vao, vbo, ebo, tex_bg; + v2f bg_inverse_ratio; ui_px mouse[2], mouse_click[2]; u32 mouse_state[2]; @@ -207,6 +210,7 @@ struct{ ui_rect click_fader, click_fader_end; float click_fade_opacity; + f32 frosting; ui_scheme scheme; const ui_font *font; @@ -220,8 +224,9 @@ struct{ cursor; SDL_Cursor *cursor_map[ k_ui_cursor_max ]; - v4f colour; + + f32 hue; /* lol this sucks */ } static vg_ui = { .scheme = { @@ -261,59 +266,76 @@ static vg_ui = { [ k_ui_gray + k_ui_brighter ] = UI_RGB( 0xa89984 ), }, .font = &vg_ui_font_small, - .colour = {1.0f,1.0f,1.0f,1.0f} + .colour = {1.0f,1.0f,1.0f,1.0f}, + .bg_inverse_ratio = {1,1} }; -static struct vg_shader _shader_ui = -{ - .name = "[vg] ui", +static struct vg_shader _shader_ui ={ + .name = "[vg] ui - transparent", .link = NULL, - .vs = - { + .vs = { .orig_file = NULL, .static_src = "layout (location=0) in vec2 a_co;" "layout (location=1) in vec2 a_uv;" "layout (location=2) in vec4 a_colour;" "uniform mat3 uPv;" + "uniform vec2 uBGInverseRatio;" "" - "out vec2 aTexCoords;" + "out vec4 aTexCoords;" "out vec4 aColour;" - "out vec2 aWsp;" "" - "void main()" - "{" - "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" - "aTexCoords = a_uv * 0.00390625;" /* TODO: Uniform */ + "void main(){" + "vec4 proj_pos = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" + "gl_Position = proj_pos;" + "aTexCoords = vec4( a_uv * 0.00390625, " + " (proj_pos.xy*0.5+0.5) * uBGInverseRatio );" "aColour = a_colour;" - - "aWsp = a_co;" "}", }, - .fs = - { + .fs = { .orig_file = NULL, .static_src = - "uniform sampler2D uTexGlyphs;" + "uniform sampler2D uTexGlyphs;" + "uniform sampler2D uTexBG;" "uniform vec4 uColour;" + "uniform float uSpread;" "out vec4 FragColor;" "" - "in vec2 aTexCoords;" + "in vec4 aTexCoords;" "in vec4 aColour;" "" - "in vec2 aWsp;" - + "vec2 rand_hash22( vec2 p ){" + "vec3 p3 = fract(vec3(p.xyx) * 213.8976123);" + "p3 += dot(p3, p3.yzx+19.19);" + "return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y));" + "}" "" - "void main()" - "{" + "void main(){" "vec4 diffuse = aColour;" - "if( diffuse.a == 0.0 )" - "{" - "diffuse.a = texture( uTexGlyphs, aTexCoords ).r;" + "vec4 avg = vec4(0.0);" + + "if( aColour.a == 0.0 ){" + "avg = aColour;" + "avg.a = texture( uTexGlyphs, aTexCoords.xy ).r;" "}" - - "FragColor = diffuse * uColour;" + "else{" + "if( uSpread > 0.0001 ){" + "for( int i=0; i<4; i ++ ){" + "vec2 spread = rand_hash22(aTexCoords.zw+vec2(float(i)));" + "avg += texture( uTexBG, aTexCoords.zw + (spread-0.5)*uSpread );" + "}" + "avg *= 0.25;" + "avg.a = 1.0;" + "avg.rgb = mix( avg.rgb, aColour.rgb, aColour.a );" + "}" + "else{" + "avg = aColour;" + "}" + "}" + + "FragColor = avg * uColour;" "}" } }; @@ -363,10 +385,58 @@ static struct vg_shader _shader_ui_image = { } }; +static struct vg_shader _shader_ui_hsv = { + .name = "[vg] ui_hsv", + .link = NULL, + .vs = { + .orig_file = NULL, + .static_src = + "layout (location=0) in vec2 a_co;" + "layout (location=1) in vec2 a_uv;" + "layout (location=2) in vec4 a_colour;" + "uniform mat3 uPv;" + + "out vec2 aTexCoords;" + "out vec4 aColour;" + "out vec2 aWsp;" + + "void main()" + "{" + "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" + "aTexCoords = a_uv * 0.00390625;" + "aColour = a_colour;" + + "aWsp = a_co;" + "}", + }, + .fs = + { + .orig_file = NULL, + .static_src = + "uniform float uHue;" + "out vec4 FragColor;" + + "in vec2 aTexCoords;" + "in vec4 aColour;" + "in vec2 aWsp;" + + "void main()" + "{" + "vec3 c = vec3( uHue, aTexCoords );" + "vec4 K = vec4(1.0,2.0/3.0,1.0/3.0,3.0);" + "vec3 p = abs(fract(c.xxx+K.xyz)*6.0 - K.www);" + "vec3 colour = c.z*mix(K.xxx,clamp(p-K.xxx,0.0,1.0),c.y);" + "FragColor = vec4( colour, 1.0 );" + "}" + } +}; + static void _vg_ui_init(void){ if( !vg_shader_compile( &_shader_ui ) || - !vg_shader_compile( &_shader_ui_image ) ) + !vg_shader_compile( &_shader_ui_image ) || + !vg_shader_compile( &_shader_ui_hsv ) ){ vg_fatal_error( "Failed to compile ui shader" ); + } /* * Vertex buffer @@ -472,7 +542,8 @@ static void _vg_ui_init(void){ enum ui_shader { k_ui_shader_colour, - k_ui_shader_image + k_ui_shader_image, + k_ui_shader_hsv, }; static void rect_copy( ui_rect a, ui_rect b ){ @@ -480,7 +551,7 @@ static void rect_copy( ui_rect a, ui_rect b ){ b[i] = a[i]; } -static void ui_flush( enum ui_shader shader ){ +static void ui_flush( enum ui_shader shader, f32 w, f32 h ){ u32 vertex_offset = vg_ui.vert_start*sizeof(ui_vert), vertex_count = vg_ui.cur_vert-vg_ui.vert_start, vertex_size = vertex_count*sizeof(ui_vert), @@ -509,8 +580,8 @@ static void ui_flush( enum ui_shader shader ){ m3x3f view = M3X3_IDENTITY; m3x3_translate( view, (v3f){ -1.0f, 1.0f, 0.0f } ); - m3x3_scale( view, (v3f){ 1.0f/((float)vg.window_x*0.5f), - -1.0f/((float)vg.window_y*0.5f), 1.0f } ); + m3x3_scale( view, (v3f){ 1.0f/(w*0.5f), + -1.0f/(h*0.5f), 1.0f } ); if( shader == k_ui_shader_colour ){ glUseProgram( _shader_ui.id ); @@ -523,6 +594,14 @@ static void ui_flush( enum ui_shader shader ){ glActiveTexture( GL_TEXTURE0 ); glBindTexture( GL_TEXTURE_2D, vg_ui.tex_glyphs ); glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexGlyphs" ), 0 ); + + glActiveTexture( GL_TEXTURE1 ); + glBindTexture( GL_TEXTURE_2D, vg_ui.tex_bg ); + glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexBG" ), 1 ); + glUniform1f( glGetUniformLocation( _shader_ui.id, "uSpread" ), + vg_ui.frosting ); + glUniform2fv( glGetUniformLocation( _shader_ui.id, "uBGInverseRatio" ), + 1, vg_ui.bg_inverse_ratio ); } else if( shader == k_ui_shader_image ){ glUseProgram( _shader_ui_image.id ); @@ -532,6 +611,12 @@ static void ui_flush( enum ui_shader shader ){ glUniform4fv( glGetUniformLocation( _shader_ui_image.id, "uColour" ), 1, vg_ui.colour ); } + else if( shader == k_ui_shader_hsv ){ + glUseProgram( _shader_ui_hsv.id ); + glUniformMatrix3fv( glGetUniformLocation( _shader_ui_hsv.id, "uPv" ), 1, + GL_FALSE, (float *)view ); + glUniform1f( glGetUniformLocation(_shader_ui_hsv.id,"uHue"), vg_ui.hue ); + } else vg_fatal_error( "Invalid UI shader (%d)\n", shader ); @@ -1005,11 +1090,11 @@ static void ui_info( ui_rect inout_panel, const char *text ){ } static void ui_image( ui_rect rect, GLuint image ){ - ui_flush( k_ui_shader_colour ); + ui_flush( k_ui_shader_colour, vg.window_x, vg.window_y ); glActiveTexture( GL_TEXTURE0 ); glBindTexture( GL_TEXTURE_2D, image ); ui_fill_rect( rect, 0xffffffff, (ui_px[4]){ 0,256,256,0 } ); - ui_flush( k_ui_shader_image ); + ui_flush( k_ui_shader_image, vg.window_x, vg.window_y ); } static u32 v4f_u32_colour( v4f colour ){ @@ -1024,6 +1109,8 @@ static u32 v4f_u32_colour( v4f colour ){ static void ui_defocus_all(void){ if( vg_ui.focused_control_type == k_ui_control_textbox ){ SDL_StopTextInput(); + if( vg_ui.textbox.callbacks.escape ) + vg_ui.textbox.callbacks.escape(); } vg_ui.focused_control_id = NULL; @@ -1032,23 +1119,28 @@ static void ui_defocus_all(void){ } enum ui_button_state { - k_ui_button_none = 0, - k_ui_button_click = 1, - k_ui_button_holding_inside = 2, - k_ui_button_holding_outside = 3, - k_ui_button_hover = 4 + k_ui_button_none = 0x0, + k_ui_button_click = 0x1, + k_ui_button_holding_inside = 0x2, + k_ui_button_holding_outside = 0x4, + k_ui_button_hover = 0x8 }; +/* TODO: split this out into a formatless button and one that auto fills */ static enum ui_button_state ui_colourbutton( ui_rect rect, - enum ui_scheme_colour colour ){ + enum ui_scheme_colour colour, + enum ui_scheme_colour hover_colour, + enum ui_scheme_colour hi_colour, + bool const fill ){ int clickup= ui_click_up(UI_MOUSE_LEFT), click = ui_clicking(UI_MOUSE_LEFT) | clickup, target = ui_inside_rect( rect, vg_ui.mouse_click ) && click, hover = ui_inside_rect( rect, vg_ui.mouse ); u32 col_base = vg_ui.scheme[ colour ], - col_highlight = vg_ui.scheme[ k_ui_fg ], - col_hover = vg_ui.scheme[ colour + k_ui_brighter ]; + col_highlight = vg_ui.scheme[ hi_colour? hi_colour: k_ui_fg ], + col_hover = vg_ui.scheme[ hover_colour? hover_colour: + colour + k_ui_brighter ]; if( vg_ui.focused_control_type != k_ui_control_none ){ clickup = 0; @@ -1065,41 +1157,43 @@ static enum ui_button_state ui_colourbutton( ui_rect rect, if( target ){ if( hover ){ if( clickup ){ - ui_fill( rect, col_highlight ); vg_ui.ignore_input_frames = 2; - rect_copy( rect, vg_ui.click_fader ); + ui_defocus_all(); - rect_copy( rect, vg_ui.click_fader_end ); - vg_ui.click_fader_end[3] = 0; - ui_rect_center( rect, vg_ui.click_fader_end ); + if( fill ) { + ui_fill( rect, col_highlight ); + rect_copy( rect, vg_ui.click_fader ); + rect_copy( rect, vg_ui.click_fader_end ); + vg_ui.click_fader_end[3] = 0; + ui_rect_center( rect, vg_ui.click_fader_end ); + vg_ui.click_fade_opacity = 1.0f; + } - vg_ui.click_fade_opacity = 1.0f; - ui_defocus_all(); return k_ui_button_click; } else{ - ui_fill( rect, col_highlight ); + if( fill ) ui_fill( rect, col_highlight ); return k_ui_button_holding_inside; } } else{ - ui_fill( rect, col_base ); + if( fill ) ui_fill( rect, col_base ); ui_outline( rect, 1, col_highlight, 0 ); return k_ui_button_holding_outside; } } else{ - ui_fill( rect, col_base ); + if( fill ) ui_fill( rect, col_base ); return k_ui_button_none; } } else{ if( hover ){ - ui_fill( rect, col_hover ); + if( fill ) ui_fill( rect, col_hover ); return k_ui_button_hover; } else{ - ui_fill( rect, col_base ); + if( fill ) ui_fill( rect, col_base ); return k_ui_button_none; } } @@ -1108,7 +1202,7 @@ static enum ui_button_state ui_colourbutton( ui_rect rect, static enum ui_button_state ui_colourbutton_text( ui_rect rect, const char *string, ui_px scale, enum ui_scheme_colour colour ){ - enum ui_button_state state = ui_colourbutton( rect, colour ); + enum ui_button_state state = ui_colourbutton( rect, colour, 0, 0, 1 ); ui_rect t = { 0,0, ui_text_line_width( string )*scale, 14*scale }; ui_rect_center( rect, t ); @@ -1198,7 +1292,7 @@ static void ui_postrender(void){ vg_ui.wants_mouse = 1; } - ui_flush( k_ui_shader_colour ); + ui_flush( k_ui_shader_colour, vg.window_x, vg.window_y ); if( !vg_ui.focused_control_hit ){ ui_defocus_all(); @@ -1229,7 +1323,7 @@ static int ui_checkbox( ui_rect inout_panel, const char *str_label, i32 *data ){ ui_split( rect, k_ui_axis_v, -rect[3], 0, label, box ); ui_text( label, str_label, k_ui_scale, k_ui_align_middle_left, 0 ); - int changed = ui_colourbutton( box, k_ui_bg )==1; + int changed = ui_colourbutton( box, k_ui_bg, 0, 0, 1 )==1; if( changed ) *data = (*data) ^ 0x1; @@ -1317,6 +1411,138 @@ static void ui_enum_post(void){ vg_ui.focused_control_hit = 1; } +/* + * Slider + * ----------------------------------------------------------------------------- + */ + +static enum ui_button_state _ui_slider( ui_rect box, + f32 min, f32 max, f32 *value, const char *format ){ + f32 t; + + enum ui_button_state + mask_using = + k_ui_button_holding_inside | + k_ui_button_holding_outside | + k_ui_button_click, + mask_brighter = + mask_using | k_ui_button_hover, + state = ui_colourbutton( box, k_ui_bg, k_ui_bg+2, k_ui_bg+3, 1 ); + + if( state & mask_using ){ + t = vg_clampf( (f32)(vg_ui.mouse[0] - box[0]) / (f32)( box[2] ), + 0.0f, 1.0f ); + *value = vg_lerpf( min, max, t ); + } + else + t = vg_clampf( (*value - min) / (max-min), 0.0f, 1.0f ); + + ui_rect line = { box[0], box[1], t * (f32)box[2], box[3] }; + ui_fill( line, ui_colour(state&mask_brighter? k_ui_bg+4: k_ui_bg+2) ); + + ui_outline( box, 1, ui_colour(state? k_ui_fg+3: k_ui_bg+3), 0 ); + + /* TODO: replace this one day with our own function */ + char buf[32]; + snprintf( buf, sizeof(buf), format? format: "%.2f", *value ); + ui_text( box, buf, 1, k_ui_align_middle_center, 0 ); + + return state; +} + +static void ui_slider( ui_rect inout_panel, const char *str_label, + f32 min, f32 max, f32 *value, const char *format ){ + ui_rect rect, label, box; + ui_standard_widget( inout_panel, rect, 1 ); + ui_label( rect, str_label, k_ui_scale, 0, box ); + _ui_slider( box, min, max, value, format ); +} + +/* + * Colour picker + * ----------------------------------------------------------------------------- + */ + +static void ui_colourpicker( ui_rect inout_panel, const char *str_label, + v4f value ){ + ui_rect widget, left, right; + ui_standard_widget( inout_panel, widget, 8 ); + ui_split_ratio( widget, k_ui_axis_v, 0.5f, 8, left, right ); + + ui_rect sliders[4]; + ui_split_ratio( right, k_ui_axis_h, 0.5f, 2, sliders[0], sliders[2] ); + ui_split_ratio( sliders[0], k_ui_axis_h, 0.5f, 2, sliders[0], sliders[1] ); + ui_split_ratio( sliders[2], k_ui_axis_h, 0.5f, 2, sliders[2], sliders[3] ); + + v4f hsv; + vg_rgb_hsv( value, hsv ); + + enum ui_button_state modified = 0x00; + + for( u32 i=0; i<4; i ++ ){ + modified |= _ui_slider( sliders[i], 0.0f, 1.0f, hsv+i, + (const char *[]){ "hue %.2f", + "sat %.2f", + "lum %.2f", + "alpha %.2f" }[i] ); + } + + ui_rect preview, square; + ui_split_ratio( left, k_ui_axis_v, 0.8f, 8, square, preview ); + + u32 state = ui_colourbutton( square, 0, 0, 0, 0 ); + modified |= state; + + enum ui_button_state + mask_using = + k_ui_button_holding_inside | + k_ui_button_holding_outside | + k_ui_button_click; + + if( state & mask_using ){ + for( u32 i=0; i<2; i ++ ){ + hsv[1+i] = vg_clampf( + (f32)(vg_ui.mouse[i] - square[i]) / (f32)(square[2+i]), + 0.0f, 1.0f ); + } + + hsv[2] = 1.0f-hsv[2]; + } + + if( modified & (k_ui_button_click|k_ui_button_holding_inside| + k_ui_button_holding_outside) ){ + vg_hsv_rgb( hsv, value ); + } + + ui_outline( square, 1, ui_colour( state? k_ui_fg+3: k_ui_bg+3 ), 0 ); + + /* preview colour */ + v4f colour; + vg_hsv_rgb( hsv, colour ); + colour[3] = 1.0f; + ui_fill( preview, v4f_u32_colour( colour ) ); + + /* Draw hsv shader thingy */ + ui_flush( k_ui_shader_colour, vg.window_x, vg.window_y ); + ui_fill_rect( square, 0xffffffff, (ui_px[4]){ 0,256,256,0 } ); + vg_ui.hue = hsv[0]; + ui_flush( k_ui_shader_hsv, vg.window_x, vg.window_y ); + + ui_rect marker = { square[0] + hsv[1] * (f32)square[2] - 4, + square[1] + (1.0f-hsv[2]) * (f32)square[3] - 4, + 8, 8 }, + lx = { square[0], + square[1] + (1.0f-hsv[2]) * (f32)square[3], + square[2], 1 }, + ly = { square[0] + hsv[1] * (f32)square[2], + square[1], + 1, square[3] }; + + ui_fill( marker, ui_colour( k_ui_fg ) ); + ui_fill( lx, ui_colour( k_ui_fg ) ); + ui_fill( ly, ui_colour( k_ui_fg ) ); +} + /* * Textbox chaos * ----------------------------------------------------------------------------- @@ -1389,6 +1615,17 @@ static void _ui_textbox_to_clipboard(void){ } } +static void _ui_textbox_change_callback(void){ + if( vg_ui.textbox.callbacks.change ){ + vg_ui.textbox.callbacks.change( vg_ui.textbuf, vg_ui.textbox.len ); + + /* we gave permission to modify the buffer in this callback so.. */ + int len = strlen( vg_ui.textbuf ); + vg_ui.textbox.cursor_user = VG_MIN( vg_ui.textbox.cursor_user, len ); + vg_ui.textbox.cursor_pos = VG_MIN( vg_ui.textbox.cursor_pos, len ); + } +} + static void ui_start_modal( const char *message, u32 options ); static void _ui_textbox_clipboard_paste(void){ if( !SDL_HasClipboardText() ) @@ -1413,6 +1650,7 @@ static void _ui_textbox_clipboard_paste(void){ _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, &vg_ui.textbox.cursor_pos, cpylength, 1 ); SDL_free( text ); + _ui_textbox_change_callback(); } static void _ui_textbox_put_char( char c ){ @@ -1551,10 +1789,7 @@ static void _ui_textbox_backspace(void){ if( vg_ui.focused_control_type == k_ui_control_textbox ){ vg_ui.textbox.cursor_user = _ui_textbox_delete_char( -1 ); vg_ui.textbox.cursor_pos = vg_ui.textbox.cursor_user; - - if( vg_ui.textbox.callbacks.change ){ - vg_ui.textbox.callbacks.change( vg_ui.textbuf, vg_ui.textbox.len ); - } + _ui_textbox_change_callback(); } } @@ -1562,10 +1797,7 @@ static void _ui_textbox_delete(void){ if( vg_ui.focused_control_type == k_ui_control_textbox ){ vg_ui.textbox.cursor_user = _ui_textbox_delete_char( 1 ); vg_ui.textbox.cursor_pos = vg_ui.textbox.cursor_user; - - if( vg_ui.textbox.callbacks.change ){ - vg_ui.textbox.callbacks.change( vg_ui.textbuf, vg_ui.textbox.len ); - } + _ui_textbox_change_callback(); } } @@ -1622,17 +1854,22 @@ static void _ui_textbox_cut(void){ _ui_textbox_to_clipboard(); vg_ui.textbox.cursor_user = _ui_textbox_delete_char(0); vg_ui.textbox.cursor_pos = vg_ui.textbox.cursor_user; + _ui_textbox_change_callback(); } static void _ui_textbox_enter(void){ if( vg_ui.focused_control_type == k_ui_control_textbox ){ + vg_ui.ignore_input_frames = 2; + if( vg_ui.textbox.callbacks.enter ) vg_ui.textbox.callbacks.enter( vg_ui.textbuf, vg_ui.textbox.len ); if( vg_ui.focused_control_type != k_ui_control_textbox ) return; - if( vg_ui.textbox.flags & UI_TEXTBOX_MULTILINE ) + if( vg_ui.textbox.flags & UI_TEXTBOX_MULTILINE ){ _ui_textbox_put_char( '\n' ); + _ui_textbox_change_callback(); + } else{ if( !(vg_ui.textbox.flags & UI_TEXTBOX_AUTOFOCUS ) ) ui_defocus_all(); @@ -2069,9 +2306,7 @@ static void ui_proc_utf8( const char *text ){ ptr ++; } - if( vg_ui.textbox.callbacks.change ){ - vg_ui.textbox.callbacks.change( vg_ui.textbuf, vg_ui.textbox.len ); - } + _ui_textbox_change_callback(); } }