mor eidoloegy
authorhgn <hgodden00@gmail.com>
Mon, 29 Dec 2025 23:46:56 +0000 (23:46 +0000)
committerhgn <hgodden00@gmail.com>
Mon, 29 Dec 2025 23:46:56 +0000 (23:46 +0000)
21 files changed:
source/async/async.kv
source/console/console_system.c
source/engine/engine.kv
source/engine/vg_audio.c
source/engine/vg_engine.c
source/engine/vg_engine.h
source/engine/vg_entity.c
source/engine/vg_entity.h
source/engine/vg_framebuffer.c
source/engine/vg_material.c
source/engine/vg_material.h
source/engine/vg_model.c
source/engine/vg_tex.c
source/engine/vg_ui.c
source/foundation/foundation.h
source/foundation/foundation.kv
source/foundation/sort.c [new file with mode: 0644]
source/graphics/ui.c
source/graphics/vg_graphics.h
source/tools/metacompiler.c
source/types.h

index bb3a9a834de60fae9155eb80a8ac743b7ba72eb8..ddd77f7dff0a0000fcc8e40bee1cda75b66ab712 100644 (file)
@@ -2,7 +2,7 @@ include ""
 
 test_feature
 {
-   feature SDL
+   feature FEATURE_SDL
    yes
    {
       add sdl_async.c
@@ -12,10 +12,3 @@ test_feature
 
    }
 }
-
-hook
-{
-   affinity -20000
-   event START
-   function _async_init
-}
index 7545f765476091471cfd1fdc227fba6abfc75592..39d365d9119847449ef5dd6ea13ad51f7034726c 100644 (file)
@@ -4,13 +4,7 @@
 struct console_item
 {
    const c8 *alias;
-   enum console_item_type
-   {
-      k_console_item_ccmd,
-      k_console_item_cvar_i32,
-      k_console_item_cvar_f32
-   }
-   type;
+   enum data_type type;
 
    union
    {
@@ -118,7 +112,7 @@ void _console_execute( const c8 *command, b8 silent, b8 cheat_allowed )
 
    if( target_command )
    {
-      if( target_command->type == k_console_item_ccmd )
+      if( target_command->type == k_data_type_function )
          target_command->fn( &args );
       else
       {
@@ -128,31 +122,35 @@ void _console_execute( const c8 *command, b8 silent, b8 cheat_allowed )
             struct stream string;
             stream_open_buffer_read( &string, set_to, buffer_last_index( set_to, 0, 0 ), 0 );
 
-            if( target_command->type == k_console_item_cvar_f32 )
+            if( target_command->type == k_data_type_f32 )
             {
                f64 f;
                if( string_parse_f64( &string, &f ) == k_string_parse_ok )
                   *target_command->_f32 = f;
             }
-            if( target_command->type == k_console_item_cvar_i32 )
+            else if( target_command->type == k_data_type_i32 )
             {
                i64 i;
                if( string_parse_i64( &string, &i ) == k_string_parse_ok )
                   *target_command->_i32 = i;
             }
+            else
+               ASSERT_CRITICAL( 0 );
 
             $log( $info, {"Value set"} );
          }
          else
          {
-            if( target_command->type == k_console_item_cvar_f32 )
+            if( target_command->type == k_data_type_f32 )
             {
                $log( $info, {"The value of "}, {target_command->alias}, {"(f32) is: "}, $float( *target_command->_f32 ) );
             }
-            else if( target_command->type == k_console_item_cvar_i32 )
+            else if( target_command->type == k_data_type_i32 )
             {
                $log( $info, {"The value of "}, {target_command->alias}, {"(i32) is: "}, $signed( *target_command->_i32 ) );
             }
+            else
+               ASSERT_CRITICAL( 0 );
          }
       }
    }
index 80985723f2dcdb2f27cd6a17569310d9e588a19b..b4b141707ca10d88f9085828ac813471b7096196 100644 (file)
@@ -5,27 +5,34 @@ include ../../submodules/SDL/include
 
 test_feature
 {
-   feature web
+   feature FEATURE_OPENGL
 
    yes
    {
-      include ../../dep/glad-3.1-es/
-      add ../../dep/glad-3.1-es/glad.c
-   }
-   no
-   {
-      include ../../dep/glad.4.3/
-      add ../../dep/glad.4.3/glad.c
+      test_feature
+      {
+         feature PLATFORM_WEB
+
+         yes
+         {
+            include ../../dep/glad-3.1-es/
+            add ../../dep/glad-3.1-es/glad.c
+         }
+         no
+         {
+            include ../../dep/glad.4.3/
+            add ../../dep/glad.4.3/glad.c
+         }
+      }
    }
 }
 
-enable SDL
+enable FEATURE_SDL
 append ../foundation/foundation.kv
 
 add vg_engine.c
 add vg_ui.c
 add vg_input.c
-add vg_asset.c
 
 ccmd
 {
@@ -57,9 +64,6 @@ ccmd
 
 append ../console/console_system.kv
 add vg_console.c
-add vg_framebuffer.c
-add vg_render.c
-add vg_shader.c
 
 input_layer
 {
@@ -203,370 +207,151 @@ input
    layer_mask ui
 }
 
-shader
-{
-   name invisible
-}
-
-shader
+test_feature
 {
-   name blit
-
-   subshader
+   feature FEATURE_OPENGL
+   yes
    {
-      type vertex
-      add shaders/blit.vs
-
-      uniform
+      add vg_asset.c
+      add vg_framebuffer.c
+      add vg_render.c
+      add vg_shader.c
+      shader
       {
-         type vec2
-         alias uInverseRatio
+         name invisible
       }
-      uniform
+      shader
       {
-         type int
-         alias uFlip
+         name blit
+         subshader
+         {
+            type vertex
+            add shaders/blit.vs
+            uniform
+            {
+               type vec2
+               alias uInverseRatio
+            }
+            uniform
+            {
+               type int
+               alias uFlip
+            }
+         }
+         subshader
+         {
+            type fragment
+            add shaders/blit_tex.fs
+            uniform
+            {
+               type sampler2D
+               alias uTexMain
+            }
+            uniform
+            {
+               type int
+               alias uSwapRGBA
+            }
+         }
       }
-   }
-
-   subshader
-   {
-      type fragment
-      add shaders/blit_tex.fs
-      
-      uniform
+      shader
       {
-         type sampler2D
-         alias uTexMain
+         name blit_colour
+         subshader
+         {
+            type vertex
+            add shaders/blit_colour.vs
+         }
+         subshader
+         {
+            type fragment
+            add shaders/blit_colour.fs
+            uniform
+            {
+               type vec4
+               alias uColour
+            }
+         }
       }
-      uniform
+      ccmd
       {
-         type int
-         alias uSwapRGBA
+         name reload_shaders
+         function _shader_recompile_ccmd
+         description "Recompile shaders (DEVELOPER ONLY!)"
       }
-   }
-}
-shader
-{
-   name blit_colour
-   subshader
-   {
-      type vertex
-      add shaders/blit_colour.vs
-   }
-
-   subshader
-   {
-      type fragment
-      add shaders/blit_colour.fs
-
-      uniform
+      add vg_camera.c
+      add vg_tex.c
+      add vg_lines.c
+      cvar
       {
-         type vec4
-         alias uColour
+         name vg_lines_enable
+         type i32
+         default 0
+         cheat 1
+         description "Show line debuggers"
       }
-   }
-}
-
-ccmd
-{
-   name reload_shaders
-   function _shader_recompile_ccmd
-   description "Recompile shaders (DEVELOPER ONLY!)"
-}
-
-add vg_camera.c
-add vg_tex.c
-add vg_lines.c
-cvar
-{
-   name vg_lines_enable
-   type i32
-   default 0
-   cheat 1
-   description "Show line debuggers"
-}
-shader
-{
-   name debug_lines
-   subshader
-   {
-      type vertex
-      add shaders/debug_lines.vs
-      uniform
+      shader
       {
-         type mat4
-         alias uPv
+         name debug_lines
+         subshader
+         {
+            type vertex
+            add shaders/debug_lines.vs
+            uniform
+            {
+               type mat4
+               alias uPv
+            }
+         }
+         subshader
+         {
+            type fragment
+            add shaders/debug_lines.fs
+         }
       }
-   }
-   subshader
-   {
-      type fragment
-      add shaders/debug_lines.fs
-   }
-}
-
-
-add vg_audio.c
-add vg_audio_dsp.c
-add vg_audio_vorbis.c
-cvar
-{
-   name vg_audio
-   type i32
-   default 0
-   cheat 1
-   description "Show audio info"
-}
-
-add vg_model.c
-add vg_material.c
-add vg_entity.c
-add vg_metascene.c
-add vg_af.c
-
-entity
-{
-   name ent_spawn
-   id 2
-
-   parameter
-   {
-      name transform
-      type transform
-   }
-   parameter
-   {
-      name name
-      type pstr
-   }
-   parameter
-   {
-      name flags
-      type u32
-      flag
+      add vg_model.c
+      add vg_material.c
+      add vg_entity.c
+      add vg_metascene.c
+      add vg_af.c
+      entity
       {
-         name locked
-         value 0x1
+         name ent_audio
+         id 7
       }
-      flag
+      entity
       {
-         name group1
-         value 0x2
+         name ent_cubemap
+         id 21
       }
-      flag
+      entity
       {
-         name group2
-         value 0x4
+         name ent_prop
+         id 23
       }
-      flag
+      entity
       {
-         name group3
-         value 0x8
+         name mdl_armature
+         id 28
       }
    }
-   function
-   {
-      name lantern
-   }
-   function
-   {
-      name boat
-   }
-   function
-   {
-      name me
-   }
-}
-entity
-{
-   name ent_water
-   id 5
-   function
-   {
-      name enable
-   }
-   function
-   {
-      name disable
-   }
-   function
-   {
-      name show
-   }
 }
-entity
-{
-   name ent_volume
-   id 6
-   function
-   {
-      name enable
-   }
-   function
-   {
-      name disable
-   }
-}
-entity
-{
-   name ent_audio
-   id 7
-}
-entity
-{
-   name ent_marker
-   id 8
-   function
-   {
-      name push
-   }
-   function
-   {
-      name tooltip
-   }
-   function
-   {
-      name tooltip_special
-   }
-}
-entity
-{
-   name ent_camera
-   id 13
-   function
-   {
-      name focus
-   }
-   function
-   {
-      name unfocus
-   }
-}
-entity
-{
-   name ent_ccmd
-   id 17
-   function
-   {
-      name exec
-   }
-}
-entity
-{
-   name ent_cubemap
-   id 21
-}
-entity
-{
-   name ent_prop
-   id 23
-}
-entity
-{
-   name ent_armature
-   id 28
-}
-entity
-{
-   name ent_atom
-   id 30
-   function
-   {
-      name pass_equal
-   }
-   function
-   {
-      name pass_greater
-   }
-   function
-   {
-      name set
-   }
-   function
-   {
-      name set_or
-   }
-   function
-   {
-      name set_and
-   }
-}
-entity
-{
-   name ent_cutscene
-   id 31
-   function
-   {
-      name play
-   }
-   function
-   {
-      name pause
-   }
-   function
-   {
-      name resume
-   }
-   function
-   {
-      name end
-   }
-}
-entity
-{
-   name ent_light
-   id 32
 
-   parameter
-   {
-      name transform
-      type transform
-   }
-   parameter
-   {
-      name flags
-      type u32
-      flag
-      {
-         name daytime
-         value 0x1
-      }
-      flag
-      {
-         name off
-         value 0x2
-      }
-   }
-   parameter
-   {
-      name type
-      type u32
-   }
-   parameter
-   {
-      name colour
-      type vec4
-      ui rgbe
-   }
-   parameter
-   {
-      name angle
-      type f32
-   }
-   parameter
-   {
-      name range
-      type f32
-   }
-
-   function
+test_feature
+{
+   feature FEATURE_AUDIO
+   yes
    {
-      name on
-      argument
+      add vg_audio.c
+      add vg_audio_dsp.c
+      add vg_audio_vorbis.c
+      cvar
       {
-         name enable
+         name vg_audio
          type i32
+         default 0
+         cheat 1
+         description "Show audio info"
       }
    }
 }
index f9f478cb431e8f52c6bdaec1504b64230abe9a36..9cbcceeec4f926d590af3878e0248bbaceef08f7 100644 (file)
@@ -16,7 +16,6 @@
 
 SDL_Mutex *_audio_mutex;
 _Thread_local static b8 _audio_have_lock = 0;
-static f32 _master_volume = 1.0f;
 
 enum channel_stage
 {
index f3ea97f0954b1a9d6c07019259bcfad1ad21109a..c1fbba0406aad352fb1c950d3322006b7bcf269a 100644 (file)
@@ -4,27 +4,34 @@
 #include <math.h>
 #include <time.h>
 #include "vg_input.h"
-#include "vg_opengl.h"
 #include "vg_engine.h"
 #include "vg_ui.h"
 #include "vg_audio.h"
 #include "vg_graphics.h"
-#include "vg_audio_dsp.h"
-#include "vg_audio.h"
-#include "vg_shader.h"
-#include "vg_render.h"
-#include "vg_tex.h"
-#include "vg_material.h"
-#include "vg_lines.h"
-#include "vg_framebuffer.h"
+#include "SDL3/SDL.h"
+
+#if defined( FEATURE_AUDIO )
+# include "vg_audio_dsp.h"
+# include "vg_audio.h"
+#endif
+
+#if defined( FEATURE_OPENGL )
+# include "vg_opengl.h"
+# include "vg_shader.h"
+# include "vg_render.h"
+# include "vg_tex.h"
+# include "vg_material.h"
+# include "vg_lines.h"
+# include "vg_framebuffer.h"
+#endif
 #include "vg_console.h"
 
 // TODO: temp
 #include "console_system.h"
 
-#if defined( __EMSCRIPTEN__ )
-#include <emscripten.h>
-#include <emscripten/html5.h>
+#if defined( PLATFORM_WEB )
+# include <emscripten.h>
+# include <emscripten/html5.h>
 #endif
 
 struct _engine _engine;
@@ -70,7 +77,8 @@ i32 _async_thread( void *_ )
    return 0;
 }
 
-#if !defined( __EMSCRIPTEN__ )
+#if defined( FEATURE_OPENGL )
+# if !defined( PLATFORM_WEB )
 APIENTRY void _opengl_debug( GLenum source,
             GLenum type,
             GLuint id,
@@ -85,11 +93,12 @@ APIENTRY void _opengl_debug( GLenum source,
    if( type == GL_DEBUG_TYPE_ERROR )
       _fatal_exit();
 }
+# endif
 #endif
 
 #include <unistd.h>
 
-void _vg_engine_frame(void);
+void _vg_engine_frame( b8 engine_update );
 void _vg_engine_frame_web(void);
 
 f64 _fixed_time = 0.0, _fixed_accumulator = 0.0;
@@ -111,35 +120,35 @@ i32 main( i32 argc, const c8 *argv[] )
       _fatal_exit();
    }
 
+#if defined( FEATURE_AUDIO )
    $log( $info, {"SDL_INIT_AUDIO"} );
    SDL_InitSubSystem( SDL_INIT_AUDIO );
+#endif
 
-#if 0
+#if defined( FEATURE_OPENGL )
+ #if 0
    $log( $info, {"SDL_INIT_GAMECONTROLLER"} );
    SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER );
-#endif
+ #endif
 
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
-#if 0
-   SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
-   SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 );
-#endif
 
-#if defined( __EMSCRIPTEN__ )
+ #if defined( PLATFORM_WEB )
    SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
    SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 0 );
    SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES );
