From 27f1751d523e9dee4ab00ec10666bbb3d8db74bc Mon Sep 17 00:00:00 2001 From: hgn Date: Mon, 11 Oct 2021 23:03:09 +0100 Subject: [PATCH] super basic UI layouting --- build.sh | 32 +++++-- fishladder.c | 5 +- vg/vg.h | 158 ++----------------------------- vg/vg_input.h | 142 +++++++++++++++++++++++++++ vg/vg_lines.h | 4 +- vg/vg_ui.h | 258 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 440 insertions(+), 159 deletions(-) create mode 100644 vg/vg_input.h create mode 100644 vg/vg_ui.h diff --git a/build.sh b/build.sh index 2395e17..cea0e94 100755 --- a/build.sh +++ b/build.sh @@ -15,6 +15,8 @@ fi run_after=false do_build=true +compile_tools=false +compile_models=false while (( "$#" )); do case $1 in @@ -30,6 +32,14 @@ while (( "$#" )); do do_build=false echo "no-build" ;; + -t|--tools) + compile_tools=true + echo "build-tools" + ;; + -m|--models) + compile_models=true + echo "build-models" + ;; *) echo "Unkown param: $1" exit 1 @@ -38,15 +48,21 @@ while (( "$#" )); do shift done -echo "Building tools" -mkdir tools -p -gcc -Wall -Wstrict-aliasing=3 $lib $flags mdlcomp.c gl/glad.c -o tools/mdlcomp $libs -Wl,-rpath=./ $defines +# Tools +if [ "$compile_tools" = true ]; then + echo "Building tools" + mkdir tools -p + gcc -Wall -Wstrict-aliasing=3 $lib $flags mdlcomp.c gl/glad.c -o tools/mdlcomp $libs -Wl,-rpath=./ $defines +fi -echo "Recompiling models" -for f in models/*.obj; - do echo "Compiling $f.."; - ./tools/mdlcomp $f $f.h -done +# Resources +if [ "$compile_models" = true ]; then + echo "Recompiling models" + for f in models/*.obj; + do echo "Compiling $f.."; + ./tools/mdlcomp $f $f.h + done +fi # Main build if [ "$do_build" = true ]; then diff --git a/fishladder.c b/fishladder.c index 1f37790..d66cd2e 100644 --- a/fishladder.c +++ b/fishladder.c @@ -934,4 +934,7 @@ void vg_render(void) } } -void vg_ui(void){} +void vg_ui(void) +{ + ui_test(); +} diff --git a/vg/vg.h b/vg/vg.h index b7abeb6..658130b 100644 --- a/vg/vg.h +++ b/vg/vg.h @@ -28,19 +28,7 @@ m3x3f vg_pv; #ifndef VG_TOOLS -#include "vg/vg_audio.h" -#include "vg/vg_shader.h" -#include "vg/vg_lines.h" -#include "vg/vg_tex.h" - -#include "steam/steamworks_thin.h" - -static inline float vg_get_axis( const char *axis ) __attribute__((unused)); -static inline int vg_get_button( const char *button ) __attribute__((unused)); -static inline int vg_get_button_down( const char *button ) __attribute__((unused)); -static inline int vg_get_button_up( const char *button ) __attribute__((unused)); - -// Globals +// Engine globals GLFWwindow* vg_window; int vg_window_x = 1280; int vg_window_y = 720; @@ -52,141 +40,15 @@ float vg_time; float vg_time_last; float vg_time_delta; -// Input -// =========================================================================================================== -GLFWgamepadstate vg_gamepad; -int vg_gamepad_ready = 0; -const char *vg_gamepad_name = NULL; -int vg_gamepad_id; - -enum EInputMode -{ - k_EInputMode_pc, - k_EInputMode_gamepad -} -vg_input_mode; - -static struct axis_binding -{ - const char *name; - union - { - int positive; - int bind; - }; - int negative; - - float value; -} -vg_axis_binds[]; - -static struct button_binding -{ - const char *name; - int bind; - - int value; int prev; -} -vg_button_binds[]; - -#include "vg/config.h" - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wreturn-type" - -static inline float vg_get_axis( const char *axis ) -{ - for( int i = 0; i < vg_list_size( vg_axis_binds ); i ++ ) - { - if( !strcmp( axis, vg_axis_binds[i].name ) ) - { - return vg_axis_binds[i].value; - } - } -} - -static inline struct button_binding *vg_get_button_ptr( const char *button ) -{ - for( int i = 0; i < vg_list_size( vg_button_binds ); i ++ ) - { - if( !strcmp( button, vg_button_binds[i].name ) ) - { - return vg_button_binds + i; - } - } -} -#pragma GCC diagnostic pop - -static inline int vg_get_button( const char *button ) -{ - return vg_get_button_ptr( button )->value; -} - -static inline int vg_get_button_down( const char *button ) -{ - struct button_binding *bind = vg_get_button_ptr( button ); - return bind->value & (bind->value ^ bind->prev); -} - -static inline int vg_get_button_up( const char *button ) -{ - struct button_binding *bind = vg_get_button_ptr( button ); - return bind->prev & (bind->value ^ bind->prev); -} - -static inline int key_is_keyboard( int const id ) -{ - vg_static_assert( GLFW_MOUSE_BUTTON_LAST < GLFW_KEY_SPACE, "GLFW: Mouse has too many buttons" ); - return id > GLFW_MOUSE_BUTTON_LAST; -} - -// Mouse AND Keyboard get button press -int get_button_cross_device( int const id ) -{ - if( key_is_keyboard( id ) ) - { - return glfwGetKey( vg_window, id ); - } - else - { - return glfwGetMouseButton( vg_window, id ) == GLFW_PRESS; - } -} +// Engine components +#include "vg/vg_audio.h" +#include "vg/vg_shader.h" +#include "vg/vg_lines.h" +#include "vg/vg_tex.h" +#include "vg/vg_input.h" +#include "vg/vg_ui.h" -void vg_update_inputs(void) -{ - // Update button inputs - for( int i = 0; i < vg_list_size( vg_button_binds ); i ++ ) - { - struct button_binding *binding = vg_button_binds + i; - binding->prev = binding->value; - - if( vg_input_mode == k_EInputMode_pc ) - { - binding->value = get_button_cross_device( binding->bind ); - } - else - { - binding->value = vg_gamepad.buttons[ binding->bind ]; - } - } - - // Update axis inputs - for( int i = 0; i < vg_list_size( vg_axis_binds ); i ++ ) - { - struct axis_binding *binding = vg_axis_binds + i; - - if( vg_input_mode == k_EInputMode_pc ) - { - binding->value = get_button_cross_device( binding->positive ); - binding->value -= get_button_cross_device( binding->negative ); - } - else - { - binding->value = vg_gamepad.axes[ binding->bind ]; - } - } -} +#include "steam/steamworks_thin.h" // Engine main // =========================================================================================================== @@ -373,7 +235,7 @@ static void vg_init( int argc, char *argv[], const char *window_name ) vg_update(); vg_render(); - vg_lines_drawall(); + vg_lines_drawall((float*)vg_pv); vg_ui(); diff --git a/vg/vg_input.h b/vg/vg_input.h new file mode 100644 index 0000000..4d1ac0e --- /dev/null +++ b/vg/vg_input.h @@ -0,0 +1,142 @@ +// Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved + +static inline float vg_get_axis( const char *axis ) __attribute__((unused)); +static inline int vg_get_button( const char *button ) __attribute__((unused)); +static inline int vg_get_button_down( const char *button ) __attribute__((unused)); +static inline int vg_get_button_up( const char *button ) __attribute__((unused)); + +// Input +// =========================================================================================================== +GLFWgamepadstate vg_gamepad; +int vg_gamepad_ready = 0; +const char *vg_gamepad_name = NULL; +int vg_gamepad_id; + +enum EInputMode +{ + k_EInputMode_pc, + k_EInputMode_gamepad +} +vg_input_mode; + +static struct axis_binding +{ + const char *name; + union + { + int positive; + int bind; + }; + int negative; + + float value; +} +vg_axis_binds[]; + +static struct button_binding +{ + const char *name; + int bind; + + int value; int prev; +} +vg_button_binds[]; + +#include "vg/config.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wreturn-type" + +static inline float vg_get_axis( const char *axis ) +{ + for( int i = 0; i < vg_list_size( vg_axis_binds ); i ++ ) + { + if( !strcmp( axis, vg_axis_binds[i].name ) ) + { + return vg_axis_binds[i].value; + } + } +} + +static inline struct button_binding *vg_get_button_ptr( const char *button ) +{ + for( int i = 0; i < vg_list_size( vg_button_binds ); i ++ ) + { + if( !strcmp( button, vg_button_binds[i].name ) ) + { + return vg_button_binds + i; + } + } +} +#pragma GCC diagnostic pop + +static inline int vg_get_button( const char *button ) +{ + return vg_get_button_ptr( button )->value; +} + +static inline int vg_get_button_down( const char *button ) +{ + struct button_binding *bind = vg_get_button_ptr( button ); + return bind->value & (bind->value ^ bind->prev); +} + +static inline int vg_get_button_up( const char *button ) +{ + struct button_binding *bind = vg_get_button_ptr( button ); + return bind->prev & (bind->value ^ bind->prev); +} + +static inline int key_is_keyboard( int const id ) +{ + vg_static_assert( GLFW_MOUSE_BUTTON_LAST < GLFW_KEY_SPACE, "GLFW: Mouse has too many buttons" ); + return id > GLFW_MOUSE_BUTTON_LAST; +} + +// Mouse AND Keyboard get button press +int get_button_cross_device( int const id ) +{ + if( key_is_keyboard( id ) ) + { + return glfwGetKey( vg_window, id ); + } + else + { + return glfwGetMouseButton( vg_window, id ) == GLFW_PRESS; + } +} + +void vg_update_inputs(void) +{ + // Update button inputs + for( int i = 0; i < vg_list_size( vg_button_binds ); i ++ ) + { + struct button_binding *binding = vg_button_binds + i; + binding->prev = binding->value; + + if( vg_input_mode == k_EInputMode_pc ) + { + binding->value = get_button_cross_device( binding->bind ); + } + else + { + binding->value = vg_gamepad.buttons[ binding->bind ]; + } + } + + // Update axis inputs + for( int i = 0; i < vg_list_size( vg_axis_binds ); i ++ ) + { + struct axis_binding *binding = vg_axis_binds + i; + + if( vg_input_mode == k_EInputMode_pc ) + { + binding->value = get_button_cross_device( binding->positive ); + binding->value -= get_button_cross_device( binding->negative ); + } + else + { + binding->value = vg_gamepad.axes[ binding->bind ]; + } + } +} diff --git a/vg/vg_lines.h b/vg/vg_lines.h index 18a0a7c..469664a 100644 --- a/vg/vg_lines.h +++ b/vg/vg_lines.h @@ -89,10 +89,10 @@ static void vg_lines_free(void) free( vg_lines.buffer ); } -static void vg_lines_drawall(void) +static void vg_lines_drawall(float* projection) { SHADER_USE( vg_line_shader ); - glUniformMatrix3fv( SHADER_UNIFORM( vg_line_shader, "uPv" ), 1, GL_FALSE, (float *)vg_pv ); + glUniformMatrix3fv( SHADER_UNIFORM( vg_line_shader, "uPv" ), 1, GL_FALSE, projection ); glBindVertexArray( vg_lines.vao ); glBindBuffer( GL_ARRAY_BUFFER, vg_lines.vbo ); diff --git a/vg/vg_ui.h b/vg/vg_ui.h new file mode 100644 index 0000000..27c0645 --- /dev/null +++ b/vg/vg_ui.h @@ -0,0 +1,258 @@ +// Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved +/* + + Concurrent UI buffer system + Coordinate space: + + 0,0 --- +(res:x) + | + | + | ++(res:y) + +*/ + +#define UI_AUTO_FILL 0 + +// Types +// ================================================================ +typedef i16 ui_px; +typedef u32 ui_colour; +typedef ui_px ui_rect[4]; +typedef struct ui_ctx ui_ctx; + +struct ui_ctx +{ + ui_px padding; + + struct ui_qnode + { + ui_rect rect; + ui_colour colour; + int mouse_over; + int capture_id; + } + stack[ 32 ]; + + ui_rect cursor; + u32 stack_count; + u32 capture_mouse_id; + + // User input + ui_px mouse[2]; + int click_state; // 0: released, 1: on down, 2: pressed, 3: on release +}; + +// Rect controls +// ========================================================== + +static void ui_rect_copy( ui_rect src, ui_rect dst ) +{ + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; +} + +static void ui_rect_pad( ui_ctx *ctx, ui_rect rect ) +{ + rect[0] += ctx->padding; + rect[1] += ctx->padding; + rect[2] -= ctx->padding*2; + rect[3] -= ctx->padding*2; +} + +static void ui_vis_rect( ui_rect rect, u32 colour ) +{ + v2f p0; + v2f p1; + + p0[0] = rect[0]; + p0[1] = rect[1]; + p1[0] = rect[0]+rect[2]; + p1[1] = rect[1]+rect[3]; + + vg_line( p0, (v2f){p1[0],p0[1]}, colour ); + vg_line( (v2f){p1[0],p0[1]}, p1, colour ); + vg_line( p1, (v2f){p0[0],p1[1]}, colour ); + vg_line( (v2f){p0[0],p1[1]}, p0, colour ); +} + +static void ui_new_node( ui_ctx *ctx ) +{ + if( ctx->stack_count == vg_list_size( ctx->stack ) ) + vg_exiterr( "[UI] Stack overflow while creating box!" ); + + struct ui_qnode *parent = &ctx->stack[ ctx->stack_count-1 ]; + struct ui_qnode *node = &ctx->stack[ ctx->stack_count++ ]; + ui_rect_copy( ctx->cursor, node->rect ); + + if( parent->mouse_over ) + { + if( ctx->mouse[0] >= node->rect[0] && ctx->mouse[0] <= node->rect[0]+node->rect[2] && + ctx->mouse[1] >= node->rect[1] && ctx->mouse[1] <= node->rect[1]+node->rect[3] ) + node->mouse_over = 1; + else + node->mouse_over = 0; + } +} + +static int ui_hasmouse( ui_ctx *ctx ) +{ + struct ui_qnode *node = &ctx->stack[ ctx->stack_count-1 ]; + return (node->mouse_over && (node->capture_id == ctx->capture_mouse_id)); +} + +static void ui_end( ui_ctx *ctx ) +{ + struct ui_qnode *node = &ctx->stack[ --ctx->stack_count ]; + + ui_rect_copy( node->rect, ctx->cursor ); + ui_vis_rect( ctx->cursor, (node->mouse_over && (node->capture_id == ctx->capture_mouse_id))? 0xffff0000: 0xff0000ff ); +} + +static void ui_end_down( ui_ctx *ctx ) +{ + ui_px height = ctx->stack[ ctx->stack_count ].rect[3]; + ui_end( ctx ); + ctx->cursor[1] += height; +} + +static void ui_end_right( ui_ctx *ctx ) +{ + ui_px width = ctx->stack[ ctx->stack_count ].rect[2]; + ui_end( ctx ); + ctx->cursor[0] += width; +} + +static void ui_capture_mouse( ui_ctx *ctx, int id ) +{ + struct ui_qnode *node = &ctx->stack[ ctx->stack_count-1 ]; + node->capture_id = id; + + if( node->mouse_over ) + { + ctx->capture_mouse_id = id; + } +} + +// API control +// ==================================================================== + +static void ui_begin( ui_ctx *ctx, ui_px res_x, ui_px res_y ) +{ + ctx->cursor[0] = 0; + ctx->cursor[1] = 0; + ctx->cursor[2] = res_x; + ctx->cursor[3] = res_y; + + ui_rect_copy( ctx->cursor, ctx->stack[0].rect ); + ctx->stack[0].mouse_over = 1; + + ctx->stack_count = 1; +} + +static void ui_resolve( ui_ctx *ctx ) +{ + if( ctx->stack_count-1 ) + vg_exiterr( "[UI] Mismatched node create/drestroy!" ); +} + +// User Input piping +// ==================================================================== + +static void ui_set_mouse( ui_ctx *ctx, int x, int y, int click_state ) +{ + ctx->mouse[0] = x; + ctx->mouse[1] = y; + + ctx->click_state = click_state; +} + +static void ui_test(void) +{ + /* + +------------------------------------------------------+ + | Central Market [x]| + +------+--------------+-+------------------------------+ + | Buy | Balance |#| [filters] [favorites] | + | <>_ | () 2,356 |#|----------------------------+-+ + |------|--------------|#| [] potion of madness 4 |#| + | Sell | \ Main sword |#|----------------------------|#| + | _*^ |--------------|#| [] Balance of time 23 | | + |------| * Side arm |#|----------------------------| | + | 235 |--------------| | [] Strength 5,300 | | + | | () Sheild | |----------------------------| | + | |--------------| | [] Bewilder 2,126 | | + | [ & Spells ] |----------------------------| | + | |--------------| | [] Eternal flames 6 | | + +------+--------------+-+----------------------------+-+ + */ + + ui_ctx ctx = { .padding = 8 }; + + ui_begin( &ctx, vg_window_x, vg_window_y ); + + // TODO: Find a more elegent form for this + int mouse_state = 0; + if( vg_get_button( "primary" ) ) mouse_state = 2; + if( vg_get_button_down( "primary" ) ) mouse_state = 1; + if( vg_get_button_up( "primary" ) ) mouse_state = 3; + + ui_set_mouse( &ctx, vg_mouse[0], vg_mouse[1], mouse_state ); + + static ui_px window_x = 20; + static ui_px window_y = 20; + static int window_drag = 0; + static ui_px drag_offset[2]; + + if( window_drag ) + { + window_x = ctx.mouse[0]+drag_offset[0]; + window_y = ctx.mouse[1]+drag_offset[1]; + + if( ctx.click_state == 0 ) + { + window_drag = 0; + } + } + + ctx.cursor[0] = window_x; + ctx.cursor[1] = window_y; + ctx.cursor[2] = 500; + ctx.cursor[3] = 350; + + ui_new_node( &ctx ); + { + ctx.cursor[0] += 20; + ctx.cursor[1] += 20; + ctx.cursor[2] = 150; + ctx.cursor[3] = 25; + + ui_capture_mouse( &ctx, 1 ); + + ui_new_node( &ctx ); + { + ui_capture_mouse( &ctx, 2 ); + + if( ui_hasmouse( &ctx ) ) + { + if( ctx.click_state == 1 ) // start drag + { + window_drag = 1; + drag_offset[0] = window_x-ctx.mouse[0]; + drag_offset[1] = window_y-ctx.mouse[1]; + } + } + } + ui_end( &ctx ); + } + ui_end( &ctx ); + + ui_resolve( &ctx ); + + 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 } ); + vg_lines_drawall( (float*)view ); +} -- 2.25.1