-#else
+ #else
    SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 4 );
    SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 );
    SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
    SDL_GL_SetAttribute( SDL_GL_CONTEXT_RELEASE_BEHAVIOR, SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH );
-#endif
+ #endif
    SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 0 );
+#endif
 
    /*
     * Get monitor information
@@ -164,13 +173,16 @@ i32 main( i32 argc, const c8 *argv[] )
       _engine.h = 768;
    }
 
-#if !defined( WIN32 ) && !defined( __EMSCRIPTEN__ )
+#if !defined( PLATFORM_WINDOWS ) && !defined( PLATFORM_WEB )
        SDL_SetHint( "SDL_VIDEO_X11_XINERAMA", "1" );
        SDL_SetHint( "SDL_VIDEO_X11_XRANDR", "0" );
        SDL_SetHint( "SDL_VIDEO_X11_XVIDMODE", "0" );
 #endif
 
-   u32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
+   u32 flags = SDL_WINDOW_RESIZABLE;
+#if defined( FEATURE_OPENGL )
+   flags |= SDL_WINDOW_OPENGL;
+#endif
 
 #if 0
    if( _vg_window.display_mode == k_vg_window_fullscreen )
@@ -198,6 +210,7 @@ i32 main( i32 argc, const c8 *argv[] )
    SDL_SetWindowMaximumSize( _engine.window_handle, 1920*2, 1080*2 );
    SDL_StopTextInput( _engine.window_handle );
 
+#if defined( FEATURE_OPENGL )
    /*
     * OpenGL loading
     */
@@ -213,11 +226,11 @@ i32 main( i32 argc, const c8 *argv[] )
       _fatal_exit();
    }
 
-#if defined( __EMSCRIPTEN__ )
+ #if defined( PLATFORM_WEB )
    if( !gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress) )
-#else
+ #else
    if( !gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress) )
-#endif
+ #endif
    {
       $log( $fatal, {"Glad Failed to initialize"} );
       SDL_GL_DestroyContext( _engine.opengl_handle );
@@ -228,12 +241,15 @@ i32 main( i32 argc, const c8 *argv[] )
    const c8 *glver = (const c8 *)glGetString( GL_VERSION );
    $log( $ok, {"Load setup complete, OpenGL version: "}, {glver} );
 
-#if !defined( __EMSCRIPTEN__ )
+ #if !defined( PLATFORM_WEB )
    glEnable              ( GL_DEBUG_OUTPUT );
    glDebugMessageCallback( _opengl_debug, 0 );
-#endif
+ #endif
 
    SDL_GL_SetSwapInterval(0); /* disable vsync while loading */
+#else
+   $log( $ok, {"Load setup complete, no graphics acceleration enabled"} );
+#endif
 
    i32 rate = video_mode->refresh_rate;
    if( rate < 25 || rate > 300 )
@@ -244,28 +260,32 @@ i32 main( i32 argc, const c8 *argv[] )
 
    f64 next_frame_time = 0.0;
 
-
    /* ------------- */
 
    mt_random_seed( &_engine.random, 887765 );
-
    _async_init();
+#if defined( FEATURE_AUDIO )
    _dsp_init();
    _audio_init();
+#endif
    _engine_ui_init();
    _input_init();
    _console_init();
    _engine_console_init();
+
+#if defined( FEATURE_OPENGL )
    _vg_render_init();
    _shader_init();
    _vg_tex_init();
    _vg_material_init();
    _vg_lines_init();
+#endif
+
    if( _vg_engine_hooks.start ) _vg_engine_hooks.start();
 
    _async_thread_handle = SDL_CreateThread( _async_thread, "ASync thread", NULL );
 
-#if defined( __EMSCRIPTEN__ )
+#if defined( PLATFORM_WEB )
    emscripten_set_main_loop( _vg_engine_frame_web, 0, 1 );
 #else
 L_new_frame:;
@@ -281,10 +301,11 @@ L_new_frame:;
       else
       {
          // FIXME: How do we fucking do a no op on web?
-#if !defined( __EMSCRIPTEN__ )
+#if !defined( PLATFORM_WEB )
          __asm__ __volatile__("pause\n");
 #endif
       }
+      _vg_engine_frame( 0 );
       goto L_new_frame;
    }
 
@@ -293,7 +314,7 @@ L_new_frame:;
    if( _engine.time_delta > 1.0/24.0 ) _engine.time_delta = 1.0/24.0;
    _engine.time = now;
    next_frame_time = now + 1.0/_engine.framerate_limit;
-   _vg_engine_frame();
+   _vg_engine_frame( 1 );
    goto L_new_frame;
 #endif
 }
@@ -305,56 +326,11 @@ void _vg_engine_frame_web(void)
    if( _engine.time_delta < 1.0/300.0 ) _engine.time_delta = 1.0/300.0;
    if( _engine.time_delta > 1.0/24.0 ) _engine.time_delta = 1.0/24.0;
    _engine.time = now;
-
-   _vg_engine_frame();
+   _vg_engine_frame( 1 );
 }
 
-void _vg_engine_frame(void)
+void _vg_engine_frame( b8 engine_update )
 {
-   while( _task_queue_process( 0 ) ) {}
-
-   /* normal update */
-   _input_update();
-   _engine_console_update();
-   if( _vg_engine_hooks.frame_start ) _vg_engine_hooks.frame_start();
-
-   /* fixed update */
-   _fixed_accumulator += _engine.time_delta;
-   f64 actual_delta = _engine.time_delta,
-       actual_time  = _engine.time;
-   _engine.time_delta = 1.0/60.0;
-   for( u32 i=0; i<8; i ++ )
-   {
-      if( _fixed_accumulator >= 1.0/60.0 )
-      {
-         _fixed_accumulator -= 1.0/60.0;
-         _fixed_time += _engine.time_delta;
-         _engine.time = _fixed_time;
-         if( _vg_engine_hooks.fixed_frame ) _vg_engine_hooks.fixed_frame();
-      }
-      else break;
-   }
-   _engine.time = actual_time;
-   _engine.time_delta = actual_delta;
-   _engine.time_fixed_extrapolate = _fixed_accumulator;// / (1.0/60.0);
-
-   i32 pw = _engine.w, ph = _engine.h;
-   //glfwGetFramebufferSize( _engine.window_handle, &_engine.w, &_engine.h );
-   SDL_GetWindowSizeInPixels( _engine.window_handle, &_engine.w, &_engine.h );
-   if( (pw != _engine.w) || (ph != _engine.h) )
-      _framebuffer_resize();
-
-   if( _vg_engine_hooks.frame_render ) _vg_engine_hooks.frame_render();
-   _engine_ui_pre_render();
-
-   if( _vg_engine_hooks.frame_ui ) _vg_engine_hooks.frame_ui();
-   _engine_console_ui();
-   _audio_gui();
-   _engine_ui_post_render();
-
-   //glfwSwapBuffers(_engine.window_handle);
-   SDL_GL_SwapWindow( _engine.window_handle );
-
    SDL_Event e;
    while( SDL_PollEvent( &e ) )
    {
@@ -391,7 +367,7 @@ void _vg_engine_frame(void)
       else if( e.type == SDL_EVENT_MOUSE_MOTION )
       {
          SDL_MouseMotionEvent *ev = (SDL_MouseMotionEvent *)&e;
-         _ui_set_mouse( ev->x / _engine_ui.divisor, ev->y / _engine_ui.divisor );
+         _ui_set_mouse( ev->x / _engine_ui.divisor, ev->y / _engine_ui.divisor, 0 ); //FIXME: MODIFIERS
       }
       else if( e.type == SDL_EVENT_TEXT_INPUT )
       {
@@ -404,4 +380,72 @@ void _vg_engine_frame(void)
          }
       }
    }
+
+   // TODO: is there an event for this?
+   i32 pw = _engine.w, ph = _engine.h;
+   SDL_GetWindowSizeInPixels( _engine.window_handle, &_engine.w, &_engine.h );
+   if( (pw != _engine.w) || (ph != _engine.h) )
+   {
+#if defined( FEATURE_OPENGL )
+      _framebuffer_resize();
+#endif
+      _engine.redraw = 1;
+   }
+
+   if( engine_update )
+   {
+      while( _task_queue_process( 0 ) ) {}
+
+      /* normal update */
+      _input_update();
+      _engine_console_update();
+      if( _vg_engine_hooks.frame_start ) _vg_engine_hooks.frame_start();
+
+      /* fixed update */
+      _fixed_accumulator += _engine.time_delta;
+      f64 actual_delta = _engine.time_delta,
+          actual_time  = _engine.time;
+      _engine.time_delta = 1.0/60.0;
+      for( u32 i=0; i<8; i ++ )
+      {
+         if( _fixed_accumulator >= 1.0/60.0 )
+         {
+            _fixed_accumulator -= 1.0/60.0;
+            _fixed_time += _engine.time_delta;
+            _engine.time = _fixed_time;
+            if( _vg_engine_hooks.fixed_frame ) _vg_engine_hooks.fixed_frame();
+         }
+         else break;
+      }
+      _engine.time = actual_time;
+      _engine.time_delta = actual_delta;
+      _engine.time_fixed_extrapolate = _fixed_accumulator;// / (1.0/60.0);
+      _engine.redraw = 1;
+   }
+
+   if( _engine.redraw )
+   {
+      _engine.redraw = 0;
+
+#if !defined( FEATURE_OPENGL )
+      _engine.surface_handle = SDL_GetWindowSurface( _engine.window_handle );
+#endif
+
+      if( _vg_engine_hooks.frame_render ) _vg_engine_hooks.frame_render();
+      _engine_ui_pre_render();
+      if( _vg_engine_hooks.frame_ui ) _vg_engine_hooks.frame_ui();
+      _engine_console_ui();
+
+#if defined( FEATURE_AUDIO )
+      _audio_gui();
+#endif
+      _engine_ui_post_render();
+
+      //glfwSwapBuffers(_engine.window_handle);
+#if defined( FEATURE_OPENGL )
+      SDL_GL_SwapWindow( _engine.window_handle );
+#else
+      ASSERT_CRITICAL( SDL_UpdateWindowSurface( _engine.window_handle ) );
+#endif
+   }
 }
index ea1c8f437f75fd810b6cd1432e89d117a654325c..f3689aa32b9ca9a60a67ece047e05d809ba72ba5 100644 (file)
@@ -1,5 +1,9 @@
 #include "random.h"
 
+#if !defined( FEATURE_OPENGL )
+# include "SDL3/SDL_surface.h"
+#endif
+
 struct _engine
 {
    b8 vsync;
@@ -15,10 +19,14 @@ struct _engine
    quality;
 
    void *window_handle;
+#if defined( FEATURE_OPENGL )
    void *opengl_handle;
-   i32 w, h;
-
    i32 native_fbo;
+#else
+   SDL_Surface *surface_handle;
+#endif
+   i32 w, h;
+   b8 redraw;
 
    struct mt_random random;
 }
index 99aa597487407e730d050f1462a30458f7303ab2..14439bae1bae043c84dc83eac406577c1c68905f 100644 (file)
@@ -13,3 +13,8 @@ i32 _vg_entity_link_function( u16 entity_type, const c8 *alias )
    $log( $error, {"Failed to link function from alias '"}, {alias}, {"'"} );
    return -1;
 }
+
+struct entity_info *_vg_entity_info( u16 entity_type )
+{
+   return &_entity_infos[ _vg_entity_type_index( entity_type ) ];
+}
index 40e835bbbe1a3c2eb0cd2bf7ab3e755329e1041a..be3ed184efb1755387ef8c7533f588ad5f8d3ae9 100644 (file)
@@ -1,6 +1,61 @@
 #pragma once
 #include "vg_model.h"
 
+enum entity_event_result 
+{
+   k_entity_event_result_OK,
+   k_entity_event_result_unhandled,
+   k_entity_event_result_invalid,
+   k_entity_event_result_invalid_parameter,
+};
+
+struct ent_event
+{
+   u32 pstr_source_event;
+   u32 pstr_recieve_event; 
+   
+   union entity_id source;
+   union entity_id reciever;
+   u16 data_type, unused2;
+
+   f32 delay;
+   u8 function_index, activator, unused0, unused1; // FIXME: REPLACES PSTR!!!!!!!!!!!
+
+   union
+   {
+      i32 const_i32;
+      f32 const_f32;
+      union entity_id const_entity_id;
+      u32 const_pstr;
+   }
+   data;
+};
+
+struct entity_info
+{
+   u16 id, size;
+   u16 function_start, function_count, parameter_start, parameter_count;
+   const c8 *name;
+};
+struct entity_function_info
+{
+   const c8 *name;
+};
+struct entity_parameter_info
+{
+   const c8 *name;
+   u16 type, offset;
+};
+
+// Generated
+extern struct entity_info _entity_infos[];
+extern struct entity_parameter_info _entity_parameter_infos[];
+
+u32 _vg_entity_type_index( u16 entity_type );
+struct entity_info *_vg_entity_info( u16 entity_type );
+enum entity_event_result _vg_entity_event_dispatch( struct ent_event *event );
+i32 _vg_entity_link_function( u16 entity_type, const c8 *alias );
+
 #include "generated/entities.h"
 
 #if 0
@@ -698,60 +753,3 @@ struct ent_npc
    struct mdl_transform transform;
    u32 pstr_id, pstr_context_id, pstr_anim, none1;
 };
-
-enum entity_event_result 
-{
-   k_entity_event_result_OK,
-   k_entity_event_result_unhandled,
-   k_entity_event_result_invalid,
-   k_entity_event_result_invalid_parameter,
-};
-
-enum ent_event_flags
-{
-   k_ent_event_data_void = 0x0,
-   k_ent_event_data_const_i32 = 0x1,
-   k_ent_event_data_const_f32 = 0x2,
-   k_ent_event_data_const_entity_id = 0x4,
-   k_ent_event_data_const_string = 0x8,
-   k_ent_event_data_data_alias = 0x10,
-   k_ent_event_data_v3f = 0x20,
-};
-
-struct ent_event
-{
-   u32 pstr_source_event;
-   u32 pstr_recieve_event; 
-   
-   union entity_id source;
-   union entity_id reciever;
-   u32 flags;
-
-   f32 delay;
-   u8 function_index, activator, unused0, unused1; // FIXME: REPLACES PSTR!!!!!!!!!!!
-
-   union
-   {
-      i32 const_i32;
-      f32 const_f32;
-      union entity_id const_entity_id;
-      u32 const_pstr;
-      u32 pstr_data_alias;
-   }
-   data;
-};
-
-struct entity_info
-{
-   u16 id, unused0;
-   u16 function_start, function_count;
-   const c8 *name;
-};
-struct entity_function_info
-{
-   const c8 *name;
-};
-
-struct entity_info *_vg_entity_info( u16 entity_type );
-enum entity_event_result _vg_entity_event_dispatch( struct ent_event *event );
-i32 _vg_entity_link_function( u16 entity_type, const c8 *alias );
index dbc288ffcddff89167739ea7f89d09cce5d958fb..76d290aa950c0f8e8465febbc2e75ffb809b441f 100644 (file)
@@ -375,7 +375,6 @@ void framebuffer_free( struct framebuffer *fb )
 
 void _framebuffer_resize(void)
 {
-   $log( $info, {"Resizing framebuffers..."} );
    ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_OPENGL ) );
    for( i32 i=0; i<_framebuffer.count; i++ )
    {
index e56f230f6fb3957674bc822cdb1ae5337728fdb9..bcac544d38db583b8419475e4495fa729a8886c3 100644 (file)
@@ -2,9 +2,6 @@
 #include "vg_material.h"
 #include "vg_shader.h"
 
-// FIXME!!!!!!!!!!!!!!!!!!!! QSORT
-#include <stdlib.h>
-
 struct
 {
    struct vg_asset_list asset_list;
@@ -18,16 +15,6 @@ void _vg_material_init(void)
    vg_allocate_asset_list( &_vg_material.asset_list, VG_ASSET_MATERIALS_MAX );
 }
 
-i32 compar( const void *a, const void *b )
-{
-   return ((struct sort_index *)a)->value - ((struct sort_index *)b)->value;
-}
-
-void index_sort( struct sort_index *indices, u32 indice_count )
-{
-   qsort( indices, indice_count, sizeof(struct sort_index), compar );
-}
-
 u16 _vg_material_load( const c8 *path, struct stack_allocator *stack )
 {
    u16 id = vg_asset_get( &_vg_material.asset_list, path );
@@ -45,6 +32,13 @@ u16 _vg_material_load( const c8 *path, struct stack_allocator *stack )
       return 0;
    }
 
+   u32 flag_it = 0;
+   while( keyvalues_foreach( &material->kvs, &flag_it, 0, "renderflag" ) )
+   {
+      const c8 *render_flag = keyvalues_value( &material->kvs, flag_it, NULL );
+      if( compare_buffers( render_flag, 0, "additive", 0 ) ) material->flags |= VG_MATERIAL_ADDITIVE;
+   }
+
    u32 tilemap_block = keyvalues_get( &material->kvs, 0, "tilemap",  0 );
    if( tilemap_block )
    {
@@ -150,7 +144,6 @@ u16 _vg_material_load( const c8 *path, struct stack_allocator *stack )
       }
    }
 
-
    u32 shader_block = keyvalues_get_child( &material->kvs, 0, 0 );
    if( shader_block )
    {
index cf99cc249d941de3e6d6068dbe756acc7e1ebad8..6c4c6f41a0809b16563f9c589d712f2f0b7affc7 100644 (file)
@@ -10,7 +10,8 @@
 
 struct vg_material
 {
-   u16 shader_id, flags;
+   i16 shader_id;
+   u16 flags;
    struct keyvalues kvs;
    struct tex_tilemap tilemap;
    union
index 262f56e9c54d5838467850ba62dcdb70335cc3c1..5ea1db996524e65020a3a384e2fa18b7eaa6a0c4 100644 (file)
@@ -441,13 +441,6 @@ void vg_model_bind_mesh( struct vg_model *model )
    glBindVertexArray( model->vao );
 }
 
-#if 0
-void vg_model_draw_elements( u32 start, u32 count )
-{
-   glDrawElements( GL_TRIANGLES, count, GL_UNSIGNED_INT, (void *)(start*sizeof(u32)) );
-}
-#endif
-
 void vg_model_draw_batch( struct mdl_batch *batch )
 {
    glDrawElements( GL_TRIANGLES, batch->count, GL_UNSIGNED_INT, (void *)(u64)batch->offset );
index cabc08ef6605d3cd4c7da03f73ef577fa3faf07f..e7b2d77f2f9d33aab12e54820bdff5bad5ddf8eb 100644 (file)
@@ -494,13 +494,12 @@ u16 _vg_texture_load( const c8 *path, struct tex_tilemap *tilemap )
                {
                   for( i32 ox = -VG_TEX_TILEMAP_PADDING; ox < tileset->size[0] +VG_TEX_TILEMAP_PADDING; ox ++ )
                   {
-                     i32 tile_i = tile_y*tileset->grid[0] + tile_x,
-                          dst_x = tile->pixel_root[0] + ox,
-                          dst_y = tile->pixel_root[1] + oy,
-                          src_x = tileset->co[0] + tile_x*tileset->size[0] + i32_clamp( ox, 0, tileset->size[0]-1 ),
-                          src_y = tileset->co[1] + tile_y*tileset->size[1] + i32_clamp( oy, 0, tileset->size[1]-1 ),
-                          dst_i = (((tilemap->sheet_size[1] - dst_y) -1)*tilemap->sheet_size[0] + dst_x)*4,
-                          src_i = (src_y*tilemap->image_size[0] + src_x)*4;
+                     i32 dst_x = tile->pixel_root[0] + ox,
+                         dst_y = tile->pixel_root[1] + oy,
+                         src_x = tileset->co[0] + tile_x*tileset->size[0] + i32_clamp( ox, 0, tileset->size[0]-1 ),
+                         src_y = tileset->co[1] + tile_y*tileset->size[1] + i32_clamp( oy, 0, tileset->size[1]-1 ),
+                         dst_i = (((tilemap->sheet_size[1] - dst_y) -1)*tilemap->sheet_size[0] + dst_x)*4,
+                         src_i = (src_y*tilemap->image_size[0] + src_x)*4;
 
                      if( src_buffer[ src_i + 3 ] > 0 )
                      {
index 91e755ebba01b9e717d69af0ef54d695e6104153..8e5ede382bf76e3b7683cba7f2847e9f68d4abd9 100644 (file)
@@ -1,15 +1,20 @@
 #include "foundation.h"
 #include "vg_engine.h"
 #include "vg_graphics.h"
-#include "vg_render.h"
-#include "vg_opengl.h"
-#include "vg_shader.h"
 #include "vg_ui.h"
+#include "SDL3/SDL.h"
 
-#if defined( __EMSCRIPTEN__ )
-# define PIXEL_FORMAT GL_RGBA
+#if defined( FEATURE_OPENGL )
+# include "vg_render.h"
+# include "vg_opengl.h"
+# include "vg_shader.h"
+# if defined( PLATFORM_WEB )
+#  define PIXEL_FORMAT GL_RGBA
+# else
+#  define PIXEL_FORMAT GL_BGRA
+# endif
 #else
-# define PIXEL_FORMAT GL_BGRA
+# include "SDL3/SDL_surface.h"
 #endif
 
 struct graphics_target _ui_surface = 
@@ -19,13 +24,15 @@ struct graphics_target _ui_surface =
 };
 struct _engine_ui _engine_ui;
 
+#if defined( FEATURE_OPENGL )
 static GLuint _ui_surface_texture;
+#endif
 
 #include <unistd.h>
 void _engine_ui_init(void)
 {
    _ui_surface.buffer = _heap_allocate( 1920*1080*4 );
-
+#if defined( FEATURE_OPENGL )
    glGenTextures( 1, &_ui_surface_texture );
    glActiveTexture( GL_TEXTURE0 );
    glBindTexture( GL_TEXTURE_2D, _ui_surface_texture );
@@ -34,6 +41,7 @@ void _engine_ui_init(void)
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+#endif
 }
 
 void _engine_ui_pre_render(void)
@@ -59,6 +67,10 @@ void _engine_ui_pre_render(void)
 
 void _engine_ui_post_render(void)
 {
+   _ui_draw( (i16[]){ _engine_ui.w, _engine_ui.h } );
+
+   /* Copy via shader onto screen (with blending) */
+#if defined( FEATURE_OPENGL )
    glEnable( GL_BLEND );
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
    glBlendEquation( GL_FUNC_ADD );
@@ -72,15 +84,39 @@ void _engine_ui_post_render(void)
    _shader_blit_uTexMain( 0 );
    _shader_blit_uFlip( 1 );
    _shader_blit_uInverseRatio( (f32[2]){ (f64)_engine_ui.w/1920.0, (f64)_engine_ui.h/1080.0 } );
-#if defined( __EMSCRIPTEN__ )
+# if defined( PLATFORM_WEB )
    _shader_blit_uSwapRGBA( 1 );
-#endif
+# endif
 
    _render_fullscreen_quad();
-#if defined( __EMSCRIPTEN__ )
+# if defined( PLATFORM_WEB )
    _shader_blit_uSwapRGBA( 0 );
-#endif
+# endif
    glDisable( GL_BLEND );
+#else
+   /* Copy to surface without any blending. We currently don't assume anything is under us. */
+   SDL_Surface *surface = _engine.surface_handle;
+
+   if( surface->format != SDL_PIXELFORMAT_XRGB8888 )
+   {
+      $log( $error, {"Wrong pixel format. Got: "}, $unsigned( surface->format, .base=16 ) );
+   }
+   ASSERT_CRITICAL( surface->format == SDL_PIXELFORMAT_XRGB8888 );
+   ASSERT_CRITICAL( surface->pixels );
+
+   u8 *dest = surface->pixels;
+   for( i32 y=0; y<surface->h; y ++ )
+   {
+      for( i32 x=0; x<surface->w; x ++ )
+      {
+         dest[ y*surface->pitch + x*4 + 0 ] = _ui_surface.buffer[ (y*1920 + x)*4 + 0 ];
+         dest[ y*surface->pitch + x*4 + 1 ] = _ui_surface.buffer[ (y*1920 + x)*4 + 1 ];
+         dest[ y*surface->pitch + x*4 + 2 ] = _ui_surface.buffer[ (y*1920 + x)*4 + 2 ];
+         dest[ y*surface->pitch + x*4 + 3 ] = _ui_surface.buffer[ (y*1920 + x)*4 + 3 ];
+      }
+   }
+
+#endif
 
    static b8 want_text_prev = 0;
    b8 want_text = _ui_want_text();
index 89d9347a67aae5c5bf839284a5d34a164ce9509a..86194ca327c55d72d404f6623c12c63c90ef517c 100644 (file)
@@ -1,15 +1,6 @@
 /* Voyager common application interface */
 #pragma once
 
-#if 0
-#define VG_PRE_MAIN \
-   _exit_init(); \
-   _log_init(); \
-   _options_init( argc, argv ); \
-   EVENT_CALL( OPTIONS ); \
-   _options_check_end(); 
-#endif
-
 #define BYTES_KB( X ) X*1024
 #define BYTES_MB( X ) X*1024*1024
 #define BYTES_GB( X ) X*1024*1024*1024
@@ -121,7 +112,6 @@ struct pool_allocator
    struct pool_node *nodes;
    u32 count;
 };
-//void pool_init( struct pool_allocator *pool, struct pool_node *nodes, u16 node_count, struct pool_chain *full_chain );
 u32  pool_index( struct pool_allocator *pool, u16 pool_id );
 u16  pool_reference( struct pool_allocator *pool, u16 pool_id, b8 increment );
 u16  pool_next( struct pool_allocator *pool, u16 pool_id, b8 right );
@@ -152,8 +142,8 @@ void *queue_tail_data( struct queue_allocator *queue );
 u32   queue_offset( struct queue_allocator *queue, void *pointer );
 /* warning: this is not the size but the allocation size (may be padded) */
 u32   queue_item_size( struct queue_allocator *queue, u32 item_id );
-b8  queue_next( struct queue_allocator *queue, u32 item_id, u32 *out_next );
-b8  queue_previous( struct queue_allocator *queue, u32 item_id, u32 *out_prev );
+b8    queue_next( struct queue_allocator *queue, u32 item_id, u32 *out_next );
+b8    queue_previous( struct queue_allocator *queue, u32 item_id, u32 *out_prev );
 void  queue_pop( struct queue_allocator *queue );
 void  queue_copy_buffer( struct queue_allocator *queue, void *dst, u32 start, u32 size );
 u32   queue_usage( struct queue_allocator *queue );
@@ -201,7 +191,7 @@ void stream_open_auto( struct stream *stream, u32 flags );
 void stream_open_stack( struct stream *stream, struct stack_allocator *stack, u32 flags );
 void stream_open_buffer_write( struct stream *stream, void *buffer, u32 buffer_length, u32 flags );
 void stream_open_buffer_read( struct stream *stream, const void *buffer, u32 buffer_length, u32 flags );
-b8 stream_open_file( struct stream *stream, const c8 *path, u32 flags );
+b8   stream_open_file( struct stream *stream, const c8 *path, u32 flags );
 
 void stream_close( struct stream *stream );
 u32  stream_read( struct stream *stream, void *buffer, u32 length );
@@ -209,7 +199,7 @@ void *stream_read_all( struct stream *stream, struct stack_allocator *stack, u32
 u32  stream_write( struct stream *stream, const void *buffer, u32 length );
 u32  stream_offset( struct stream *stream );
 void stream_seek( struct stream *stream, u32 offset );
-b8 stream_error( struct stream *stream );
+b8   stream_error( struct stream *stream );
 
 /* String (Stream subset)
  * ------------------------------------------------------------------------------------------------------------------ */
@@ -232,7 +222,6 @@ struct v_string_arg
       i64 _i64;
       f64 _f64;
    };
-
    u32 type;
    u32 base;
    union
@@ -270,6 +259,7 @@ enum string_parse_result string_parse_i64( struct stream *string, i64 *value );
 enum string_parse_result string_parse_f64( struct stream *string, f64 *value );
 enum string_parse_result string_parse_string( struct stream *string, u32 *out_start, u32 *out_length, c8 delimiter );
 
+
 /* Logging
  * ------------------------------------------------------------------------------------------------------------------ */
 
@@ -349,9 +339,9 @@ u32 keyvalues_get_child( struct keyvalues *kvs, u32 root_offset, u32 index );
 b8 keyvalues_foreach( struct keyvalues *kvs, u32 *kv, u32 block, const c8 *alias );
 
 const c8 *keyvalues_read_string( struct keyvalues *kvs, u32 root_offset, const c8 *key, const c8 *default_value );
-b8 keyvalues_read_i32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, i32 *default_values, i32 *out_values, u32 len );
-b8 keyvalues_read_u32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, u32 *default_values, u32 *out_values, u32 len );
-b8 keyvalues_read_f32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, f32 *default_values, f32 *out_values, u32 len );
+b8  keyvalues_read_i32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, i32 *default_values, i32 *out_values, u32 len );
+b8  keyvalues_read_u32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, u32 *default_values, u32 *out_values, u32 len );
+b8  keyvalues_read_f32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, f32 *default_values, f32 *out_values, u32 len );
 u32 keyvalues_append_frame( struct keyvalues *kvs, u32 parent_offset, const c8 *key );
 u32 keyvalues_append_string( struct keyvalues *kvs, u32 parent_offset, const c8 *key, const c8 *value );
 u32 keyvalues_append_i32s( struct keyvalues *kvs, u32 parent_offset, const c8 *key, i32 *values, u32 len );
@@ -384,10 +374,6 @@ b8 directory_next_entry( struct directory *directory );
 enum directory_entry_type directory_entry_type( struct directory *directory );
 void directory_close( struct directory *directory );
 
-#define EVENT_CALL( NAME, ... ) \
-   for( u32 ev_iter=0; _event_##NAME##_subscribers[ ev_iter ]; ev_iter ++ ) \
-      _event_##NAME##_subscribers[ ev_iter ]( __VA_ARGS__ );
-
 struct sort_index
 {
    u32 index;
index c4c49912b51fd0f230d458e20081fc202782d21c..7b104af47053470f420855511ea939ac665400b1 100644 (file)
@@ -11,10 +11,11 @@ add string.c
 add keyvalues.c
 add buffer_operations.c
 add temporary.c
+add sort.c
 
 test_feature
 {
-   feature linux
+   feature PLATFORM_LINUX
    yes
    {
       add io.c
@@ -29,7 +30,7 @@ test_feature
 
 test_feature
 {
-   feature SDL
+   feature FEATURE_SDL
    yes
    {
       add threads_sdl.c
diff --git a/source/foundation/sort.c b/source/foundation/sort.c
new file mode 100644 (file)
index 0000000..fbdc36e
--- /dev/null
@@ -0,0 +1,12 @@
+#include <stdlib.h>
+#include "foundation.h"
+
+i32 compar( const void *a, const void *b )
+{
+   return ((struct sort_index *)a)->value - ((struct sort_index *)b)->value;
+}
+
+void index_sort( struct sort_index *indices, u32 indice_count )
+{
+   qsort( indices, indice_count, sizeof(struct sort_index), compar );
+}
index 6ca6b688f107b708d11ce24dcc775ecd4c4ecfe5..b2a024bc0acfd19328cace30a5145e773bebaa09 100644 (file)
@@ -3,16 +3,17 @@
 #include "vg_input.h"
 #include "common_maths.h"
 
-#define KEYBOARD_ALT      0x1
-#define KEYBOARD_SHIFT    0x2
-#define KEYBOARD_CAPSLOCK 0x4
-#define KEYBOARD_CONTROL  0x8
-
 struct
 {
    //i16 clipping_area[4];
-
    i16 mouse_co[2], mouse_co_clicked[2];
+   u32 mouse_mods;
+
+   i16 mouse_co_container_last[4],
+       mouse_co_container[4],
+       mouse_co_clicked_container_last[4],
+       mouse_co_clicked_container[4];
+
    b8 mouse_went_in_click_hole;
 
    /* internal state */
@@ -26,6 +27,7 @@ struct
    active_control_type;
    b8 active_control_touched;
    b8 redraw;
+   b8 fiddling;
 
    union
    {
@@ -49,9 +51,25 @@ struct
          enum dropdown_action action;
       }
       dropdown;
+
+      struct
+      {
+         u32 start_values[4];
+      }
+      number;
    }
    controls;
 
+   struct
+   {
+      struct ui_panel panel;
+      enum data_type data_type;
+      void *value;
+      char buf[32];
+      i16 co[2];
+   }
+   fiddle;
+
    /* user defined state */
    enum ui_text_encoding text_encoding;
 }
@@ -204,43 +222,50 @@ void _ui_widget_row( i16 inout_panel[4], i16 out_rect[4], i16 row_size )
    rect_split( inout_panel, 0, _ui_widget_row_height( row_size ), UI_PADDING_PX, out_rect, inout_panel );
 }
 
+static b8 rect_eq( i16 a[4], i16 b[4] )
+{
+   if( (a[0] == b[0]) &&
+       (a[1] == b[1]) &&
+       (a[2] == b[2]) &&
+       (a[3] == b[3]) )
+      return 1;
+   else return 0;
+}
+
 enum button_action _ui_button( i16 rect[4] )
 {
    union colour debug_colour = (union colour){{ 200, 200, 200, 50 }};
    enum button_action action = k_button_none;
 
-   b8 availible = 0;
-   if( _ui.active_control_type == k_ui_control_none )
-      availible = 1;
-   else if( _ui.active_control_type == k_ui_control_button )
-      availible = ui_inside_rect( rect, _ui.mouse_co_clicked );
+   b8 mouse_in_box = 0,
+      clicked_in_box = 0;
+
+   if( ui_inside_rect( rect, _ui.mouse_co ) )
+   {
+      rect_copy( rect, _ui.mouse_co_container );
+      if( rect_eq( rect, _ui.mouse_co_container_last ) )
+         mouse_in_box = 1;
+   }
+
+   if( ui_inside_rect( rect, _ui.mouse_co_clicked ) )
+      if( rect_eq( rect, _ui.mouse_co_clicked_container_last ) )
+         clicked_in_box = 1;
 
-   if( availible )
+   if( mouse_in_box )
    {
-      if( ui_inside_rect( rect, _ui.mouse_co ) )
+      if( _input_button_down( k_input_action_ui_click, 0 ) )
       {
-         if( _input_button_down( k_input_action_ui_click, 0 ) )
-         {
-            action = k_button_click;
-            debug_colour = (union colour){{ 255, 255, 255, 255 }};
-            _ui.active_control_type = k_ui_control_button;
-         }
-         else
-         {
-            action = _input_button( k_input_action_ui_click )? k_button_repeat: k_button_hover;
-            debug_colour = (union colour){{ 0, 0, 255, 170 }};
-         }
+         action = k_button_click;
+         _ui.active_control_type = k_ui_control_button;
+         rect_copy( rect, _ui.mouse_co_clicked_container );
       }
-      else
-         if( _input_button( k_input_action_ui_click ) )
-            if( ui_inside_rect( rect, _ui.mouse_co_clicked ) )
-            {
-               action = k_button_repeat;
-               debug_colour = (union colour){{ 255, 0, 255, 140 }};
-            }
+
+      if( !_input_button( k_input_action_ui_click ) )
+         action = k_button_hover;
    }
 
-   _graphics_line_rect( rect, debug_colour );
+   if( clicked_in_box && _input_button( k_input_action_ui_click ) && !_input_button_down( k_input_action_ui_click, 0 ) )
+      action = k_button_repeat;
 
    if( action == k_button_click || action == k_button_repeat )
       _ui.active_control_touched = 1;
@@ -340,10 +365,11 @@ static void _ui_textbox_set_cursor_user( i32 position, b8 super )
       _ui.controls.textbox.cursor_pos = position;
 }
 
-void _ui_set_mouse( i16 x, i16 y )
+void _ui_set_mouse( i16 x, i16 y, u32 mods )
 {
    _ui.mouse_co[0] = x;
    _ui.mouse_co[1] = y;
+   _ui.mouse_mods = mods;
 }
 
 void _ui_input_text( const c8 *text )
@@ -365,16 +391,19 @@ enum textbox_action _ui_textbox( i16 rect[4], c8 *text_buffer, u32 text_buffer_l
 
    if( flags & UI_AUTOFOCUS )
    {
-      if( _ui.active_control_type != k_ui_control_textbox )
-         init = 1;
-      else if( _ui.controls.textbox.text_buffer != text_buffer )
-         init = 1;
-
-      if( init )
+      if( !_input_button( k_input_action_ui_click ) ) /* only autofocus if not touching stuff, because thats annoying */
       {
-         i32 p = buffer_last_index( text_buffer, 0, 0 );
-         _ui.controls.textbox.cursor_pos = p;
-         _ui.controls.textbox.cursor_user = p;
+         if( _ui.active_control_type != k_ui_control_textbox )
+            init = 1;
+         else if( _ui.controls.textbox.text_buffer != text_buffer )
+            init = 1;
+
+         if( init )
+         {
+            i32 p = buffer_last_index( text_buffer, 0, 0 );
+            _ui.controls.textbox.cursor_pos = p;
+            _ui.controls.textbox.cursor_user = p;
+         }
       }
    }
 
@@ -507,6 +536,9 @@ enum dropdown_action _ui_dropdown( i16 rect[4], struct ui_dropdown_option *optio
 
 b8 _ui_update(void)
 {
+   rect_copy( _ui.mouse_co_container, _ui.mouse_co_container_last );
+   rect_copy( _ui.mouse_co_clicked_container, _ui.mouse_co_clicked_container_last );
+
    if( _input_button_down( k_input_action_ui_click, 0 ) )
    {
       _ui.mouse_co_clicked[0] = _ui.mouse_co[0];
@@ -628,7 +660,7 @@ b8 _ui_update(void)
    return _ui.redraw;
 }
 
-void _ui_draw(void)
+void _ui_draw( i16 window_size[2] )
 {
    if( _ui.active_control_type == k_ui_control_dropdown )
    {
@@ -642,11 +674,21 @@ void _ui_draw(void)
          button_rect[1] += button_rect[3];
 
          struct ui_dropdown_option *option = &_ui.controls.dropdown.options[i];
-         if( _ui_button( button_rect ) == k_button_click )
+         enum button_action action = _ui_button( button_rect );
+         if( action == k_button_click )
          {
             _ui.controls.dropdown.action = k_dropdown_changed;
             *_ui.controls.dropdown.value = option->value;
+            _ui.active_control_touched = 0;
+            $log( $info, {"Set dropdown enum value to: "}, $signed( option->value ) );
          }
+
+         union colour c = (union colour){{100,100,100,255}};
+         if( action == k_button_hover )
+            c = (union colour){{160,160,160,255}};
+         else if( action != k_button_none )
+            c = (union colour){{200,200,200,255}};
+         _graphics_fill_rect( button_rect, c );
          
          c8 temp[32];
          struct stream string;
@@ -663,11 +705,95 @@ void _ui_draw(void)
          }
       }
       _ui.active_control_type = k_ui_control_dropdown;
+      _ui.active_control_touched = 1;
+   }
+
+   if( _ui.fiddling )
+   {
+      i16 w = 300,
+          h = 96,
+          px = i16_clamp( _ui.fiddle.co[0], 0, window_size[0]-w ),
+          py = i16_clamp( _ui.fiddle.co[1], 0, window_size[1]-h );
+
+      _ui_panel( &_ui.fiddle.panel, (i16[]){ px,py,w,h }, "Fiddle", UI_NO_OPTIONS );
+      if( _ui_panel_content( &_ui.fiddle.panel ) )
+      {
+         enum textbox_action text_action = 
+            _ui_panel_textbox( &_ui.fiddle.panel, _ui.fiddle.buf, sizeof(_ui.fiddle.buf), 1, UI_AUTOFOCUS, "value" );
+
+         struct ui_panel cols[2];
+         _ui_panel_grid( &_ui.fiddle.panel, cols, 1, 2, 1, UI_PADDING_PX, NULL );
+
+         enum button_action set_action = _ui_panel_button( &cols[0], "SET", 0 ),
+                            cancel_action = _ui_panel_button( &cols[1], "Cancel", 0 );
+
+         if( (text_action == k_textbox_enter) || (set_action == k_button_click) )
+         {
+            struct stream parse_string;
+            stream_open_buffer_read( &parse_string, _ui.fiddle.buf, buffer_last_index(_ui.fiddle.buf,0,0), 0 );
+
+            enum string_parse_result result = k_string_parse_error;
+            if( _ui.fiddle.data_type == k_data_type_f32 )
+            {
+               f64 v;
+               result = string_parse_f64(&parse_string, &v);
+               if( result == k_string_parse_ok )
+               {
+                  $log( $low, {"Parsed fiddle f32 value from: "}, {_ui.fiddle.buf} ); 
+                  *((f32 *)_ui.fiddle.value) = (f32)v;
+                  _ui.fiddling = 0;
+               }
+               else
+               {
+                  $log( $error, {"Failed to parse f32 value from: "}, {_ui.fiddle.buf} ); 
+               }
+            }
+
+            if( _ui.fiddle.data_type == k_data_type_i32 )
+            {
+               i64 v;
+               result = string_parse_i64(&parse_string, &v);
+               if( result == k_string_parse_ok )
+               {
+                  $log( $low, {"Parsed fiddle i32 value from: "}, {_ui.fiddle.buf} ); 
+                  *((i32 *)_ui.fiddle.value) = (i32)v;
+                  _ui.fiddling = 0;
+               }
+               else
+               {
+                  $log( $error, {"Failed to parse i32 value from: "}, {_ui.fiddle.buf} ); 
+               }
+            }
+         }
+
+         if( cancel_action == k_button_click )
+         {
+            $log( $low, {"Cancel fiddle"} ); 
+            _ui.fiddling = 0;
+         }
+      }
    }
 
    _ui.redraw = 0;
    _graphics_fill_rect( (i16[]){ _ui.mouse_co[0],_ui.mouse_co[1], 4,4 }, (union colour){{0,0,0,255}} );
    _graphics_fill_rect( (i16[]){ _ui.mouse_co[0],_ui.mouse_co[1], 3,3 }, (union colour){{255,255,255,255}} );
+
+   i16 padded_container[4],
+       padded_container_click[4];
+
+   rect_copy( _ui.mouse_co_container_last, padded_container );
+   rect_copy( _ui.mouse_co_clicked_container_last, padded_container_click );
+
+   for( i32 i=0; i<2; i ++ )
+   {
+      padded_container[i] -= 2;
+      padded_container[i+2] += 4;
+      padded_container_click[i] -= 2;
+      padded_container_click[i+2] += 4;
+   }
+
+   _graphics_line_rect( padded_container, (union colour){{100,100,100,255}} );
+   _graphics_line_rect( padded_container_click, (union colour){{190,190,100,255}} );
 }
 
 b8 _ui_want_mouse( i16 area[4] )
@@ -691,3 +817,317 @@ b8 _ui_want_text(void)
 {
    return _ui.active_control_type == k_ui_control_textbox;
 }
+
+void _ui_panel( struct ui_panel *panel, i16 base_rect[4], const c8 *title, u32 flags )
+{
+   panel->main.title = title;
+   panel->flags = UI_MAIN | flags;
+   rect_copy( base_rect, panel->main.rect_base );
+   if( panel->main.hidden )
+      panel->main.rect_base[3] = 24;
+   _ui_button( panel->main.rect_base ); // Click hole the mouse
+}
+
+void _ui_panel_row( struct ui_panel *panel, i16 size, i16 padding, struct ui_panel *out_row )
+{
+   out_row->flags = UI_FULL_HEIGHT;
+   i16 kerning[3];
+   _font_get_kerning( kerning );
+   rect_split( panel->content_area, UI_HORIZONTAL, kerning[1]*size + UI_PADDING_PX, 0, out_row->content_area, panel->content_area );
+
+   if( padding )
+   {
+      i16 _[4];
+      rect_split( panel->content_area, UI_HORIZONTAL, padding, 0, _, panel->content_area );
+   }
+}
+
+void _ui_panel_grid( struct ui_panel *panel, struct ui_panel *out_panels, i32 rows, i32 columns, i16 row_size, i16 padding, f32 *column_bias )
+{
+   f32 column_total_bias = 0.0f;
+   if( column_bias )
+      for( i32 x=0; x<columns; x ++ )
+         column_total_bias += column_bias[x];
+
+   for( i32 y=0; y<rows; y ++ )
+   {
+      struct ui_panel row;
+      _ui_panel_row( panel, row_size, ((y+1)==rows)? UI_PADDING_PX: 0, &row );
+
+      for( i32 x=0; x<columns; x ++ )
+      {
+         struct ui_panel *leaf = &out_panels[ y*columns + x ];
+         leaf->flags = UI_FULL_HEIGHT;
+
+         i16 column_width = (panel->content_area[2]) / columns;
+         if( column_bias )
+         {
+            f32 ratio = column_bias[x] / column_total_bias;
+            column_width = (f32)panel->content_area[2] * ratio;
+         }
+         rect_split( row.content_area, UI_VERTICAL, column_width, 0, leaf->content_area, row.content_area );
+         if( x == columns-1 )
+            leaf->content_area[2] = (panel->content_area[0] + panel->content_area[2]) - leaf->content_area[0];
+         else
+            leaf->content_area[2] -= padding;
+      }
+   }
+}
+
+void _ui_panel_widget( struct ui_panel *panel, i16 out_rect[4] )
+{
+   if( panel->flags & UI_FULL_HEIGHT )
+      rect_copy( panel->content_area, out_rect );
+   else
+   {
+      struct ui_panel row;
+      _ui_panel_row( panel, 1, UI_PADDING_PX, &row );
+      rect_copy( row.content_area, out_rect );
+   }
+}
+
+enum button_action _ui_panel_button_internal( i16 rect[4], const c8 *text, u32 flag )
+{
+   enum button_action action = k_button_none;
+   if( !(flag & UI_DISABLED) )
+      action = _ui_button( rect );
+
+   union colour c = (union colour){{200,200,200,255}};
+   union colour tc = (flag & UI_DULL)? (union colour){{140,140,140,255}}: (union colour){{20,20,20,255}};
+
+   if( action == k_button_hover )
+      c = (union colour){{230,230,230,255}};
+   else if( action != k_button_none )
+      c = (union colour){{255,255,255,255}};
+   if( flag & UI_DISABLED )
+   {
+      c = (union colour){{140,140,140,255}};
+      tc = (union colour){{160,160,160,255}};
+   }
+
+   _graphics_fill_rect( rect, c );
+   _ui_text( rect, text, k_ui_align_center, tc );
+
+   if( flag & UI_MARKED )
+   {
+      i16 outline_rect[4] = { rect[0] - 2, rect[1] - 2, rect[2] + 4, rect[3] + 4 };
+      _graphics_line_rect( outline_rect, (union colour){{0,255,20,200}} );
+   }
+   return action;
+}
+
+b8 _ui_panel_content( struct ui_panel *panel )
+{
+   ASSERT_CRITICAL( panel->flags & UI_MAIN );
+   _graphics_fill_rect( panel->main.rect_base, (union colour){{50,50,50,160}} );
+
+   i16 kerning[3];
+   _font_get_kerning( kerning );
+
+   i16 title_box[4] = { panel->main.rect_base[0], panel->main.rect_base[1], 
+                        panel->main.rect_base[2], kerning[1] + UI_PADDING_PX };
+   _ui_text( title_box, panel->main.title, k_ui_align_center, (union colour){{255,255,255,255}} );
+
+   b8 show = !panel->main.hidden;
+   if( !(panel->flags & UI_NO_OPTIONS) )
+   {
+      i16 toggle_box[4] = { title_box[0] + title_box[2] - 20, title_box[1], 20, title_box[3] };
+      if( _ui_panel_button_internal( toggle_box, panel->main.hidden? "+": "-", 0 ) == k_button_click ) 
+      {
+         panel->main.hidden ^= 0x1;
+      }
+   }
+
+   panel->content_area[0] = panel->main.rect_base[0] + UI_PADDING_PX;
+   panel->content_area[1] = title_box[1] + title_box[3] + UI_PADDING_PX;
+   panel->content_area[2] = panel->main.rect_base[2] - UI_PADDING_PX*2;
+   panel->content_area[3] = ((panel->main.rect_base[1] + panel->main.rect_base[3]) - panel->content_area[1]) - UI_PADDING_PX;
+   return show;
+}
+
+enum button_action _ui_panel_button( struct ui_panel *panel, const c8 *text, u32 flag )
+{
+   i16 button_rect[4];
+   _ui_panel_widget( panel, button_rect );
+   return _ui_panel_button_internal( button_rect, text, flag );
+}
+
+enum button_action _ui_panel_toggle( struct ui_panel *panel, const c8 *label, u32 *value, u32 toggle_mask, u32 flag,
+                                const c8 *options[2] )
+{
+   struct ui_panel cols[3];
+   _ui_panel_grid( panel, cols, 1, 3, 1, 0, (f32[]){ 4.0f, 0.5f, 3.0f } );
+
+   i16 label_box[4];
+   _ui_panel_widget( cols+0, label_box );
+   
+   union colour tc = (flag & UI_DULL)? (union colour){{20,20,20,255}}: (union colour){{200,200,200,255}};
+   _ui_text( label_box, label, k_ui_align_y_center | k_ui_align_x_right, tc );
+
+   b8 yes = ((*value) & toggle_mask) == toggle_mask;
+   enum button_action action = _ui_panel_button( cols+2, yes? (options? options[1]:"Yes"): (options? options[0]:"No"), flag );
+
+   if( action == k_button_click )
+      *value = (*value) ^ toggle_mask;
+
+   return action;
+}
+
+enum textbox_action _ui_panel_textbox( struct ui_panel *panel, c8 *text_buffer, u32 text_buffer_length, u32 lines, u32 flag, const c8 *label )
+{
+   struct ui_panel cols[3];
+   _ui_panel_grid( panel, cols, 1, 3, 1, 0, (f32[]){ 1.0f, 0.5f, 4.0f } );
+
+   i16 label_box[4];
+   _ui_panel_widget( cols+0, label_box );
+   
+   union colour tc = (flag & UI_DULL)? (union colour){{20,20,20,255}}: (union colour){{200,200,200,255}};
+   _ui_text( label_box, label, k_ui_align_y_center | k_ui_align_x_right, tc );
+
+   i16 rect[4];
+   _ui_panel_widget( cols+2, rect );
+   enum textbox_action action = _ui_textbox( rect, text_buffer, text_buffer_length, lines, flag );
+   return action;
+}
+
+void _ui_panel_number( struct ui_panel *panel, const c8 *label, 
+                       void *values, 
+                       void *mins,
+                       void *maxs,
+                       enum data_type data_type, u32 flag )
+{
+   f32 *values_f32 = values;
+   f32 *mins_f32 = mins;
+   f32 *maxs_f32 = maxs;
+   f32 *start_f32s = (void *)_ui.controls.number.start_values;
+
+   i32 *values_i32 = values;
+   i32 *mins_i32 = mins;
+   i32 *maxs_i32 = maxs;
+   i32 *start_i32s = (void *)_ui.controls.number.start_values;
+
+   struct ui_panel grid[6*16];
+   _ui_panel_grid( panel, grid, DATATYPE_GET_COUNT(data_type), 6, 1, 0, (f32[]){ 4.0f,0.5f,0.75f,2.0f,0.75f,0.75f } );
+
+   i16 label_box[4];
+   _ui_panel_widget( &grid[0], label_box );
+   union colour tc = (flag & UI_DULL)? (union colour){{20,20,20,255}}: (union colour){{200,200,200,255}};
+   _ui_text( label_box, label, k_ui_align_y_center | k_ui_align_x_right, tc );
+
+   for( u32 i=0; i<DATATYPE_GET_COUNT(data_type); i ++ )
+   {
+      struct ui_panel *buttons = grid + (i*6) + 2;
+      enum button_action down_a = _ui_panel_button( &buttons[0], "-", flag ),
+                         edit_a = _ui_panel_button( &buttons[3], "=", flag ),
+                         up_a   = _ui_panel_button( &buttons[2], "+", flag );
+      enum button_action main_a = _ui_button( buttons[1].content_area );
+
+      b8 modified = 0;
+      enum data_type single_type = DATATYPE_GET_SINGLE( data_type );
+      
+      /* inc/dec */
+      if( down_a == k_button_click ) 
+      {
+              if( single_type == k_data_type_i32 ) values_i32[i] --;
+         else if( single_type == k_data_type_f32 ) values_f32[i] -= 1.0f;
+         modified = 1;
+      }
+      if( up_a == k_button_click )
+      {
+              if( single_type == k_data_type_i32 ) values_i32[i] ++;
+         else if( single_type == k_data_type_f32 ) values_f32[i] += 1.0f;
+         modified = 1;
+      }
+
+      /* slider action */
+      if( main_a == k_button_click )
+      {
+              if( single_type == k_data_type_i32 ) start_i32s[i] = values_i32[i];
+         else if( single_type == k_data_type_f32 ) start_f32s[i] = values_f32[i];
+      }
+      if( main_a == k_button_repeat )
+      {
+         i32 delta = _ui.mouse_co[0] - _ui.mouse_co_clicked[0],
+             div   = 1;
+         if( _ui.mouse_mods & UI_KEYBOARD_ALT )
+            div = 8;
+              if( single_type == k_data_type_i32 ) values_i32[i] = start_i32s[i] + (delta/div);
+         else if( single_type == k_data_type_f32 ) values_f32[i] = start_f32s[i] + ((f32)delta/(f32)div)/10.0f;
+         modified = 1;
+      }
+
+      if( modified && (mins && maxs) )
+      {
+              if( single_type == k_data_type_i32 )
+            values_i32[i] = i32_clamp( values_i32[i], mins_i32[i], maxs_i32[i] );
+         else if( single_type == k_data_type_f32 ) 
+            values_f32[i] = f32_clamp( values_f32[i], mins_f32[i], maxs_f32[i] );
+      }
+
+      c8 buf[32];
+      struct stream number_str;
+      stream_open_buffer_write( &number_str, buf, sizeof(buf), k_stream_null_terminate );
+
+      if( single_type == k_data_type_i32 ) 
+      {
+         $v_string( &number_str, $signed( values_i32[i] ) );
+      }
+      else if( single_type == k_data_type_f32 ) 
+      {
+         $v_string( &number_str, $float( values_f32[i] ) );
+      }
+      else
+      {
+         $v_string( &number_str, {"NOT_VIEWABLE"} );
+      }
+
+      _ui_panel_widget( &buttons[1], label_box );
+      _graphics_fill_rect( label_box, (union colour){{20,20,20,255}} );
+      _graphics_line_rect( label_box, (union colour){{60,60,60,255}} );
+      _ui_text( label_box, buf, k_ui_align_center, tc );
+
+      if( edit_a == k_button_click )
+      {
+         buffer_copy( buf, 0, _ui.fiddle.buf, sizeof(_ui.fiddle.buf) );
+         _ui.fiddling = 1;
+         _ui.fiddle.value = values + DATATYPE_GET_WIDTH(data_type) * i;
+         _ui.fiddle.data_type = single_type;
+         _ui.fiddle.co[0] = buttons[2].content_area[0];
+         _ui.fiddle.co[1] = buttons[2].content_area[1];
+      }
+   }
+}
+
+enum dropdown_action _ui_panel_enum( struct ui_panel *panel, const c8 *label, i32 *value, 
+                                     struct ui_dropdown_option *options, u32 option_count, u32 flag )
+{
+   struct ui_panel cols[3];
+   _ui_panel_grid( panel, cols, 1, 3, 1, 0, (f32[]){ 1.0f, 0.5f, 4.0f } );
+
+   i16 label_box[4];
+   _ui_panel_widget( &cols[0], label_box );
+   
+   union colour tc = (flag & UI_DULL)? (union colour){{20,20,20,255}}: (union colour){{200,200,200,255}};
+   _ui_text( label_box, label, k_ui_align_y_center | k_ui_align_x_right, tc );
+
+   i16 rect[4];
+   _ui_panel_widget( &cols[2], rect );
+   enum dropdown_action action = _ui_dropdown( rect, options, option_count, value );
+   return action;
+}
+
+void _ui_panel_heading( struct ui_panel *panel, const c8 *label, u32 flag )
+{
+   i16 kerning[3];
+   _font_get_kerning( kerning );
+
+   i16 line[4];
+   rect_split( panel->content_area, UI_HORIZONTAL, 2+UI_PADDING_PX/2, UI_PADDING_PX, line, panel->content_area );
+   _graphics_fill_rect( line, (union colour){{20,20,20,255}} );
+
+   i16 rect[4];
+   rect_split( panel->content_area, UI_HORIZONTAL, kerning[1] + UI_PADDING_PX, UI_PADDING_PX, rect, panel->content_area );
+
+   union colour tc = (flag & UI_DULL)? (union colour){{20,20,20,255}}: (union colour){{200,200,200,255}};
+   _ui_text( rect, label, k_ui_align_center, tc );
+}
index a21b8e4bcde162535014282d41239880329e9870..3c53591b01d2ecd21ba27e69596182b33c464235 100644 (file)
@@ -94,7 +94,7 @@ b8 _font_decode_bitmap( i16 uv[2] );
 
 /* Immediate mode UI
  * ------------------------------------------------------------------------------------------------------------------ */
-void _ui_set_mouse( i16 x, i16 y );
+void _ui_set_mouse( i16 x, i16 y, u32 mods );
 void _ui_get_mouse_co( i16 out_co[2] );
 void _ui_input_text( const c8 *text );
 b8 _ui_want_mouse( i16 area[4] );
@@ -104,14 +104,24 @@ b8 _ui_want_text(void);
 #define UI_ROW_PADDING_PX 18
 #define UI_HORIZONTAL 0
 #define UI_VERTICAL   1
-#define MOUSE_LEFT  0x1
-#define MOUSE_RIGHT 0x2
+#define UI_MOUSE_LEFT  0x1
+#define UI_MOUSE_RIGHT 0x2
+#define UI_KEYBOARD_ALT      0x1
+#define UI_KEYBOARD_SHIFT    0x2
+#define UI_KEYBOARD_CAPSLOCK 0x4
+#define UI_KEYBOARD_CONTROL  0x8
 
 #define UI_AUTOFOCUS 0x1
 #define UI_MULTILINE 0x2
+#define UI_FULL_HEIGHT  0x100
+#define UI_DULL         0x200
+#define UI_MARKED       0x400
+#define UI_MAIN         0x800
+#define UI_DISABLED     0x1000
+#define UI_NO_OPTIONS   0x2000
 
 b8 _ui_update(void);
-void _ui_draw(void);
+void _ui_draw( i16 window_size[2] );
 
 /* Text */
 enum ui_text_encoding
@@ -197,3 +207,45 @@ enum textbox_action
    k_textbox_escape
 };
 enum textbox_action _ui_textbox( i16 rect[4], c8 *text_buffer, u32 text_buffer_length, u32 lines, u32 flags );
+
+/* Panel based immediate mode UI
+ * ------------------------------------------------------------------------------------------------------------------ */
+struct ui_panel
+{
+   union
+   {
+      struct
+      {
+         const c8 *title;
+         b8 hidden;
+         i16 rect_base[4];
+      }
+      main;
+   };
+
+   u32 flags;
+   i16 content_area[4];
+};
+
+void _ui_panel( struct ui_panel *panel, i16 base_rect[4], const c8 *title, u32 flags );
+void _ui_panel_row( struct ui_panel *panel, i16 size, i16 padding, struct ui_panel *out_row );
+void _ui_panel_grid( struct ui_panel *panel, struct ui_panel *out_panels, i32 rows, i32 columns, i16 row_size,
+                       i16 padding, f32 *column_bias );
+void _ui_panel_widget( struct ui_panel *panel, i16 out_rect[4] );
+enum button_action _ui_panel_button_internal( i16 rect[4], const c8 *text, u32 flag );
+b8 _ui_panel_content( struct ui_panel *panel );
+enum button_action _ui_panel_button( struct ui_panel *panel, const c8 *text, u32 flag );
+enum button_action _ui_panel_toggle( struct ui_panel *panel, const c8 *label, u32 *value, u32 toggle_mask, 
+                                    u32 flag, const c8 *options[2] );
+enum textbox_action _ui_panel_textbox( struct ui_panel *panel, c8 *text_buffer, u32 text_buffer_length, u32 lines, 
+                                    u32 flag, const c8 *label );
+void _ui_panel_heading( struct ui_panel *panel, const c8 *label, u32 flag );
+
+void _ui_panel_number( struct ui_panel *panel, const c8 *label, 
+                       void *values, 
+                       void *mins,
+                       void *maxs,
+                       enum data_type data_type, u32 flag );
+
+enum dropdown_action _ui_panel_enum( struct ui_panel *panel, const c8 *label, i32 *value, 
+                                     struct ui_dropdown_option *options, u32 option_count, u32 flag );
index 64c3ead7d3daebd892ce80bd606cdf623759612d..f7ed14db95bca3d289f595e050ce8afc9fd1cd3d 100644 (file)
@@ -90,6 +90,7 @@ struct
 {
    struct stream name, tripple, folder;
    const c8 *enabled_features[ 64 ];
+   u32 feature_count;
 }
 static _metacompiler;
 
@@ -157,8 +158,8 @@ void _cannonicalize( struct file_context *file_context, const c8 *path, struct s
    free( source_file_path );
 }
 
-void _assemble_unit_file( const c8 *path, u32 assembly_block, u32 feature_count );
-void _parse_kv_block( struct keyvalues *kvs, u32 block, struct file_context *file_context, u32 assembly_block, u32 feature_count )
+void _assemble_unit_file( const c8 *path, u32 assembly_block );
+void _parse_kv_block( struct keyvalues *kvs, u32 block, struct file_context *file_context, u32 assembly_block )
 {
    u32 it = 0;
    while( keyvalues_foreach( kvs, &it, block, NULL ) )
@@ -172,18 +173,18 @@ void _parse_kv_block( struct keyvalues *kvs, u32 block, struct file_context *fil
             ASSERT_CRITICAL( test_feature );
 
             b8 result = 0;
-            for( u32 i=0; i<feature_count; i ++ )
+            for( u32 i=0; i<_metacompiler.feature_count; i ++ )
                if( compare_buffers( _metacompiler.enabled_features[i], 0, test_feature, 0 ) )
                   result = 1;
 
             u32 pass_block = keyvalues_get( kvs, it, result? "yes": "no", 0 );
             if( pass_block )
-               _parse_kv_block( kvs, pass_block, file_context, assembly_block, feature_count );
+               _parse_kv_block( kvs, pass_block, file_context, assembly_block );
          }
          else
          {
             /* Add the block title to the assembly if it's not a pre-processor thing */
-            _parse_kv_block( kvs, it, file_context, keyvalues_append_frame( &_assembly, assembly_block, it_k ), feature_count );
+            _parse_kv_block( kvs, it, file_context, keyvalues_append_frame( &_assembly, assembly_block, it_k ) );
          }
       }
       else
@@ -192,7 +193,11 @@ void _parse_kv_block( struct keyvalues *kvs, u32 block, struct file_context *fil
 
          /* ENABLE FEATURE */
          if( compare_buffers( it_k, 0, "enable", 0 ) )
-            _metacompiler.enabled_features[ feature_count ++ ] = it_v;
+         {
+            ASSERT_CRITICAL( _metacompiler.feature_count < ARRAY_COUNT( _metacompiler.enabled_features ) );
+            u32 copied_kv = keyvalues_append_string( &_assembly, assembly_block, it_k, it_v );
+            _metacompiler.enabled_features[ _metacompiler.feature_count ++ ] = keyvalues_value( &_assembly, copied_kv, NULL );
+         }
 
          /* APPEND */
          else if( compare_buffers( it_k, 0, "append", 0 ) )
@@ -202,7 +207,7 @@ void _parse_kv_block( struct keyvalues *kvs, u32 block, struct file_context *fil
                struct stream path_string;
                stream_open_stack( &path_string, _temporary_stack_allocator(), k_stream_null_terminate );
                _cannonicalize( file_context, it_v, &path_string );
-               _assemble_unit_file( string_get( &path_string ), assembly_block, feature_count );
+               _assemble_unit_file( string_get( &path_string ), assembly_block );
             }
             _end_temporary_frame( temp_frame );
          }
@@ -214,7 +219,7 @@ void _parse_kv_block( struct keyvalues *kvs, u32 block, struct file_context *fil
    }
 }
 
-void _assemble_unit_file( const c8 *path, u32 assembly_block, u32 feature_count )
+void _assemble_unit_file( const c8 *path, u32 assembly_block )
 {
    u32 temp_frame = _start_temporary_frame();
    {
@@ -223,7 +228,7 @@ void _assemble_unit_file( const c8 *path, u32 assembly_block, u32 feature_count
 
       struct keyvalues kvs;
       ASSERT_CRITICAL( keyvalues_read_file( &kvs, path, _temporary_stack_allocator() ) );
-      _parse_kv_block( &kvs, 0, file_context, assembly_block, feature_count );
+      _parse_kv_block( &kvs, 0, file_context, assembly_block );
 
       file_context->assembly_kv_end = keyvalues_current_offset( &_assembly );
    }
@@ -579,7 +584,8 @@ void _codegen_console(void)
       ASSERT_CRITICAL( name && function );
 
       $v_string( &H, {"i32 "}, {function}, {"( struct console_arguments *args );\n"} );
-      $v_string( &C, {"   {\n      .alias = \""}, {name}, {"\",\n      .type = k_console_item_ccmd,\n      .fn = "}, {function}, {"\n   },\n"} );
+      $v_string( &C, {"   {\n      .alias = \""}, {name}, {"\",\n      .type = k_data_type_function,\n      .fn = "}, 
+                     {function}, {"\n   },\n"} );
    }
 
    u32 cvar_it = 0;
@@ -591,7 +597,7 @@ void _codegen_console(void)
       const c8 *cheat = keyvalues_read_string( &_assembly, cvar_it, "cheat", NULL );
       ASSERT_CRITICAL( name && type && value );
       $v_string( &H, {"extern "}, {type}, {" _cvar_"}, {name}, {";\n"} );
-      $v_string( &C, {"   {\n      .alias = \""}, {name}, {"\",\n      .type = k_console_item_cvar_"},{type},
+      $v_string( &C, {"   {\n      .alias = \""}, {name}, {"\",\n      .type = k_data_type_"},{type},
                      {",\n      ._"},{type},{" = &_cvar_"}, {name}, {"\n   },\n"} );
    }
    $v_string( &C, {"};\n\n"} );
@@ -674,7 +680,7 @@ void _codegen_entities(void)
       const c8 *name;
       u32 id;
 
-      u16 function_start, function_count;
+      u16 function_start, function_count, parameter_start, parameter_count;
    }
    entities[256];
    u32 entity_count = 0;
@@ -707,18 +713,21 @@ void _codegen_entities(void)
 
    index_sort( entity_ids, entity_count );
 
-   $v_string( &H, {"enum entity_type\n{\n   k_ent_none = 0,\n"} );
+   $v_string( &H, {"enum entity_type\n{\n  k_ent_none = 0,\n"} );
    for( u32 i=0; i<entity_count; i ++ )
       $v_string( &H, {"   k_"}, {entities[entity_ids[i].index].name}, {" = "}, $signed(entity_ids[i].value), {",\n"} );
+   $v_string( &H, {"  k_ent_total_count = "},$unsigned(entity_count),{"\n"} );
    $v_string( &H, {"};\n"} );
 
-   u32 function_count = 0;
+   u32 function_count = 0,
+       parameter_count = 0;
 
    $v_string( &H, {"/* serialized entity structures */\n"} );
    for( u32 i=0; i<entity_count; i ++ )
    {
       struct entity *ent = &entities[ entity_ids[i].index ];
       ent->function_start = function_count;
+      ent->parameter_start = parameter_count;
 
       if( !keyvalues_get( &_assembly, ent->block, "parameter", 0 ) )
       {
@@ -792,6 +801,10 @@ void _codegen_entities(void)
                   
                   { "f32",       "f32",                  "","c_float" },
                   { "f64",       "f64",                  "","c_double" },
+
+                  { "entity_id", "union entity_id","","c_uint32" },
+                  { "2dbox",     "f32",            "[2][2]","(c_float*2)*2" },
+                  { "3dbox",     "f32",            "[2][3]","(c_float*3)*2" }
                };
 
                i32 type_index = -1;
@@ -808,6 +821,9 @@ void _codegen_entities(void)
 
                $v_string( &H, {"   "}, {types[type_index].c_type}, {" "}, {name}, {types[type_index].c_post}, {";\n"} );
                $v_string( &PY, {"   (\""}, {name}, {"\","}, {types[type_index].py_type}, {"),\n"} );
+
+               ent->parameter_count ++;
+               parameter_count ++;
             }
             $v_string( &H, {"};\n"} );
          }
@@ -825,18 +841,43 @@ void _codegen_entities(void)
    }
 
    /* entity info structures */
+   $v_string( &H, {"/* Enitity information */\n"} );
+   $v_string( &H, {"extern struct entity_info _entity_infos[];\n"} );
    $v_string( &C, {"/* Enitity information */\n"} );
    $v_string( &C, {"struct entity_info _entity_infos[] = {\n"} );
    for( u32 i=0; i<entity_count; i ++ )
    {
       struct entity *ent = &entities[ entity_ids[i].index ];
       i32 ent_id = entity_ids[i].value;
-      $v_string( &C, {"   { .id = "}, $signed(ent_id), {", .name = \""}, {ent->name}, {"\", .function_start="}, 
-                  $unsigned( ent->function_start ), {", .function_count = "}, 
-                  $unsigned( ent->function_count ), {" },\n"} );
+      $v_string( &C, {"   { .id = "}, $signed(ent_id), {", .name = \""}, {ent->name}, {"\""}, 
+                  {", .size=sizeof(struct "}, {ent->name}, {")\n"},
+                  {",\n.function_start="}, $unsigned( ent->function_start ), 
+                  {", .function_count="}, $unsigned( ent->function_count ), 
+                  {",\n.parameter_start="}, $unsigned( ent->parameter_start ), 
+                  {", .parameter_count="}, $unsigned( ent->parameter_count ), 
+                  {" },\n"} );
    }
    $v_string( &C, {"};\n"} );
 
+   /* function info structures (indexed by entity structures) */
+   $v_string( &C, {"/* Parameter information */\n"} );
+   $v_string( &C, {"struct entity_parameter_info _entity_parameter_infos[] = {\n"} );
+   for( u32 i=0; i<entity_count; i ++ )
+   {
+      struct entity *ent = &entities[ entity_ids[i].index ];
+      u32 parameter_it = 0;
+      while( keyvalues_foreach( &_assembly, &parameter_it, ent->block, "parameter" ) )
+      {
+         const c8 *name = keyvalues_read_string( &_assembly, parameter_it, "name", NULL );
+         const c8 *type = keyvalues_read_string( &_assembly, parameter_it, "type", NULL );
+         ASSERT_CRITICAL( name && type );
+         $v_string( &C, {"   { .name = \""}, {name}, {"\""} );
+         $v_string( &C, {", .type = k_data_type_"}, {type} );
+         $v_string( &C, {", .offset = __builtin_offsetof(struct "}, {ent->name}, {", "}, {name}, {")"} );
+         $v_string( &C, {" },\n"} );
+      }
+   }
+   $v_string( &C, {"};\n"} );
 
    /* function info structures (indexed by entity structures) */
    $v_string( &C, {"/* Function information */\n"} );
@@ -855,14 +896,14 @@ void _codegen_entities(void)
    $v_string( &C, {"};\n"} );
 
    /* Entity info, and function to get it by entity ID */
-   $v_string( &C, {"struct entity_info *_vg_entity_info( u16 entity_type )\n{\n"} );
+   $v_string( &C, {"u32 _vg_entity_type_index( u16 entity_type )\n{\n"} );
    for( u32 i=0; i<entity_count; i ++ )
    {
       struct entity *ent = &entities[ entity_ids[i].index ];
       i32 ent_id = entity_ids[i].value;
-      $v_string( &C, {"   if( entity_type == "}, $signed(ent_id), {" ) return &_entity_infos["}, $unsigned(i), {"];\n"} );
+      $v_string( &C, {"   if( entity_type == "}, $signed(ent_id), {" ) return "}, $unsigned(i), {";\n"} );
    }
-   $v_string( &C, {"   ASSERT_CRITICAL(0); return NULL;\n"} );
+   $v_string( &C, {"   ASSERT_CRITICAL(0); return 0;\n"} );
    $v_string( &C, {"}\n"} );
 
    /* Entity functions, and dispatch */
@@ -1010,13 +1051,12 @@ i32 main( i32 argc, const c8 *argv[] )
    keyvalues_append_string( &_assembly, info_block, "tripple", string_get( &_metacompiler.tripple ) );
    keyvalues_append_string( &_assembly, info_block, "folder", string_get( &_metacompiler.folder ) );
 
-   i32 start_features = 0;
    if( _option_platform == k_platform_linux ||
-       _option_platform == k_platform_web   )   _metacompiler.enabled_features[start_features ++] = "linux";
-   if( _option_platform == k_platform_windows ) _metacompiler.enabled_features[start_features ++] = "windows";
+       _option_platform == k_platform_web   )   _metacompiler.enabled_features[_metacompiler.feature_count ++] = "PLATFORM_LINUX";
+   if( _option_platform == k_platform_windows ) _metacompiler.enabled_features[_metacompiler.feature_count ++] = "PLATFORM_WINDOWS";
 
-   if( _option_platform == k_platform_web   )   _metacompiler.enabled_features[start_features ++] = "web";
-   _assemble_unit_file( _option_file_path, 0, start_features );
+   if( _option_platform == k_platform_web   )   _metacompiler.enabled_features[_metacompiler.feature_count ++] = "PLATFORM_WEB";
+   _assemble_unit_file( _option_file_path, 0 );
    
    if( _option_assemble_only )
    {
@@ -1117,6 +1157,11 @@ i32 main( i32 argc, const c8 *argv[] )
 
       $v_string( &cmd->line, {"      -I. \\\n"} );
 
+      for( u32 i=0; i<_metacompiler.feature_count; i ++ )
+      {
+         $v_string( &cmd->line, {"    -D"}, {_metacompiler.enabled_features[i]}, {" \\\n"} );
+      }
+
       u32 compile_it = 0;
       while( keyvalues_foreach( &_assembly, &compile_it, 0, NULL ) )
       {
@@ -1142,7 +1187,9 @@ i32 main( i32 argc, const c8 *argv[] )
             $v_string( &cmd->line, {keyvalues_value( &_assembly, compile_it, NULL )}, {" \\\n"} );
          }
       }
+#if 0
       $v_string( &cmd->line, {"    generated/hooks.c \\\n"} );
+#endif
 
       $v_string( &cmd->line, {"-o "}, {string_get( &_metacompiler.folder )}, {"/"}, {string_get(&_metacompiler.name)} );
       if( _option_platform == k_platform_windows ) { $v_string( &cmd->line, {_option_shared? ".dll ": ".exe "} ); }
index 7b8142ed48fa9a10f739cda20da359845f5cef0b..8df1618f3b4ba0835156a88fffcda0a4809ef80b 100644 (file)
@@ -11,3 +11,75 @@ typedef                float f32;
 typedef               double f64;
 typedef unsigned        char b8;
 #define NULL 0
+
+#define DATATYPE_SIZE( COUNT, BYTES ) (0x0100*(COUNT-1) | 0x0020*(BYTES-1))
+#define DATATYPE_GET_WIDTH( DATA_TYPE ) (((DATA_TYPE & k_data_mask_width) >> 5)+1)
+#define DATATYPE_GET_COUNT( DATA_TYPE ) (((DATA_TYPE & k_data_mask_count) >> 8)+1)
+#define DATATYPE_GET_SINGLE( DATA_TYPE ) (data_type & (k_data_mask_format | k_data_mask_width))
+enum data_type
+{
+   k_data_mask_semantic = 0x001f, /* intended purpose      0 -> 31 (x1F) */
+   k_data_mask_width    = 0x00e0, /* single element width  1 -> 8  */
+   k_data_mask_count    = 0x0f00, /* vector wdith          1 -> 16 */
+   k_data_mask_format   = 0xf000, /* binary format of data 0 -> 15 */
+
+   k_data_semantic_scalar   = 0x0000,
+   k_data_semantic_boolean  = 0x0002,
+   k_data_semantic_character= 0x0003,
+   k_data_semantic_colour   = 0x0004,
+   k_data_semantic_rotation = 0x0005,
+   k_data_semantic_transform= 0x0006,
+   k_data_semantic_vector   = 0x0007,
+   k_data_semantic_box      = 0x0008,
+   k_data_semantic_key      = 0x0009,
+   k_data_semantic_offset_string = 0x000A,
+
+   k_data_format_unsigned = 0x1000,
+   k_data_format_signed   = 0x2000,
+   k_data_format_float    = 0x3000,
+   k_data_format_void     = 0x4000,
+
+   k_data_type_none = 0,
+
+   /* unsigned integers */
+   k_data_type_u8   = k_data_format_unsigned | DATATYPE_SIZE( 1, 1 ) | k_data_semantic_scalar,
+   k_data_type_u16  = k_data_format_unsigned | DATATYPE_SIZE( 1, 2 ) | k_data_semantic_scalar,
+   k_data_type_u32  = k_data_format_unsigned | DATATYPE_SIZE( 1, 4 ) | k_data_semantic_scalar,
+   k_data_type_u64  = k_data_format_unsigned | DATATYPE_SIZE( 1, 8 ) | k_data_semantic_scalar,
+   k_data_type_b8   = k_data_format_unsigned | DATATYPE_SIZE( 1, 1 ) | k_data_semantic_boolean,
+
+   k_data_type_pstr = k_data_format_unsigned | DATATYPE_SIZE( 1, 4 ) | k_data_semantic_offset_string,
+   k_data_type_entity_id = k_data_format_unsigned | DATATYPE_SIZE( 1, 4 ) | k_data_semantic_key,
+
+   /* signed integers */
+   k_data_type_i8   = k_data_format_signed | DATATYPE_SIZE( 1, 1 ) | k_data_semantic_scalar,
+   k_data_type_i16  = k_data_format_signed | DATATYPE_SIZE( 1, 2 ) | k_data_semantic_scalar,
+   k_data_type_i32  = k_data_format_signed | DATATYPE_SIZE( 1, 4 ) | k_data_semantic_scalar,
+    k_data_type_ivec2 = k_data_format_signed | DATATYPE_SIZE( 2, 4 ) | k_data_semantic_vector,
+    k_data_type_ivec3 = k_data_format_signed | DATATYPE_SIZE( 3, 4 ) | k_data_semantic_vector,
+    k_data_type_ivec4 = k_data_format_signed | DATATYPE_SIZE( 4, 4 ) | k_data_semantic_vector,
+   k_data_type_i64  = k_data_format_signed | DATATYPE_SIZE( 1, 8 ) | k_data_semantic_scalar,
+   k_data_type_c8   = k_data_format_signed | DATATYPE_SIZE( 1, 1 ) | k_data_semantic_character,
+
+   /* floating types */
+   k_data_type_f32  = k_data_format_float  | DATATYPE_SIZE( 1, 4 ) | k_data_semantic_scalar,
+   k_data_type_f64  = k_data_format_float  | DATATYPE_SIZE( 1, 8 ) | k_data_semantic_scalar,
+    /* vectors */
+    k_data_type_vec2 = k_data_format_float | DATATYPE_SIZE( 2, 4 ) | k_data_semantic_vector,
+    k_data_type_vec3 = k_data_format_float | DATATYPE_SIZE( 3, 4 ) | k_data_semantic_vector,
+    k_data_type_vec4 = k_data_format_float | DATATYPE_SIZE( 4, 4 ) | k_data_semantic_vector,
+    k_data_type_quat = k_data_format_float | DATATYPE_SIZE( 4, 4 ) | k_data_semantic_rotation,
+
+    /* CO|SCALE|QUAT */
+    k_data_type_transform = k_data_format_float | DATATYPE_SIZE( 10, 4 ) | k_data_semantic_transform,
+    /* matrices */
+    k_data_type_mat2x2 = k_data_format_float | DATATYPE_SIZE( 4, 4 ) | k_data_semantic_transform,
+    k_data_type_mat3x3 = k_data_format_float | DATATYPE_SIZE( 9, 4 ) | k_data_semantic_transform,
+    k_data_type_mat4x3 = k_data_format_float | DATATYPE_SIZE( 12, 4 ) | k_data_semantic_transform,
+    k_data_type_mat4x4 = k_data_format_float | DATATYPE_SIZE( 16, 4 ) | k_data_semantic_transform,
+    /* box */
+    k_data_type_2dbox  = k_data_format_float | DATATYPE_SIZE( 4, 4 ) | k_data_semantic_box,
+    k_data_type_3dbox  = k_data_format_float | DATATYPE_SIZE( 6, 4 ) | k_data_semantic_box,
+
+   k_data_type_function = k_data_format_void
+};