SWITCH TO SDL3 BECAUSE IT ACTUALLY WORKS
authorhgn <hgodden00@gmail.com>
Sun, 19 Oct 2025 22:06:18 +0000 (23:06 +0100)
committerhgn <hgodden00@gmail.com>
Sun, 19 Oct 2025 22:06:18 +0000 (23:06 +0100)
161 files changed:
.gitmodules
async.kv [deleted file]
bootstrap.sh
build.kv [deleted file]
foundation.kv [deleted file]
glfw3.kv [deleted file]
graphics.kv [deleted file]
include/common_api.h [deleted file]
include/common_thread_api.h [deleted file]
include/engine_backend.h [deleted file]
include/engine_interface.h [deleted file]
include/glfw.h [deleted file]
include/graphics_api.h [deleted file]
include/input_api.h [deleted file]
include/maths/common_maths.h [deleted file]
include/maths/rigidbody.h [deleted file]
include/opengl.h [deleted file]
include/types.h [deleted file]
shaders/blit.vs [deleted file]
shaders/blit_blur.fs [deleted file]
shaders/blit_colour.fs [deleted file]
shaders/blit_tex.fs [deleted file]
shaders/debug_lines.fs [deleted file]
shaders/debug_lines.vs [deleted file]
shaders/loader.fs [deleted file]
shaders/motion_vectors_common.glsl [deleted file]
shaders/motion_vectors_fs.glsl [deleted file]
shaders/motion_vectors_vs.glsl [deleted file]
shaders/rigidbody_view.fs [deleted file]
shaders/rigidbody_view.vs [deleted file]
shaders/ui.fs [deleted file]
shaders/ui.vs [deleted file]
shaders/ui_image.fs [deleted file]
shaders/ui_image.vs [deleted file]
shaders/ui_image_grad.fs [deleted file]
shaders/ui_image_hsv.fs [deleted file]
source/async/async.kv [new file with mode: 0644]
source/async/sdl_async.c [new file with mode: 0644]
source/async/vg_async.c [new file with mode: 0644]
source/async/vg_async.h [new file with mode: 0644]
source/console/console_system.c [new file with mode: 0644]
source/console/console_system.h [new file with mode: 0644]
source/console/console_system.kv [new file with mode: 0644]
source/console_core.c [deleted file]
source/console_core.h [deleted file]
source/engine/array_file.c [new file with mode: 0644]
source/engine/array_file.h [new file with mode: 0644]
source/engine/audio_mixer.c
source/engine/console.c
source/engine/engine.kv [new file with mode: 0644]
source/engine/input.c [deleted file]
source/engine/main.c [deleted file]
source/engine/metascene.c [new file with mode: 0644]
source/engine/metascene.h [new file with mode: 0644]
source/engine/model.c [new file with mode: 0644]
source/engine/model.h [new file with mode: 0644]
source/engine/model_compiler.c [new file with mode: 0644]
source/engine/model_compiler.h [new file with mode: 0644]
source/engine/model_entity.h [new file with mode: 0644]
source/engine/profiler.c
source/engine/shader.c [deleted file]
source/engine/shader_props.h [new file with mode: 0644]
source/engine/shaders/blit.vs [new file with mode: 0644]
source/engine/shaders/blit_tex.fs [new file with mode: 0644]
source/engine/shaders/debug_lines.fs [new file with mode: 0644]
source/engine/shaders/debug_lines.vs [new file with mode: 0644]
source/engine/steamworks.c [deleted file]
source/engine/steamworks.c_UNUSED [new file with mode: 0644]
source/engine/texture.c [deleted file]
source/engine/ui.c [deleted file]
source/engine/ui.c_UNUSED [new file with mode: 0644]
source/engine/vg_audio.c [new file with mode: 0644]
source/engine/vg_audio.h [new file with mode: 0644]
source/engine/vg_audio_dsp.c [new file with mode: 0644]
source/engine/vg_audio_dsp.h [new file with mode: 0644]
source/engine/vg_audio_vorbis.c [new file with mode: 0644]
source/engine/vg_audio_vorbis.h [new file with mode: 0644]
source/engine/vg_camera.c [new file with mode: 0644]
source/engine/vg_camera.h [new file with mode: 0644]
source/engine/vg_engine.c [new file with mode: 0644]
source/engine/vg_engine.h [new file with mode: 0644]
source/engine/vg_engine_core.kv [new file with mode: 0644]
source/engine/vg_framebuffer.c
source/engine/vg_framebuffer.h
source/engine/vg_input.c [new file with mode: 0644]
source/engine/vg_input.h [new file with mode: 0644]
source/engine/vg_lines.c [new file with mode: 0644]
source/engine/vg_lines.h [new file with mode: 0644]
source/engine/vg_opengl.h [new file with mode: 0644]
source/engine/vg_render.c [new file with mode: 0644]
source/engine/vg_render.h [new file with mode: 0644]
source/engine/vg_shader.c [new file with mode: 0644]
source/engine/vg_shader.h [new file with mode: 0644]
source/engine/vg_tex.c [new file with mode: 0644]
source/engine/vg_tex.h [new file with mode: 0644]
source/engine/vg_ui.c [new file with mode: 0644]
source/engine/vg_ui.h [new file with mode: 0644]
source/foundation/allocator_heap.c
source/foundation/allocator_pool.c
source/foundation/allocator_queue.c
source/foundation/allocator_stack.c
source/foundation/allocator_stretchy.c
source/foundation/async.c [deleted file]
source/foundation/buffer_operations.c
source/foundation/exit.c
source/foundation/exit_windows.c [new file with mode: 0644]
source/foundation/foundation.h [new file with mode: 0644]
source/foundation/foundation.kv [new file with mode: 0644]
source/foundation/io.c
source/foundation/io_windows.c
source/foundation/keyvalues.c
source/foundation/logging.c
source/foundation/options.c
source/foundation/stream.c
source/foundation/string.c
source/foundation/temporary.c
source/foundation/threads_c11.c [new file with mode: 0644]
source/foundation/threads_sdl.c [new file with mode: 0644]
source/graphics/font.c
source/graphics/graphics.c [deleted file]
source/graphics/graphics.kv [new file with mode: 0644]
source/graphics/graphics_software.c
source/graphics/ui.c
source/graphics/vg_graphics.c [new file with mode: 0644]
source/graphics/vg_graphics.h [new file with mode: 0644]
source/maths/common_maths.c
source/maths/common_maths.h [new file with mode: 0644]
source/maths/maths.kv [new file with mode: 0644]
source/maths/random.c [new file with mode: 0644]
source/maths/random.h [new file with mode: 0644]
source/maths/rigidbody.h [new file with mode: 0644]
source/thread/primative_sdl2.c [deleted file]
source/tools/metacompiler.c
source/types.h [new file with mode: 0644]
source/vg_camera/vg_camera.c [deleted file]
source/vg_camera/vg_camera.h [deleted file]
source/vg_camera/vg_camera.kv [deleted file]
source/vg_lines/debug_lines.fs [deleted file]
source/vg_lines/debug_lines.vs [deleted file]
source/vg_lines/vg_lines.c [deleted file]
source/vg_lines/vg_lines.h [deleted file]
source/vg_lines/vg_lines.kv [deleted file]
source/vg_model/array_file.c [deleted file]
source/vg_model/array_file.h [deleted file]
source/vg_model/compiler.c [deleted file]
source/vg_model/compiler.h [deleted file]
source/vg_model/entity.h [deleted file]
source/vg_model/metascene.c [deleted file]
source/vg_model/metascene.h [deleted file]
source/vg_model/model.c [deleted file]
source/vg_model/model.h [deleted file]
source/vg_model/model.kv [deleted file]
source/vg_model/shader_props.h [deleted file]
source/vg_tex/vg_tex.c [deleted file]
source/vg_tex/vg_tex.h [deleted file]
source/vg_tex/vg_tex.kv [deleted file]
source/vg_water/vg_water.c [deleted file]
source/vg_water/vg_water.h [deleted file]
source/vg_water/vg_water.kv [deleted file]
source/vg_water/water.fs [deleted file]
submodules/SDL [new submodule]

index 8fbe57b9e8ca68ace6fb5b7c072f773b5a188d8c..693ee81b26c8815eff1e4f4fea2feffc6ef7a818 100644 (file)
@@ -19,3 +19,9 @@
 [submodule "submodules/miniaudio"]
        path = submodules/miniaudio
        url = https://github.com/mackron/miniaudio.git
+[submodule "SDL"]
+       path = SDL
+       url = https://github.com/libsdl-org/SDL.git
+[submodule "submodules/SDL"]
+       path = submodules/SDL
+       url = https://github.com/libsdl-org/SDL.git
diff --git a/async.kv b/async.kv
deleted file mode 100644 (file)
index 51f5f87..0000000
--- a/async.kv
+++ /dev/null
@@ -1,7 +0,0 @@
-add source/foundation/async.c
-
-hook
-{
-   event START
-   function _async_init
-}
index b5cadbc93ac7a145d6f5a891356d9ece230d8157..38805e54afbcd8127e7a1d7ac8eb46a55cd627c3 100755 (executable)
@@ -1,7 +1,8 @@
 mkdir -p bin/vg.metacompiler-linux-x86_64-zig-cc/ &&
 taskset -c 0-1 zig cc -I. -I./include/ -fsanitize=address -lasan -Wall -Wno-unused-function -O0 -ggdb \
    -std=c11 -D_DEFAULT_SOURCE \
-   -include "types.h" \
+   -include "source/types.h" \
+   -I./source/foundation/ \
    source/foundation/options.c \
    source/foundation/logging.c \
    source/foundation/allocator_heap.c \
@@ -16,6 +17,7 @@ taskset -c 0-1 zig cc -I. -I./include/ -fsanitize=address -lasan -Wall -Wno-unus
    source/foundation/io.c \
    source/foundation/buffer_operations.c \
    source/foundation/temporary.c \
+   source/foundation/threads_c11.c \
    \
    source/tools/metacompiler.c \
    -o bin/vg.metacompiler-linux-x86_64-zig-cc/vg.metacompiler
diff --git a/build.kv b/build.kv
deleted file mode 100644 (file)
index bfc604d..0000000
--- a/build.kv
+++ /dev/null
@@ -1,2 +0,0 @@
-include include/
-include source/
diff --git a/foundation.kv b/foundation.kv
deleted file mode 100644 (file)
index cd18ebd..0000000
+++ /dev/null
@@ -1,368 +0,0 @@
-event
-{
-   name OPTIONS
-   prototype "void"
-}
-event
-{
-   name START
-   prototype "void"
-}
-
-add source/foundation/options.c
-add source/foundation/logging.c
-{
-   hook OPTIONS
-   function _log_options
-}
-
-add source/foundation/allocator_heap.c
-add source/foundation/allocator_stack.c
-add source/foundation/allocator_pool.c
-add source/foundation/allocator_queue.c
-add source/foundation/allocator_stretchy.c
-add source/foundation/stream.c
-add source/foundation/string.c
-add source/foundation/keyvalues.c
-add source/foundation/buffer_operations.c
-add source/foundation/temporary.c
-
-{
-   if linux
-   add source/foundation/io.c
-   add source/foundation/exit.c
-}
-
-{
-   if windows
-   add source/foundation/io_windows.c
-   add source/foundation/exit_windows.c
-}
-
-{
-   if terminal
-   add source/terminal_main.c
-}
-
-{
-
-   if game_engine
-   include source/engine
-
-   {
-      add source/engine/main.c
-      hook
-      {
-         event OPTIONS
-         function _engine_options
-      }
-
-      event
-      {
-         name ENGINE_NEW_FRAME
-         prototype "void"
-      }
-      event
-      {
-         name ENGINE_FIXED_UPDATE
-         prototype "void"
-      }
-      event
-      {
-         name ENGINE_RENDER
-         prototype "void"
-      }
-      event
-      {
-         name ENGINE_UI
-         prototype "void"
-      }
-      event
-      {
-         name ENGINE_WINDOW_RESIZE
-         prototype "void"
-      }
-   }
-
-   {
-      add source/engine/ui.c
-      hook
-      {
-         event START
-         function _engine_ui_init
-      }
-   }
-
-   {
-      add source/engine/input.c
-      hook
-      {
-         event START
-         function _input_init
-      }
-      hook
-      {
-         event ENGINE_NEW_FRAME
-         function _input_update
-      }
-      ccmd
-      {
-         name bind
-         function _input_bind_ccmd
-         description "Bind device input to a button, action or axis"
-
-         parameter
-         {
-            description "Device input alias"
-         }
-
-         parameter
-         {
-            description "button,action,axis name"
-         }
-      }
-      ccmd
-      {
-         name unbind
-         description "Unbind all bindings that match"
-         function _input_unbind_ccmd
-
-         parameter
-         {
-            description "binding"
-         }
-      }
-   }
-
-   {
-      add source/console_core.c
-      hook
-      {
-         event START
-         function _console_init
-      }
-   }
-
-   {
-      add source/engine/console.c
-      hook
-      {
-         event START
-         function _engine_console_init
-      }
-      hook
-      {
-         event ENGINE_NEW_FRAME
-         function _engine_console_update
-      }
-      hook
-      {
-         event ENGINE_UI
-         function _engine_console_ui
-         affinity 9999
-      }
-   }
-
-   {
-      add source/engine/vg_framebuffer.c
-      hook 
-      {
-         event ENGINE_WINDOW_RESIZE
-         function _framebuffer_resize
-      }
-   }
-
-   add source/engine/shader.c
-   input_layer
-   {
-      name console
-   }
-   input_layer
-   {
-      name ui
-   }
-   input
-   {
-      name console
-      type action
-      layer_mask console
-   }
-
-   append async.kv
-   thread
-   {
-      name main
-      queue_size_m 40
-      flag MAIN
-      flag OPENGL
-   }
-   thread
-   {
-      name async
-      queue_size_m 40
-      flag ASYNC
-   }
-   thread
-   {
-      name audio
-   }
-   thread
-   {
-      name exitor
-      queue_size_m 1
-   }
-
-   append graphics.kv
-   append glfw3.kv
-
-   ccmd
-   {
-      name exec
-      function _console_exec_ccmd
-      description "Execute a configuration file"
-
-      parameter
-      {
-         description "The path to the config"
-      }
-   }
-   config "bind ALT+GRAVE_ACCENT console"
-
-   config "bind BACKSPACE ui_backspace"
-   config "bind DELETE ui_delete"
-   config "bind ENTER ui_enter"
-   config "bind TAB ui_indent"
-   config "bind HOME ui_home"
-   config "bind END ui_end"
-   config "bind LEFT ui_left"
-   config "bind RIGHT ui_right"
-   config "bind UP ui_up"
-   config "bind DOWN ui_down"
-   config "bind MOUSE_LEFT ui_click"
-
-   config "bind LEFT_SHIFT ui_select"
-   config "bind RIGHT_SHIFT ui_select"
-
-   input
-   {
-      name ui_delete
-      type action
-      layer_mask ui
-   }
-   input
-   {
-      name ui_backspace
-      type action
-      layer_mask ui
-   }
-   input
-   {
-      name ui_enter
-      type action
-      layer_mask ui
-   }
-   input
-   {
-      name ui_indent
-      type action
-      layer_mask ui
-   }
-
-   input
-   {
-      name ui_select
-      type button
-      layer_mask ui
-   }
-   input
-   {
-      name ui_home
-      type button
-      layer_mask ui
-   }
-   input
-   {
-      name ui_end
-      type button
-      layer_mask ui
-   }
-   input
-   {
-      name ui_left
-      type button
-      layer_mask ui
-   }
-   input
-   {
-      name ui_right
-      type button
-      layer_mask ui
-   }
-   input
-   {
-      name ui_up
-      type button
-      layer_mask ui
-   }
-   input
-   {
-      name ui_down
-      type button
-      layer_mask ui
-   }
-   input 
-   {
-      name ui_click
-      type action
-      layer_mask ui
-   }
-
-   shader
-   {
-      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
-         }
-      }
-   }
-
-   cvar
-   {
-      name test
-      type u32
-      default 5
-      cheat 1
-      description "This is just a test variable!"
-   }
-
-   hook
-   {
-      event START
-      function "_shaders_compile"
-   }
-}
diff --git a/glfw3.kv b/glfw3.kv
deleted file mode 100644 (file)
index 37acf90..0000000
--- a/glfw3.kv
+++ /dev/null
@@ -1,37 +0,0 @@
-include dep/glfw-3.4/src
-add dep/glfw-3.4/src/init.c
-add dep/glfw-3.4/src/input.c
-add dep/glfw-3.4/src/monitor.c
-add dep/glfw-3.4/src/platform.c
-add dep/glfw-3.4/src/context.c
-add dep/glfw-3.4/src/window.c
-add dep/glfw-3.4/src/vulkan.c
-
-add dep/glfw-3.4/src/null_init.c
-add dep/glfw-3.4/src/null_joystick.c
-add dep/glfw-3.4/src/null_monitor.c
-add dep/glfw-3.4/src/null_window.c
-
-include dep/glad.4.3/
-add dep/glad.4.3/glad.c
-
-{
-   if linux
-   define _GLFW_X11
-   add dep/glfw-3.4/src/posix_thread.c
-   add dep/glfw-3.4/src/posix_poll.c
-   add dep/glfw-3.4/src/posix_time.c
-   add dep/glfw-3.4/src/posix_module.c
-   add dep/glfw-3.4/src/x11_init.c
-   add dep/glfw-3.4/src/x11_monitor.c
-   add dep/glfw-3.4/src/x11_window.c
-   add dep/glfw-3.4/src/linux_joystick.c
-   add dep/glfw-3.4/src/glx_context.c
-   add dep/glfw-3.4/src/egl_context.c
-   add dep/glfw-3.4/src/osmesa_context.c
-   add dep/glfw-3.4/src/xkb_unicode.c
-}
-
-{
-   if windows
-}
diff --git a/graphics.kv b/graphics.kv
deleted file mode 100644 (file)
index f6e2877..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-add source/graphics/font.c
-add source/graphics/ui.c
-add source/graphics/graphics.c
-add source/graphics/graphics_software.c
diff --git a/include/common_api.h b/include/common_api.h
deleted file mode 100644 (file)
index f504ab8..0000000
+++ /dev/null
@@ -1,373 +0,0 @@
-/* Voyager common application interface */
-#pragma once
-
-#define VG_PRE_MAIN \
-   _exit_init(); \
-   _log_init(); \
-   _options_init( argc, argv ); \
-   EVENT_CALL( OPTIONS ); \
-   _options_check_end(); 
-
-#define BYTES_KB( X ) X*1024
-#define BYTES_MB( X ) X*1024*1024
-#define BYTES_GB( X ) X*1024*1024*1024
-#define ARRAY_COUNT( X ) (sizeof((X))/sizeof((X)[0]))
-#define i8_MAX  0x7F
-#define u8_MAX  0XFF
-#define i16_MAX 0x7FFF
-#define u16_MAX 0xFFFF
-#define i32_MAX 0x7FFFFFFF
-#define u32_MAX 0xFFFFFFFF
-#define i64_MAX 0x7FFFFFFFFFFFFFFF
-#define u64_MAX 0xFFFFFFFFFFFFFFFF
-
-#define PAD_TO_4( X )  (((u32)X+0x3) & ~(u32)0x3)
-#define PAD_TO_8( X )  (((u32)X+0x7) & ~(u32)0x7)
-#define PAD_TO_16( X ) (((u32)X+0xf) & ~(u32)0xf)
-
-static inline f32 f32_min( f32 a, f32 b ){ return a < b? a: b; }
-static inline f32 f32_max( f32 a, f32 b ){ return a > b? a: b; }
-static inline i32 i32_min( i32 a, i32 b ){ return a < b? a: b; }
-static inline i32 i32_max( i32 a, i32 b ){ return a > b? a: b; }
-static inline i32 u32_min( u32 a, u32 b ){ return a < b? a: b; }
-static inline i32 u32_max( u32 a, u32 b ){ return a > b? a: b; }
-static inline i16 i16_min( i16 a, i16 b ){ return a < b? a: b; }
-static inline i16 i16_max( i16 a, i16 b ){ return a > b? a: b; }
-static inline f32 f32_clamp( f32 a, f32 min, f32 max ) { return f32_min( max, f32_max( a, min ) ); }
-static inline i16 i16_clamp( i16 a, i16 min, i16 max ){ return i16_min( max, i16_max( a, min ) ); }
-static inline i16 i32_clamp( i32 a, i32 min, i32 max ){ return i32_min( max, i32_max( a, min ) ); }
-static inline f32 f32_sign( f32 a ) { return a < 0.0f? -1.0f: 1.0f; }
-
-void _exit_init(void);
-void _fatal_exit(void);
-void _normal_exit(void);
-#define ASSERT_CRITICAL( CONDITION ) \
-   if( !(CONDITION) ) { $log( $fatal, {"(" $TO_STRING(CONDITION) ")" KRED " == 0" KNRM } ); _fatal_exit(); }
-
-/* Command line options
- * ------------------------------------------------------------------------------------------------------------------ */
-void _options_init( i32 argc, const c8 *argv[] );
-void _options_check_end( void );
-bool _option_flag( c8 c, const c8 *desc );
-const c8 *_option_argument( char c, const c8 *desc );
-bool _option_long( c8 *name, const c8 *desc );
-const c8 *_option_long_argument( char *name, const c8 *desc );
-const c8 *_option(void);
-
-/* Heap allocation
- * ------------------------------------------------------------------------------------------------------------------ */
-void *_heap_allocate( u64 size );
-void *_heap_reallocate( void *buf, u64 size );
-void  _heap_free( void *buf );
-
-void zero_buffer( void *buffer, u32 length );
-
-/* if max_length is 0, the operation runs until a 0 is found in the buffer */
-u32 buffer_djb2( const void *buffer, i32 max_length );
-bool compare_buffers( const void *buffer_a, i32 a_max, const void *buffer_b, i32 b_max );
-i32 buffer_first_index( const void *buffer, u8 match, i32 max_length );
-i32 buffer_last_index( const void *buffer, u8 match, i32 max_length );
-bool buffer_copy( const void *buffer_src, u32 src_length, void *buffer_dest, u32 dest_length );
-
-struct stretchy_allocator
-{
-   i32 count, element_size;
-   void *segments[26];
-};
-void stretchy_init( struct stretchy_allocator *stretchy, u32 element_size );
-void *stretchy_append( struct stretchy_allocator *stretchy );
-void *stretchy_get( struct stretchy_allocator *stretchy, u32 index );
-void stretchy_delete( struct stretchy_allocator *stretchy, u32 index );
-void stretchy_shrink( struct stretchy_allocator *stretchy );
-u32 stretchy_count( struct stretchy_allocator *stretchy );
-void stretchy_free( struct stretchy_allocator *stretchy );
-
-/* Stack
- * ------------------------------------------------------------------------------------------------------------------ */
-struct stack_allocator
-{
-   u32 capacity, offset; /* bytes */
-   void *data;
-
-   void *check_pointer;
-};
-void  stack_init( struct stack_allocator *stack, void *buffer, u32 capacity, const c8 *debug_name );
-void *stack_allocate( struct stack_allocator *stack, u32 size, u32 alignment, const c8 *debug_name );
-void  stack_clear( struct stack_allocator *stack );
-void  stack_extend_last( struct stack_allocator *stack, void *check_pointer, i32 extra_bytes );
-u32   stack_offset( struct stack_allocator *stack, void *pointer );
-void *stack_pointer( struct stack_allocator *stack, u32 offset );
-
-u32  _start_temporary_frame(void);
-void _end_temporary_frame( u32 whence );
-void *_temporary_allocate( u32 bytes, u32 alignment );
-struct stack_allocator *_temporary_stack_allocator(void);
-
-/* Pool
- * ------------------------------------------------------------------------------------------------------------------ */
-struct pool_node
-{
-   u16 l, r, refcount, unused0;
-};
-struct pool_chain
-{
-   u16 head, tail, count, unused0;
-};
-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, bool increment );
-u16  pool_next( struct pool_allocator *pool, u16 pool_id, bool right );
-void pool_switch( struct pool_allocator *pool, struct pool_chain *source, struct pool_chain *dest, u16 which );
-
-/* Queue
- * ------------------------------------------------------------------------------------------------------------------ */
-struct queue_item
-{
-   u32 alloc_size,prev_size;
-   u8 data[];
-};
-struct queue_allocator
-{
-   void *buffer;
-   u32 size;
-   u32 head_offset, tail_offset;
-   u32 allocation_count;
-};
-void  queue_init( struct queue_allocator *queue, void *buffer, u32 buffer_size );
-void *queue_alloc( struct queue_allocator *queue, u32 size );
-void *queue_data( struct queue_allocator *queue, u32 offset );
-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 );
-bool  queue_next( struct queue_allocator *queue, u32 item_id, u32 *out_next );
-bool  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 );
-void  queue_clear( struct queue_allocator *queue );
-
-/* Stream
- * ------------------------------------------------------------------------------------------------------------------ */
-enum stream_flag
-{
-   /* options */
-   k_stream_overflow_error = 0x1000,
-   k_stream_null_terminate = 0x2000,
-
-   /* memory type */
-   k_stream_buffer = 0x10,
-   k_stream_procedural = 0x20,
-   k_stream_stack = 0x40,
-   k_stream_posix = 0x80,
-   k_stream_auto = 0x100,
-
-   /* modes */
-   k_stream_write = 0x1,
-   k_stream_read  = 0x2
-};
-struct stream
-{
-   u32 flags, buffer_length, offset;
-   union
-   {
-      void *posix_stream;
-      u8 *buffer_write;
-      const u8 *buffer_read;
-      u32 (*write_procedure)( struct stream *stream, const void *buffer, u32 length );
-      u32 (*read_procedure)( struct stream *stream, void *buffer, u32 length );
-   };
-
-   union
-   {
-      struct stack_allocator *stack;
-      void *procedure_userdata;
-   };
-};
-
-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 );
-bool 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 );
-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 );
-bool stream_error( struct stream *stream );
-
-/* String (Stream subset)
- * ------------------------------------------------------------------------------------------------------------------ */
-
-const c8 *string_get( struct stream *string );
-void string_clip( struct stream *string, i32 length );
-void string_append( struct stream *string, const c8 *substring, u32 length );
-void string_append_c8( struct stream *string, c8 c );
-void string_append_i64( struct stream *string, i64 value, u64 base );
-void string_append_i64r( struct stream *string, i64 value, u64 base, u32 width, c8 blank_c8acter );
-void string_append_u64( struct stream *string, u64 value, u64 base );
-void string_append_f64( struct stream *string, f64 value, u64 base, u32 decimal_places );
-
-struct v_string_arg
-{
-   const c8 *_string;
-   union
-   {
-      u64 _u64;
-      i64 _i64;
-      f64 _f64;
-   };
-
-   u32 type;
-   u32 base;
-   union
-   {
-   u32 decimals;
-   u32 length;
-   };
-};
-void v_string( struct stream *string, struct v_string_arg *argument_list );
-
-#define k_$end 99
-#define k_$string 0
-#define k_$unsigned 2
-#define k_$signed 3
-#define k_$float 4
-#define k_$errno 5
-
-#define $string( X, ... ) { X, __VA_ARGS__ }
-#define $unsigned( X, ... ) { .type=k_$unsigned, ._u64=X, __VA_ARGS__ }
-#define $signed( X, ... ) { .type=k_$signed, ._i64=X, __VA_ARGS__ }
-#define $float( X, ... ) { .type=k_$float, ._f64=X, __VA_ARGS__ }
-#define $errno( ... ) { .type=k_$errno, __VA_ARGS__ }
-#define $v_string( STRING, ... ) v_string( STRING, (struct v_string_arg[]){ __VA_ARGS__, {.type=k_$end} } )
-
-enum string_parse_result
-{
-   k_string_parse_ok,
-   k_string_parse_eof,
-   k_string_parse_error,
-   k_string_parse_whitespace
-};
-enum string_parse_result string_parse_c8 ( struct stream *string, c8 *c );
-enum string_parse_result string_parse_u64( struct stream *string, u64 *value );
-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
- * ------------------------------------------------------------------------------------------------------------------ */
-
-#define KNRM  "\x1B[0m"
-#define KBLK  "\x1B[30m"
-#define KRED  "\x1B[31m"
-#define KGRN  "\x1B[32m"
-#define KYEL  "\x1B[33m"
-#define KBLU  "\x1B[34m"
-#define KMAG  "\x1B[35m"
-#define KCYN  "\x1B[36m"
-#define KWHT  "\x1B[37m"
-
-#define $low      0x1
-#define $info     0x2
-#define $ok       0x4
-#define $warning  0x8
-#define $error    0x10
-#define $fatal    0x20
-#define $shell    0x40
-#define $raw      0x80
-
-/* One day we will replace this shit with a good pre-processor. */
-#define $TO_STRING( S ) $TO_STRING_1( S )
-#define $TO_STRING_1( S ) #S
-#define $line __FILE__ ":" $TO_STRING(__LINE__)
-#define $log( C, ... ) { $v_string( _log_event( C, $line ), __VA_ARGS__ ); _log_end_event(); }
-
-struct stream *_log_event( u32 type, const c8 *code_location );
-void _log_init(void);
-void _log_end_event(void);
-void _log_add_listener( void (*fn)(const c8 *line, u32 length, u32 type), u32 filter, bool accept_vt_codes );
-
-/* Keyvalues
- * ------------------------------------------------------------------------------------------------------------------ */
-struct keyvalues
-{
-   struct stack_allocator *stack;
-   u32 root_offset;
-   u32 kv_page_offset, kv_page_count;
-};
-void keyvalues_init( struct keyvalues *kvs, struct stack_allocator *stack );
-void keyvalues_parse_stream( struct keyvalues *kvs, u32 root_offset, struct stream *in_stream );
-void keyvalues_write_stream( struct keyvalues *kvs, struct stream *out_stream, u32 node, u32 depth );
-
-bool keyvalues_read_file( struct keyvalues *kvs, const char *path, struct stack_allocator *stack );
-bool keyvalues_write_file( struct keyvalues *kvs, const char *path );
-
-enum keyvalue_type
-{
-   k_keyvalue_type_frame   = 0,
-   k_keyvalue_type_pair    = 1,
-   k_keyvalue_type_unused2 = 2,
-   k_keyvalue_type_unused3 = 3
-};
-u32 keyvalues_type( struct keyvalues *kvs, u32 kv_offset );
-c8 *keyvalues_key( struct keyvalues *kvs, u32 kv_offset, u32 *out_length );
-c8 *keyvalues_value( struct keyvalues *kvs, u32 kv_offset, u32 *out_length );
-
-u32 keyvalues_get( struct keyvalues *kvs, u32 root_offset, const c8 *key, bool relative );
-u32 keyvalues_get_next( struct keyvalues *kvs, u32 kv_offset );
-u32 keyvalues_get_child( struct keyvalues *kvs, u32 root_offset, u32 index );
-
-const c8 *keyvalues_read_string( struct keyvalues *kvs, u32 root_offset, const c8 *key, const c8 *default_value );
-bool keyvalues_read_i32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, i32 *default_values, i32 *out_values, u32 len );
-bool keyvalues_read_u32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, u32 *default_values, u32 *out_values, u32 len );
-bool 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 );
-u32 keyvalues_append_u32s( struct keyvalues *kvs, u32 parent_offset, const c8 *key, u32 *values, u32 len );
-u32 keyvalues_append_f32s( struct keyvalues *kvs, u32 parent_offset, const c8 *key, f32 *values, u32 len );
-
-/* IO */
-struct directory;
-
-enum directory_entry_type
-{
-   k_directory_entry_type_unknown,
-   k_directory_entry_type_file,
-   k_directory_entry_type_dir
-};
-
-enum directory_status
-{
-   k_directory_status_none,
-   k_directory_status_ok,
-   k_directory_status_path_too_long,
-   k_directory_status_invalid_path,
-   k_directory_status_is_file
-};
-
-struct directory *directory_open( const c8 *path, struct stack_allocator *stack );
-enum directory_status directory_status( struct directory *directory );
-const c8 *directory_entry_name( struct directory *directory );
-bool 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;
-   i32 value;
-};
-void index_sort( struct sort_index *indices, u32 indice_count );
diff --git a/include/common_thread_api.h b/include/common_thread_api.h
deleted file mode 100644 (file)
index a149aa6..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#include "generated/threads.h"
-
-struct thread_info
-{
-   const c8 *name;
-   u32 queue_size_m;
-   u32 flags;
-};
-struct thread_info *_get_thread_info( enum thread_id thread );
-
-#define ASYNC_CRITICAL 0x1
-#define ASYNC_NONBLOCKING 0x2
-
-// TODO THIS SHOULD BE PART OF THE METACOMPILER
-#define THREAD_FLAG_MAIN 0x1
-#define THREAD_FLAG_ASYNC 0x2
-#define THREAD_FLAG_OPENGL 0x4
-
-// TODO THIS SHOULD BE PART OF THE METACOMPILER
-#define ASYNC_GROUP_OPENGL 0x1
-#define ASYNC_GROUP_FIRST_LOAD 0x2
-
-void           _set_thread_id( enum thread_id id );
-enum thread_id _get_thread_id(void);
-bool _thread_has_flags( enum thread_id id, u32 flags );
-
-struct task *_task_new( enum thread_id thread, u32 buffer_size, u32 async_flags, const c8 *debug_info );
-void         task_send( struct task *task, void (*fn)( struct task *task ) );
-
-void *task_buffer( struct task *task );
-u32 task_buffer_size( struct task *task );
-
-bool _task_queue_process( bool blocking );
-bool _task_queue_can_fit( enum thread_id thread, u32 bytes );
-
-void _async_init(void);
-
-void _async_push_groups( u16 groups, bool exclusive );
-void _async_pop_groups(void);
-u16 _async_get_groups(void);
-i16 _async_group_count( u16 group );
diff --git a/include/engine_backend.h b/include/engine_backend.h
deleted file mode 100644 (file)
index 7f1621a..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-void _engine_console_ui(void);
-void _engine_console_init(void);
-
-void _engine_ui_init(void);
-void _engine_ui_pre_render(void);
-void _engine_ui_post_render(void);
-void _engine_ui_input_character( u32 codepoint );
diff --git a/include/engine_interface.h b/include/engine_interface.h
deleted file mode 100644 (file)
index a428d99..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-struct _engine
-{
-   bool vsync;
-   f64 framerate_limit;
-   f64 time, time_delta, time_fixed_extrapolate;
-
-   enum quality_profile
-   {
-      k_quality_profile_high = 0,
-      k_quality_profile_low = 1,
-      k_quality_profile_min = 2
-   }
-   quality;
-
-   void *window_handle;
-   i32 w, h;
-
-   i32 native_fbo;
-}
-extern _engine;
-#include "generated/console.h"
diff --git a/include/glfw.h b/include/glfw.h
deleted file mode 100644 (file)
index db755b5..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#define GLFW_INCLUDE_NONE
-#include "vg/dep/glfw-3.4/include/GLFW/glfw3.h"
diff --git a/include/graphics_api.h b/include/graphics_api.h
deleted file mode 100644 (file)
index e66096e..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/* Basic graphics
- * ------------------------------------------------------------------------------------------------------------------ */
-
-enum graphics_mode
-{
-   k_graphics_mode_software,
-   k_graphics_mode_opengl
-};
-struct graphics_target 
-{
-   enum graphics_mode mode;
-   i16 size[2];
-   union
-   {
-      u8 *buffer;
-   };
-};
-
-extern struct graphics_target *_graphics_target;
-
-#pragma pack( push, 1 )
-union colour
-{
-   struct{ u8 r, g, b, a; };
-   u8 channels[4];
-   u32 word;
-};
-#pragma pack(pop)
-
-union colour graphics_lerp_colour( union colour c0, union colour c1, i32 d, i32 t );
-
-enum blending_mode
-{
-   k_blending_mode_mix_alpha,
-   k_blending_mode_set,
-   k_blending_mode_add,
-   k_blending_mode_subtract,
-   k_blending_mode_multiply,
-   k_blending_mode_pop = -1
-};
-
-void _graphics_set_target( struct graphics_target *target );
-void _graphics_viewport( i16 x, i16 y, i16 w, i16 h );
-void _graphics_pixel_unclipped( i16 co[2], union colour colour );
-void _graphics_pixel( i16 co[2], union colour colour );
-void _graphics_push_blendmode( enum blending_mode mode );
-
-void _graphics_fill_rect( i16 rect[4], union colour colour );
-void _graphics_line_rect( i16 rect[4], union colour colour );
-void _graphics_line( i16 p0[2], i16 p1[2], union colour colour );
-void _graphics_line2( i16 p0[2], i16 p1[2], union colour c0, union colour c1 );
-void _graphics_line_triangle( i16 p0[2], i16 p1[2], i16 p2[2], union colour colour );
-void _graphics_fill_triangle( i16 in_p0[2], i16 in_p1[2], i16 in_p2[2], union colour colour );
-void _graphics_glyph( i16 co[2], u32 glyph, i16 scale, union colour colour );
-
-void rect_clip( i16 child[4], i16 parent[4], i16 clipped[4] );
-void rect_copy( i16 a[4], i16 b[4] );
-void rect_pad( i16 rect[4], i16 padding[2] );
-void rect_split( i16 rect[4], u32 vertical, i16 width, i16 gap, i16 out_left[4], i16 out_right[4] );
-void rect_split_ratio( i16 rect[4], u32 vertical, f32 ratio, i16 gap, i16 out_left[4], i16 out_right[4] );
-bool ui_inside_rect( i16 rect[4], i16 co[2] );
-
-/* Font
- * ------------------------------------------------------------------------------------------------------------------ */
-
-struct font_face_descriptor
-{
-   i16 cw, ch, sx, sy, baseline;
-   i16 map[256][2];
-};
-
-struct _font
-{
-   enum font_face
-   {
-      k_font_face_normal=0,
-      k_font_face_larger,
-      k_font_face_title,
-      k_font_face_pop = -1
-   }
-   face_stack[8];
-   u32 face_stack_depth;
-
-   i16 bitmap_size[2];
-   u32 bitmap[];
-}
-extern _font;
-void _font_push_face( enum font_face face );
-void _font_get_kerning( i16 out_kerning[3] );
-void _font_get_glyph_uvwh( u32 glyph, i16 out_uvwh[5] );
-bool _font_decode_bitmap( i16 uv[2] );
-
-/* Immediate mode UI
- * ------------------------------------------------------------------------------------------------------------------ */
-void _ui_set_mouse( i16 x, i16 y );
-void _ui_get_mouse_co( i16 out_co[2] );
-void _ui_input_text( const c8 *text );
-bool _ui_want_mouse( i16 area[4] );
-
-#define UI_PADDING_PX     8
-#define UI_ROW_PADDING_PX 18
-#define UI_HORIZONTAL 0
-#define UI_VERTICAL   1
-#define MOUSE_LEFT  0x1
-#define MOUSE_RIGHT 0x2
-
-#define UI_AUTOFOCUS 0x1
-#define UI_MULTILINE 0x2
-
-bool _ui_update(void);
-void _ui_draw(void);
-
-/* Text */
-enum ui_text_encoding
-{
-   k_ui_text_default,
-   k_ui_text_utf8
-};
-void _ui_set_encoding( enum ui_text_encoding encoding );
-
-enum ui_align
-{
-   k_ui_align_x_left   = 0x1,
-   k_ui_align_x_right  = 0x2,
-   k_ui_align_x_center = 0x4,
-
-   k_ui_align_y_top    = 0x10,
-   k_ui_align_y_bottom = 0x20,
-   k_ui_align_y_center = 0x40,
-   k_ui_align_center   = k_ui_align_x_center|k_ui_align_y_center
-};
-void _ui_text( i16 rect[4], const c8 *text, enum ui_align align, union colour colour );
-
-/* Simple controls
- * -------------------------------------------------  */
-i16 _ui_widget_row_height( i16 row_size );
-void _ui_widget_row( i16 inout_panel[4], i16 out_rect[4], i16 row_size );
-
-/* Button --------------------------------- */
-enum button_action
-{
-   k_button_none = 0,
-   k_button_click = 1,
-   k_button_repeat = 2,
-   k_button_hover  = 3,
-};
-enum button_action _ui_button( i16 rect[4] );
-
-/* Checkbox ------------------------------- */
-enum button_action _ui_checkbox( i16 rect[4], i32 *value );
-
-/* Dropdown ------------------------------- */
-enum dropdown_action
-{
-   k_dropdown_none,
-   k_dropdown_changed
-};
-struct ui_dropdown_option
-{
-   i32 value;
-   const c8 *display_name;
-};
-enum dropdown_action _ui_dropdown( i16 rect[4], struct ui_dropdown_option *options, u32 option_count, i32 *value );
-
-/* Slider ---------------------------------- */
-enum button_action _ui_slider( i16 rect[4], i32 vertical, f32 min, f32 max, f32 *value, f32 *out_t );
-
-/* Textbox --------------------------------- */
-enum textbox_action
-{
-   k_textbox_no_action,
-   k_textbox_enter,
-   k_textbox_input_up,
-   k_textbox_input_down,
-   k_textbox_changed,
-   k_textbox_escape
-};
-enum textbox_action _ui_textbox( i16 rect[4], c8 *text_buffer, u32 text_buffer_length, u32 lines, u32 flags );
diff --git a/include/input_api.h b/include/input_api.h
deleted file mode 100644 (file)
index e4f21dd..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-enum input_type
-{
-   k_input_type_action,
-   k_input_type_button,
-   k_input_type_axis
-};
-
-enum input_device
-{
-   k_input_device_none = 0,
-   k_input_device_keyboard,
-   k_input_device_controller,
-   k_input_device_mouse,
-   k_input_device_modifier,
-};
-
-struct input_info
-{
-   const c8 *name;
-   enum input_type type;
-   u32 layer_mask;
-};
-
-enum input_id;
-
-u8 _input_button_down( enum input_id id, bool allow_repeats );
-u8 _input_button_up( enum input_id id );
-u8 _input_button( enum input_id id );
-
-void _input_layer_whitelist( u32 whitelist );
-bool _input_layer_filter( u32 mask );
-void _input_string( struct stream *stream, enum input_id id );
-
-#include "generated/input.h"
diff --git a/include/maths/common_maths.h b/include/maths/common_maths.h
deleted file mode 100644 (file)
index b8345d3..0000000
+++ /dev/null
@@ -1,1405 +0,0 @@
-#include <math.h>
-#define VG_PIf 3.141592 /* fuck you */
-#define VG_TAUf 6.283 /* fuckk you */
-
-static inline u32 f32_raw_bits( f32 a ) { return *((u32 *)(&a)); }
-static inline bool f32_is_infinite( f32 a ) { return ((f32_raw_bits(a)) & 0x7FFFFFFFU) == 0x7F800000U; }
-static inline bool f32_is_nan( f32 a ) { return !f32_is_infinite(a) && ((f32_raw_bits(a)) & 0x7F800000U) == 0x7F800000U;}
-static inline bool f32_is_number( f32 a ) { return ((f32_raw_bits(a)) & 0x7F800000U) != 0x7F800000U; }
-
-/* Scalars ---------------------------------------------------------------------------------------------------------- */
-
-static inline f32 f32_fractional_part( f32 a ) { return a - floorf( a ); }
-static inline f64 f64_fractional_part( f64 a ) { return a - floor( a ); }
-static inline f32 f32_degrees_to_radians( f32 degrees ) { return degrees * VG_PIf/180.0f; }
-static inline f32 f32_friction( f32 velocity, f32 F )
-{
-   return -f32_sign(velocity) * f32_min( F, fabsf(velocity) );
-}
-static inline f32 f32_lerp( f32 a, f32 b, f32 t ) { return a + t*(b-a); }
-static inline f64 f64_lerp( f64 a, f64 b, f64 t ) { return a + t*(b-a); }
-static inline void vg_slewf( f32 *a, f32 b, f32 speed )
-{
-   f32 d = f32_sign( b-*a ),
-       c = *a + d*speed;
-   *a = f32_min( b*d, c*d ) * d;
-}
-static inline f32 f32_smoothstep( f32 x ) { return x*x*(3.0f - 2.0f*x); }
-
-static inline f32 f32_radian_difference( f32 a, f32 b )
-{
-   f32 d = fmodf(b,VG_TAUf)-fmodf(a,VG_TAUf);
-   if( fabsf(d) > VG_PIf )
-      d = -f32_sign(d) * (VG_TAUf - fabsf(d));
-   return d;
-}
-static inline f32 f32_radian_lerp( f32 a, f32 b, f32 t )
-{
-   f32 d = fmodf( b-a, VG_TAUf ),
-       s = fmodf( 2.0f*d, VG_TAUf ) - d;
-   return a + s*t;
-}
-
-static inline u32 f32_quantize( f32 a, u32 bits, f32 min, f32 max )
-{
-   u32 mask = (0x1 << bits) - 1;
-   return f32_clamp((a - min) * ((f32)mask/(max-min)), 0.0f, mask );
-}
-static inline f32 f32_unquantize( u32 q, u32 bits, f32 min, f32 max )
-{
-   return min + (f32)q * ((max-min) / (f32)((0x1 << bits) - 1));
-}
-/* https://iquilezles.org/articles/functions/ 
- *
- * Use k to control the stretching of the function. Its maximum, which is 1, 
- * happens at exactly x = 1/k. 
- */
-static inline f32 f32_exponential_impulse( f32 x, f32 k )
-{
-    f32 h = k*x;
-    return h*expf(1.0f-h);
-}
-
-/* f32[2] ----------------------------------------------------------------------------------------------------------- */
-static inline void v2_copy( f32 a[2], f32 d[2] )
-{
-   d[0] = a[0]; 
-   d[1] = a[1];
-}
-static inline void v2_add( f32 a[2], f32 b[2], f32 d[2] )
-{
-   d[0] = a[0]+b[0]; 
-   d[1] = a[1]+b[1];
-}
-static inline void v2_sub( f32 a[2], f32 b[2], f32 d[2] )
-{
-   d[0] = a[0]-b[0]; 
-   d[1] = a[1]-b[1];
-}
-static inline void v2_min( f32 a[2], f32 b[2], f32 d[2] )
-{
-   d[0] = f32_min(a[0], b[0]); 
-   d[1] = f32_min(a[1], b[1]);
-}
-static inline void v2_max( f32 a[2], f32 b[2], f32 d[2] )
-{
-   d[0] = f32_max(a[0], b[0]); 
-   d[1] = f32_max(a[1], b[1]);
-}
-static inline f32 v2_dot( f32 a[2], f32 b[2] )
-{
-   return a[0] * b[0] + a[1] * b[1];
-}
-static inline f32 v2_cross( f32 a[2], f32 b[2] )
-{
-   return a[0]*b[1] - a[1]*b[0];
-}
-static inline void v2_abs( f32 a[2], f32 d[2] )
-{
-   d[0] = fabsf( a[0] ); 
-   d[1] = fabsf( a[1] );
-}
-static inline void v2_muls( f32 a[2], f32 s, f32 d[2] )
-{
-   d[0] = a[0]*s; 
-   d[1] = a[1]*s;
-}
-static inline void v2_divs( f32 a[2], f32 s, f32 d[2] )
-{
-   d[0] = a[0]/s; 
-   d[1] = a[1]/s;
-}
-static inline void v2_mul( f32 a[2], f32 b[2], f32 d[2] )
-{
-   d[0] = a[0]*b[0]; 
-   d[1] = a[1]*b[1];
-}
-static inline void v2_div( f32 a[2], f32 b[2], f32 d[2] )
-{
-   d[0] = a[0]/b[0]; 
-   d[1] = a[1]/b[1];
-}
-static inline void v2_muladd( f32 a[2], f32 b[2], f32 s[2], f32 d[2] )
-{
-   d[0] = a[0]+b[0]*s[0]; 
-   d[1] = a[1]+b[1]*s[1];
-}
-static inline void v2_muladds( f32 a[2], f32 b[2], f32 s, f32 d[2] )
-{
-   d[0] = a[0]+b[0]*s; 
-   d[1] = a[1]+b[1]*s;
-}
-static inline f32 v2_length2( f32 a[2] )
-{
-   return a[0]*a[0] + a[1]*a[1];
-}
-static inline f32 v2_length( f32 a[2] )
-{
-   return sqrtf( v2_length2( a ) );
-}
-static inline f32 v2_dist2( f32 a[2], f32 b[2] )
-{
-   f32 delta[2];
-   v2_sub( a, b, delta );
-   return v2_length2( delta );
-}
-static inline f32 v2_dist( f32 a[2], f32 b[2] )
-{
-   return sqrtf( v2_dist2( a, b ) );
-}
-static inline void v2_lerp( f32 a[2], f32 b[2], f32 t, f32 d[2] )
-{
-   d[0] = a[0] + t*(b[0]-a[0]);
-   d[1] = a[1] + t*(b[1]-a[1]);
-}
-static inline void v2_normalize( f32 a[2] )
-{
-   v2_muls( a, 1.0f / v2_length( a ), a );
-}
-static inline void v2_normalize_clamp( f32 a[2] )
-{
-   f32 l2 = v2_length2( a );
-   if( l2 > 1.0f )
-      v2_muls( a, 1.0f/sqrtf(l2), a );
-}
-static inline void v2_floor( f32 a[2], f32 b[2] )
-{
-   b[0] = floorf( a[0] );
-   b[1] = floorf( a[1] );
-}
-static inline void v2_fill( f32 a[2], f32 v )
-{
-   a[0] = v;
-   a[1] = v;
-}
-static inline void v2_copysign( f32 a[2], f32 b[2] )
-{
-   a[0] = copysignf( a[0], b[0] );
-   a[1] = copysignf( a[1], b[1] );
-}
-/* integer variants  */
-static inline void v2i_copy( i32 a[2], i32 b[2] )
-{
-   b[0] = a[0]; 
-   b[1] = a[1];
-}
-static inline int v2i_eq( i32 a[2], i32 b[2] )
-{
-   return ((a[0] == b[0]) && (a[1] == b[1]));
-}
-static inline void v2i_add( i32 a[2], i32 b[2], i32 d[2] )
-{
-   d[0] = a[0]+b[0]; 
-   d[1] = a[1]+b[1];
-}
-static inline void v2i_sub( i32 a[2], i32 b[2], i32 d[2] )
-{
-   d[0] = a[0]-b[0]; 
-   d[1] = a[1]-b[1];
-}
-
-/* f32[3] ----------------------------------------------------------------------------------------------------------- */
-static inline bool v3_is_numbers( f32 a[3] )
-{
-   return f32_is_number( a[0] ) && f32_is_number( a[1] ) && f32_is_number( a[2] );
-}
-static inline void v3_copy( f32 a[3], f32 b[3] )
-{
-   b[0] = a[0]; b[1] = a[1]; b[2] = a[2];
-}
-static inline void v3_add( f32 a[3], f32 b[3], f32 d[3] )
-{
-   d[0] = a[0]+b[0]; 
-   d[1] = a[1]+b[1]; 
-   d[2] = a[2]+b[2];
-}
-static inline void v3i_add( i32 a[3], i32 b[3], i32 d[3] )
-{
-   d[0] = a[0]+b[0]; 
-   d[1] = a[1]+b[1]; 
-   d[2] = a[2]+b[2];
-}
-static inline void v3_sub( f32 a[3], f32 b[3], f32 d[3] )
-{
-   d[0] = a[0]-b[0]; 
-   d[1] = a[1]-b[1]; 
-   d[2] = a[2]-b[2];
-}
-static inline void v3i_sub( i32 a[3], i32 b[3], i32 d[3] )
-{
-   d[0] = a[0]-b[0]; 
-   d[1] = a[1]-b[1]; 
-   d[2] = a[2]-b[2];
-}
-static inline void v3_mul( f32 a[3], f32 b[3], f32 d[3] )
-{
-   d[0] = a[0]*b[0];
-   d[1] = a[1]*b[1];
-   d[2] = a[2]*b[2];
-}
-static inline void v3_div( f32 a[3], f32 b[3], f32 d[3] )
-{
-   d[0] = b[0]!=0.0f? a[0]/b[0]: INFINITY;
-   d[1] = b[1]!=0.0f? a[1]/b[1]: INFINITY;
-   d[2] = b[2]!=0.0f? a[2]/b[2]: INFINITY;
-}
-static inline void v3_muls( f32 a[3], f32 s, f32 d[3] )
-{
-   d[0] = a[0]*s;
-   d[1] = a[1]*s;
-   d[2] = a[2]*s;
-}
-static inline void v3_fill( f32 a[3], f32 v )
-{
-   a[0] = v;
-   a[1] = v;
-   a[2] = v;
-}
-static inline void v4_fill( f32 a[4], f32 v )
-{
-   a[0] = v;
-   a[1] = v;
-   a[2] = v;
-   a[3] = v;
-}
-static inline void v3_divs( f32 a[3], f32 s, f32 d[3] )
-{
-   if( s == 0.0f )
-      v3_fill( d, INFINITY );
-   else
-   {
-      d[0] = a[0]/s; 
-      d[1] = a[1]/s; 
-      d[2] = a[2]/s;
-   }
-}
-static inline void v3_muladds( f32 a[3], f32 b[3], f32 s, f32 d[3] )
-{
-   d[0] = a[0]+b[0]*s; 
-   d[1] = a[1]+b[1]*s; 
-   d[2] = a[2]+b[2]*s;
-}
-static inline void v3_muladd( f32 a[3], f32 b[3], f32 s[3], f32 d[3] )
-{
-   d[0] = a[0]+b[0]*s[0]; 
-   d[1] = a[1]+b[1]*s[1];
-   d[2] = a[2]+b[2]*s[2];
-}
-static inline f32 v3_dot( f32 a[3], f32 b[3] )
-{
-   return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
-}
-static inline void v3_cross( f32 a[3], f32 b[3], f32 dest[3] )
-{
-   f32 d[3];
-   d[0] = a[1]*b[2] - a[2]*b[1];
-   d[1] = a[2]*b[0] - a[0]*b[2];
-   d[2] = a[0]*b[1] - a[1]*b[0];
-   v3_copy( d, dest );
-}
-static inline f32 v3_length2( f32 a[3] )
-{
-   return v3_dot( a, a );
-}
-static inline f32 v3_length( f32 a[3] )
-{
-   return sqrtf( v3_length2( a ) );
-}
-static inline f32 v3_dist2( f32 a[3], f32 b[3] )
-{
-   f32 delta[3];
-   v3_sub( a, b, delta );
-   return v3_length2( delta );
-}
-static inline f32 v3_dist( f32 a[3], f32 b[3] )
-{
-   return sqrtf( v3_dist2( a, b ) );
-}
-static inline void v3_normalize( f32 a[3] )
-{
-   v3_muls( a, 1.f / v3_length( a ), a );
-}
-static inline void v3_lerp( f32 a[3], f32 b[3], f32 t, f32 d[3] )
-{
-   d[0] = a[0] + t*(b[0]-a[0]);
-   d[1] = a[1] + t*(b[1]-a[1]);
-   d[2] = a[2] + t*(b[2]-a[2]);
-}
-static inline void v3_min( f32 a[3], f32 b[3], f32 d[3] )
-{
-   d[0] = f32_min(a[0], b[0]);
-   d[1] = f32_min(a[1], b[1]);
-   d[2] = f32_min(a[2], b[2]);
-}
-static inline void v3_max( f32 a[3], f32 b[3], f32 d[3] )
-{
-   d[0] = f32_max(a[0], b[0]);
-   d[1] = f32_max(a[1], b[1]);
-   d[2] = f32_max(a[2], b[2]);
-}
-static inline f32 v3_min_element( f32 a[3] )
-{
-   return f32_min( f32_min( a[0], a[1] ), a[2] );
-}
-static inline f32 v3_max_element( f32 a[3] )
-{
-   return f32_max( f32_max( a[0], a[1] ), a[2] );
-}
-static inline void v3_floor( f32 a[3], f32 b[3] )
-{
-   b[0] = floorf( a[0] );
-   b[1] = floorf( a[1] );
-   b[2] = floorf( a[2] );
-}
-static inline void v3_ceil( f32 a[3], f32 b[3] )
-{
-   b[0] = ceilf( a[0] );
-   b[1] = ceilf( a[1] );
-   b[2] = ceilf( a[2] );
-}
-static inline void v3_negate( f32 a[3], f32 b[3] )
-{
-   b[0] = -a[0];
-   b[1] = -a[1];
-   b[2] = -a[2];
-}
-static inline void v3_rotate( f32 v[3], f32 angle, f32 axis[3], f32 d[3] ) 
-{
-  f32 v1[3], v2[3], k[3], c, s;
-  c = cosf( angle );
-  s = sinf( angle );
-  v3_copy( axis, k );
-  v3_normalize( k );
-  v3_muls( v, c, v1 );
-  v3_cross( k, v, v2 );
-  v3_muls( v2, s, v2 );
-  v3_add( v1, v2, v1 );
-  v3_muls( k, v3_dot(k, v) * (1.0f - c), v2);
-  v3_add( v1, v2, d );
-}
-static inline void v3_tangent_basis( f32 n[3], f32 out_tx[3], f32 out_ty[3] )
-{
-   /* Compute tangent basis (box2d) */
-   if( fabsf( n[0] ) >= 0.57735027f )
-   {
-      out_tx[0] =  n[1];
-      out_tx[1] = -n[0];
-      out_tx[2] =  0.0f;
-   }
-   else
-   {
-      out_tx[0] =  0.0f;
-      out_tx[1] =  n[2];
-      out_tx[2] = -n[1];
-   }
-   v3_normalize( out_tx );
-   v3_cross( n, out_tx, out_ty );
-}
-static inline void v3_vector_to_angles( f32 v[3], f32 out_angles[3] )
-{
-   f32 yaw = atan2f( v[0], -v[2] ),
-     pitch = atan2f( -v[1], sqrtf( v[0]*v[0] + v[2]*v[2] ) );
-   out_angles[0] = yaw;
-   out_angles[1] = pitch;
-   out_angles[2] = 0.0f;
-}
-static inline void v3_angles_to_vector( f32 angles[3], f32 out_vector[3] )
-{
-   out_vector[0] =  sinf( angles[0] ) * cosf( angles[1] );
-   out_vector[1] = -sinf( angles[1] );
-   out_vector[2] = -cosf( angles[0] ) * cosf( angles[1] );
-}
-
-/* f32[4] ----------------------------------------------------------------------------------------------------------- */
-static inline void v4_copy( f32 a[4], f32 d[4] )
-{
-   d[0] = a[0]; 
-   d[1] = a[1]; 
-   d[2] = a[2]; 
-   d[3] = a[3];
-}
-static inline void v4_add( f32 a[4], f32 b[3], f32 d[4] )
-{
-   d[0] = a[0]+b[0]; 
-   d[1] = a[1]+b[1];
-   d[2] = a[2]+b[2];
-   d[3] = a[3]+b[3];
-}
-static inline void v4_muls( f32 a[4], f32 s, f32 d[4] )
-{
-   d[0] = a[0]*s; 
-   d[1] = a[1]*s;
-   d[2] = a[2]*s;
-   d[3] = a[3]*s;
-}
-static inline void v4_muladds( f32 a[4], f32 b[4], f32 s, f32 d[4] )
-{
-   d[0] = a[0]+b[0]*s; 
-   d[1] = a[1]+b[1]*s;
-   d[2] = a[2]+b[2]*s;
-   d[3] = a[3]+b[3]*s;
-}
-static inline void v4_lerp( f32 a[4], f32 b[4], f32 t, f32 d[4] )
-{
-   d[0] = a[0] + t*(b[0]-a[0]);
-   d[1] = a[1] + t*(b[1]-a[1]);
-   d[2] = a[2] + t*(b[2]-a[2]);
-   d[3] = a[3] + t*(b[3]-a[3]);
-}
-static inline f32 v4_dot( f32 a[4], f32 b[4] )
-{
-   return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3];
-}
-static inline f32 v4_length( f32 a[4] )
-{
-   return sqrtf( v4_dot(a,a) );
-}
-
-/* f32[4] ----------------------------------------------------------------------------------------------------------- */
-static inline void q_identity( f32 q[4] )
-{
-   q[0] = 0.0f; 
-   q[1] = 0.0f; 
-   q[2] = 0.0f; 
-   q[3] = 1.0f;
-}
-static inline void q_axis_angle( f32 q[4], f32 axis[3], f32 angle )
-{
-   f32 a = angle*0.5f,
-       c = cosf(a),
-       s = sinf(a);
-   q[0] = s*axis[0];
-   q[1] = s*axis[1];
-   q[2] = s*axis[2];
-   q[3] = c;
-}
-static inline void q_mul( f32 q[4], f32 q1[4], f32 d[4] )
-{
-   f32 t[4];
-   t[0] = q[3]*q1[0] + q[0]*q1[3] + q[1]*q1[2] - q[2]*q1[1];
-   t[1] = q[3]*q1[1] - q[0]*q1[2] + q[1]*q1[3] + q[2]*q1[0];
-   t[2] = q[3]*q1[2] + q[0]*q1[1] - q[1]*q1[0] + q[2]*q1[3];
-   t[3] = q[3]*q1[3] - q[0]*q1[0] - q[1]*q1[1] - q[2]*q1[2];
-   v4_copy( t, d );
-}
-static inline void q_normalize( f32 q[4] )
-{
-   f32 l2 = v4_dot(q,q);
-   if( l2 < 0.00001f ) 
-      q_identity( q );
-   else 
-   {
-      f32 s = 1.0f/sqrtf(l2);
-      q[0] *= s;
-      q[1] *= s;
-      q[2] *= s;
-      q[3] *= s;
-   }
-}
-static inline void q_inv( f32 q[4], f32 d[4] )
-{
-   f32 s = 1.0f / v4_dot(q,q);
-   d[0] = -q[0]*s;
-   d[1] = -q[1]*s;
-   d[2] = -q[2]*s;
-   d[3] =  q[3]*s;
-}
-static inline void q_nlerp( f32 a[4], f32 b[4], f32 t, f32 d[4] )
-{
-   if( v4_dot(a,b) < 0.0f )
-   {
-      f32 c[4];
-      v4_muls( b, -1.0f, c );
-      v4_lerp( a, c, t, d );
-   }
-   else
-      v4_lerp( a, b, t, d );
-   q_normalize( d );
-}
-static inline void q_m3x3( f32 q[4], f32 d[3][3] )
-{
-   f32 l = v4_length(q),
-       s = l > 0.0f? 2.0f/l: 0.0f,
-      xx = s*q[0]*q[0], xy = s*q[0]*q[1], wx = s*q[3]*q[0],
-      yy = s*q[1]*q[1], yz = s*q[1]*q[2], wy = s*q[3]*q[1],
-      zz = s*q[2]*q[2], xz = s*q[0]*q[2], wz = s*q[3]*q[2];
-   d[0][0] = 1.0f - yy - zz;
-   d[1][1] = 1.0f - xx - zz;
-   d[2][2] = 1.0f - xx - yy;
-   d[0][1] = xy + wz;
-   d[1][2] = yz + wx;
-   d[2][0] = xz + wy;
-   d[1][0] = xy - wz;
-   d[2][1] = yz - wx;
-   d[0][2] = xz - wy;
-}
-static inline void q_mulv( f32 q[4], f32 v[3], f32 d[3] )
-{
-   f32 v1[3], v2[3];
-   v3_muls( q, 2.0f*v3_dot(q,v), v1 );
-   v3_muls( v, q[3]*q[3] - v3_dot(q,q), v2 );
-   v3_add( v1, v2, v1 );
-   v3_cross( q, v, v2 );
-   v3_muls( v2, 2.0f*q[3], v2 );
-   v3_add( v1, v2, d );
-}
-static inline f32 q_dist( f32 q0[4], f32 q1[4] )
-{
-   return acosf( 2.0f * v4_dot(q0,q1) -1.0f );
-}
-static inline void q_copy( f32 a[4], f32 b[4] )
-{
-   b[0] = a[0]; 
-   b[1] = a[1]; 
-   b[2] = a[2]; 
-   b[3] = a[3];
-}
-static inline void m2x2_copy( f32 a[2][2], f32 b[2][2] )
-{
-   v2_copy( a[0], b[0] );
-   v2_copy( a[1], b[1] );
-}
-static inline void m2x2_identity( f32 a[2][2] )
-{
-   m2x2_copy( (f32[2][2]){{1,0},
-                          {0,1}}, a );
-}
-static inline void m2x2_create_rotation( f32 a[2][2], f32 theta )
-{
-   f32 s = sinf( theta ),
-       c = cosf( theta );
-   a[0][0] =  c;
-   a[0][1] = -s;
-   a[1][0] =  s;
-   a[1][1] =  c;
-}
-static inline void m2x2_mulv( f32 m[2][2], f32 v[2], f32 d[2] )
-{
-   f32 res[2];
-   res[0] = m[0][0]*v[0] + m[1][0]*v[1];
-   res[1] = m[0][1]*v[0] + m[1][1]*v[1];
-   v2_copy( res, d );
-}
-
-/* f32[3][3] -------------------------------------------------------------------------------------------------------- */
-static inline void euler_m3x3( f32 angles[3], f32 d[3][3] )
-{
-   f32 cosY = cosf( angles[0] ),
-       sinY = sinf( angles[0] ),
-       cosP = cosf( angles[1] ),
-       sinP = sinf( angles[1] ),
-       cosR = cosf( angles[2] ),
-       sinR = sinf( angles[2] );
-   d[2][0] = -sinY * cosP;
-   d[2][1] =  sinP;
-   d[2][2] =  cosY * cosP;
-   d[0][0] =  cosY * cosR;
-   d[0][1] =  sinR;
-   d[0][2] =  sinY * cosR;
-   v3_cross( d[0], d[2], d[1] );
-}
-static inline void m3x3_q( f32 m[3][3], f32 q[4] )
-{
-   f32 diag, r, rinv;
-   diag = m[0][0] + m[1][1] + m[2][2];
-   if( diag >= 0.0f )
-   {
-      r    = sqrtf( 1.0f + diag );
-      rinv = 0.5f / r;
-      q[0] = rinv * (m[1][2] - m[2][1]);
-      q[1] = rinv * (m[2][0] - m[0][2]);
-      q[2] = rinv * (m[0][1] - m[1][0]);
-      q[3] = r    * 0.5f;
-   } 
-   else if( m[0][0] >= m[1][1] && m[0][0] >= m[2][2] )
-   {
-      r    = sqrtf( 1.0f - m[1][1] - m[2][2] + m[0][0] );
-      rinv = 0.5f / r;
-      q[0] = r    * 0.5f;
-      q[1] = rinv * (m[0][1] + m[1][0]);
-      q[2] = rinv * (m[0][2] + m[2][0]);
-      q[3] = rinv * (m[1][2] - m[2][1]);
-   } 
-   else if( m[1][1] >= m[2][2] )
-   {
-      r    = sqrtf( 1.0f - m[0][0] - m[2][2] + m[1][1] );
-      rinv = 0.5f / r;
-      q[0] = rinv * (m[0][1] + m[1][0]);
-      q[1] = r    * 0.5f;
-      q[2] = rinv * (m[1][2] + m[2][1]);
-      q[3] = rinv * (m[2][0] - m[0][2]);
-   } 
-   else 
-   {
-      r    = sqrtf( 1.0f - m[0][0] - m[1][1] + m[2][2] );
-      rinv = 0.5f / r;
-      q[0] = rinv * (m[0][2] + m[2][0]);
-      q[1] = rinv * (m[1][2] + m[2][1]);
-      q[2] = r    * 0.5f;
-      q[3] = rinv * (m[0][1] - m[1][0]);
-   }
-}
-/* a X b == [b]T a == ...*/
-static inline void m3x3_skew_symetric( f32 a[3][3], f32 v[3] )
-{
-   a[0][0] =  0.0f;
-   a[0][1] =  v[2];
-   a[0][2] = -v[1];
-   a[1][0] = -v[2];
-   a[1][1] =  0.0f;
-   a[1][2] =  v[0];
-   a[2][0] =  v[1];
-   a[2][1] = -v[0];
-   a[2][2] =  0.0f;
-}
-/* aka kronecker product */
-static inline void m3x3_outer_product( f32 out_m[3][3], f32 a[3], f32 b[3] )
-{
-   out_m[0][0] = a[0]*b[0];
-   out_m[0][1] = a[0]*b[1];
-   out_m[0][2] = a[0]*b[2];
-   out_m[1][0] = a[1]*b[0];
-   out_m[1][1] = a[1]*b[1];
-   out_m[1][2] = a[1]*b[2];
-   out_m[2][0] = a[2]*b[0];
-   out_m[2][1] = a[2]*b[1];
-   out_m[2][2] = a[2]*b[2];
-}
-static inline void m3x3_add( f32 a[3][3], f32 b[3][3], f32 d[3][3] )
-{
-   v3_add( a[0], b[0], d[0] );
-   v3_add( a[1], b[1], d[1] );
-   v3_add( a[2], b[2], d[2] );
-}
-static inline void m3x3_sub( f32 a[3][3], f32 b[3][3], f32 d[3][3] )
-{
-   v3_sub( a[0], b[0], d[0] );
-   v3_sub( a[1], b[1], d[1] );
-   v3_sub( a[2], b[2], d[2] );
-}
-static inline void m3x3_copy( f32 a[3][3], f32 b[3][3] )
-{
-   v3_copy( a[0], b[0] );
-   v3_copy( a[1], b[1] );
-   v3_copy( a[2], b[2] );
-}
-static inline void m3x3_identity( f32 a[3][3] )
-{
-   m3x3_copy( (f32[3][3]){{1,0,0},
-                          {0,1,0},
-                          {0,0,1}}, a );
-}
-static inline void m3x3_zero( f32 a[3][3] )
-{
-   m3x3_copy( (f32[3][3]){{0,0,0},
-                          {0,0,0},
-                          {0,0,0}}, a );
-}
-static inline void m3x3_diagonal( f32 out_a[3][3], f32 t )
-{
-   m3x3_identity( out_a );
-   out_a[0][0] = t;
-   out_a[1][1] = t;
-   out_a[2][2] = t;
-}
-static inline void m3x3_set_diagonal_v3( f32 a[3][3], f32 v[3] )
-{
-   a[0][0] = v[0];
-   a[1][1] = v[1];
-   a[2][2] = v[2];
-}
-static inline void m3x3_inv( f32 src[3][3], f32 dest[3][3] )
-{
-   f32 a = src[0][0], b = src[0][1], c = src[0][2],
-       d = src[1][0], e = src[1][1], f = src[1][2],
-       g = src[2][0], h = src[2][1], i = src[2][2],
-     det = 1.f / (+a*(e*i-h*f)
-                  -b*(d*i-f*g)
-                  +c*(d*h-e*g));
-   dest[0][0] =  (e*i-h*f)*det;
-   dest[0][1] = -(b*i-c*h)*det;
-   dest[0][2] =  (b*f-c*e)*det;
-   dest[1][0] = -(d*i-f*g)*det;
-   dest[1][1] =  (a*i-c*g)*det;
-   dest[1][2] = -(a*f-d*c)*det;
-   dest[2][0] =  (d*h-g*e)*det;
-   dest[2][1] = -(a*h-g*b)*det;
-   dest[2][2] =  (a*e-d*b)*det;
-}
-static inline f32 m3x3_det( f32 m[3][3] )
-{
-   return   m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
-          - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])
-          + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
-}
-static inline void m3x3_transpose( f32 src[3][3], f32 dest[3][3] )
-{
-   f32 a = src[0][0], b = src[0][1], c = src[0][2],
-       d = src[1][0], e = src[1][1], f = src[1][2],
-       g = src[2][0], h = src[2][1], i = src[2][2];
-   dest[0][0] = a;
-   dest[0][1] = d;
-   dest[0][2] = g;
-   dest[1][0] = b;
-   dest[1][1] = e;
-   dest[1][2] = h;
-   dest[2][0] = c;
-   dest[2][1] = f;
-   dest[2][2] = i;
-}
-static inline void m3x3_mul( f32 a[3][3], f32 b[3][3], f32 d[3][3] )
-{
-   f32 a00 = a[0][0], a01 = a[0][1], a02 = a[0][2],
-       a10 = a[1][0], a11 = a[1][1], a12 = a[1][2],
-       a20 = a[2][0], a21 = a[2][1], a22 = a[2][2],
-       b00 = b[0][0], b01 = b[0][1], b02 = b[0][2],
-       b10 = b[1][0], b11 = b[1][1], b12 = b[1][2],
-       b20 = b[2][0], b21 = b[2][1], b22 = b[2][2];
-   d[0][0] = a00*b00 + a10*b01 + a20*b02;
-   d[0][1] = a01*b00 + a11*b01 + a21*b02;
-   d[0][2] = a02*b00 + a12*b01 + a22*b02;
-   d[1][0] = a00*b10 + a10*b11 + a20*b12;
-   d[1][1] = a01*b10 + a11*b11 + a21*b12;
-   d[1][2] = a02*b10 + a12*b11 + a22*b12;
-   d[2][0] = a00*b20 + a10*b21 + a20*b22;
-   d[2][1] = a01*b20 + a11*b21 + a21*b22;
-   d[2][2] = a02*b20 + a12*b21 + a22*b22;
-}
-static inline void m3x3_mulv( f32 m[3][3], f32 v[3], f32 d[3] )
-{
-   f32 res[3];
-   res[0] = m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2];
-   res[1] = m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2];
-   res[2] = m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2];
-   v3_copy( res, d );
-}
-static inline void m3x3_projection( f32 d[3][3], f32 left, f32 right, f32 bottom, f32 top )
-{
-   f32 rl, tb;
-   m3x3_zero( d );
-   rl = 1.0f / (right - left);
-   tb = 1.0f / (top   - bottom);
-   d[0][0] = 2.0f * rl;
-   d[1][1] = 2.0f * tb;
-   d[2][2] = 1.0f;
-}
-static inline void m3x3_translate( f32 m[3][3], f32 v[3] )
-{
-   m[2][0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0];
-   m[2][1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1];
-   m[2][2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2];
-}
-static inline void m3x3_scale( f32 m[3][3], f32 v[3] )
-{
-   v3_muls( m[0], v[0], m[0] );
-   v3_muls( m[1], v[1], m[1] );
-   v3_muls( m[2], v[2], m[2] );
-}
-static inline void m3x3_scalef( f32 m[3][3], f32 f )
-{
-   f32 v[3];
-   v3_fill( v, f );
-   m3x3_scale( m, v );
-}
-static inline void m3x3_rotate( f32 m[3][3], f32 angle )
-{
-   f32 m00 = m[0][0], m10 = m[1][0],
-       m01 = m[0][1], m11 = m[1][1],
-       m02 = m[0][2], m12 = m[1][2], c, s;
-   s = sinf( angle );
-   c = cosf( angle );
-   m[0][0] = m00 * c + m10 * s;
-   m[0][1] = m01 * c + m11 * s;
-   m[0][2] = m02 * c + m12 * s;
-   m[1][0] = m00 * -s + m10 * c;
-   m[1][1] = m01 * -s + m11 * c;
-   m[1][2] = m02 * -s + m12 * c;
-}
-
-/* f32[4][3] -------------------------------------------------------------------------------------------------------- */
-static inline void m4x3_to_3x3( f32 a[4][3], f32 b[3][3] )
-{
-   v3_copy( a[0], b[0] );
-   v3_copy( a[1], b[1] );
-   v3_copy( a[2], b[2] );
-}
-static inline void m4x3_invert_affine( f32 a[4][3], f32 b[4][3] )
-{
-   m3x3_transpose( a, b );
-   m3x3_mulv( b, a[3], b[3] );
-   v3_negate( b[3], b[3] );
-}
-static inline void m4x3_invert_full( f32 src[4][3], f32 dst[4][3] )
-{
-  f32 t2, t4, t5,
-      det,
-      a = src[0][0], b = src[0][1], c = src[0][2],
-      e = src[1][0], f = src[1][1], g = src[1][2],
-      i = src[2][0], j = src[2][1], k = src[2][2],
-      m = src[3][0], n = src[3][1], o = src[3][2];
-   t2 = j*o - n*k;
-   t4 = i*o - m*k;
-   t5 = i*n - m*j;
-   
-   dst[0][0] =  f*k - g*j;
-   dst[1][0] =-(e*k - g*i);
-   dst[2][0] =  e*j - f*i;
-   dst[3][0] =-(e*t2 - f*t4 + g*t5);
-   
-   dst[0][1] =-(b*k - c*j);
-   dst[1][1] =  a*k - c*i;
-   dst[2][1] =-(a*j - b*i);
-   dst[3][1] =  a*t2 - b*t4 + c*t5;
-   
-   t2 = f*o - n*g;
-   t4 = e*o - m*g; 
-   t5 = e*n - m*f;
-   
-   dst[0][2] =  b*g - c*f ;
-   dst[1][2] =-(a*g - c*e );
-   dst[2][2] =  a*f - b*e ;
-   dst[3][2] =-(a*t2 - b*t4 + c * t5);
-
-   det = 1.0f / (a * dst[0][0] + b * dst[1][0] + c * dst[2][0]);
-   v3_muls( dst[0], det, dst[0] );
-   v3_muls( dst[1], det, dst[1] );
-   v3_muls( dst[2], det, dst[2] );
-   v3_muls( dst[3], det, dst[3] );
-}
-static inline void m4x3_copy( f32 a[4][3], f32 b[4][3] )
-{
-   v3_copy( a[0], b[0] );
-   v3_copy( a[1], b[1] );
-   v3_copy( a[2], b[2] );
-   v3_copy( a[3], b[3] );
-}
-static inline void m4x3_identity( f32 a[4][3] )
-{
-   m4x3_copy( (f32[4][3]){{1,0,0},
-                          {0,1,0},
-                          {0,0,1},
-                          {0,0,0}}, a );
-}
-static inline void m4x3_mul( f32 a[4][3], f32 b[4][3], f32 d[4][3] )
-{
-   f32 a00 = a[0][0], a01 = a[0][1], a02 = a[0][2],
-       a10 = a[1][0], a11 = a[1][1], a12 = a[1][2],
-       a20 = a[2][0], a21 = a[2][1], a22 = a[2][2],
-       a30 = a[3][0], a31 = a[3][1], a32 = a[3][2],
-       b00 = b[0][0], b01 = b[0][1], b02 = b[0][2],
-       b10 = b[1][0], b11 = b[1][1], b12 = b[1][2],
-       b20 = b[2][0], b21 = b[2][1], b22 = b[2][2],
-       b30 = b[3][0], b31 = b[3][1], b32 = b[3][2];
-   d[0][0] = a00*b00 + a10*b01 + a20*b02;
-   d[0][1] = a01*b00 + a11*b01 + a21*b02;
-   d[0][2] = a02*b00 + a12*b01 + a22*b02;
-   d[1][0] = a00*b10 + a10*b11 + a20*b12;
-   d[1][1] = a01*b10 + a11*b11 + a21*b12;
-   d[1][2] = a02*b10 + a12*b11 + a22*b12;
-   d[2][0] = a00*b20 + a10*b21 + a20*b22;
-   d[2][1] = a01*b20 + a11*b21 + a21*b22;
-   d[2][2] = a02*b20 + a12*b21 + a22*b22;
-   d[3][0] = a00*b30 + a10*b31 + a20*b32 + a30;
-   d[3][1] = a01*b30 + a11*b31 + a21*b32 + a31;
-   d[3][2] = a02*b30 + a12*b31 + a22*b32 + a32;
-}
-static inline void m4x3_mulv( f32 m[4][3], f32 v[3], f32 d[3] )
-{
-   f32 result[3];
-   result[0] = m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2] + m[3][0];
-   result[1] = m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2] + m[3][1];
-   result[2] = m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2] + m[3][2];
-   v3_copy( result, d );
-}
-static inline void m4x3_mulp( f32 m[4][3], f32 p[4], f32 d[4] )
-{
-   f32 o[3];
-   v3_muls( p, p[3], o );
-   m4x3_mulv( m, o, o );
-   m3x3_mulv( m, p, d );
-   d[3] = v3_dot( o, d );
-}
-static inline void m4x3_translate( f32 m[4][3], f32 v[3] )
-{
-   v3_muladds( m[3], m[0], v[0], m[3] );
-   v3_muladds( m[3], m[1], v[1], m[3] );
-   v3_muladds( m[3], m[2], v[2], m[3] );
-}
-static inline void m4x3_rotate_x( f32 m[4][3], f32 angle )
-{
-   f32 t[4][3],
-       c = cosf( angle ),
-       s = sinf( angle );
-   m4x3_identity( t );
-   t[1][1] =  c;
-   t[1][2] =  s;
-   t[2][1] = -s;
-   t[2][2] =  c;
-   m4x3_mul( m, t, m );
-}
-static inline void m4x3_rotate_y( f32 m[4][3], f32 angle )
-{
-   f32 t[4][3],
-       c = cosf( angle ),
-       s = sinf( angle );
-   m4x3_identity( t );
-   t[0][0] =  c;
-   t[0][2] = -s;
-   t[2][0] =  s;
-   t[2][2] =  c;
-   m4x3_mul( m, t, m );
-}
-static inline void m4x3_rotate_z( f32 m[4][3], f32 angle )
-{
-   f32 t[4][3],
-       c = cosf( angle ),
-       s = sinf( angle );
-   m4x3_identity( t );
-   t[0][0] =  c;
-   t[0][1] =  s;
-   t[1][0] = -s;
-   t[1][1] =  c;
-   m4x3_mul( m, t, m );
-}
-static inline void m4x3_expand( f32 m[4][3], f32 d[4][4] )
-{
-   v3_copy( m[0], d[0] );
-   v3_copy( m[1], d[1] );
-   v3_copy( m[2], d[2] );
-   v3_copy( m[3], d[3] );
-   d[0][3] = 0.0f;
-   d[1][3] = 0.0f;
-   d[2][3] = 0.0f;
-   d[3][3] = 1.0f;
-}
-static inline void m4x3_decompose( f32 m[4][3], f32 co[3], f32 q[4], f32 s[3] )
-{
-   v3_copy( m[3], co );
-   s[0] = v3_length(m[0]);
-   s[1] = v3_length(m[1]);
-   s[2] = v3_length(m[2]);
-   f32 rot[3][3];
-   v3_divs( m[0], s[0], rot[0] );
-   v3_divs( m[1], s[1], rot[1] );
-   v3_divs( m[2], s[2], rot[2] );
-   m3x3_q( rot, q );
-}
-static inline void m4x3_expand_aabb_point( f32 m[4][3], f32 box[2][3], f32 point[3] )
-{
-   f32 v[3];
-   m4x3_mulv( m, point, v );
-   v3_min( box[0], v, box[0] );
-   v3_max( box[1], v, box[1] );
-}
-static inline void m4x3_expand_aabb_aabb( f32 m[4][3], f32 boxa[2][3], f32 boxb[2][3] )
-{
-   f32 a[3]; f32 b[3];
-   v3_copy( boxb[0], a );
-   v3_copy( boxb[1], b );
-   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ a[0], a[1], a[2] } );
-   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ a[0], b[1], a[2] } );
-   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ b[0], b[1], a[2] } );
-   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ b[0], a[1], a[2] } );
-   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ a[0], a[1], b[2] } );
-   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ a[0], b[1], b[2] } );
-   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ b[0], b[1], b[2] } );
-   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ b[0], a[1], b[2] } );
-}
-static inline void m4x3_lookat( f32 m[4][3], f32 pos[3], f32 target[3], f32 up[3] )
-{
-   f32 dir[3];
-   v3_sub( target, pos, dir );
-   v3_normalize( dir );
-   v3_copy( dir, m[2] );
-   v3_cross( up, m[2], m[0] );
-   v3_normalize( m[0] );
-   v3_cross( m[2], m[0], m[1] );
-   v3_copy( pos, m[3] );
-}
-
-/* f32[4][4] -------------------------------------------------------------------------------------------------------- */
-static inline void m4x4_projection( f32 m[4][4], f32 angle, f32 ratio, f32 fnear, f32 ffar )
-{
-   f32 scale = tanf( angle * 0.5f * VG_PIf / 180.0f ) * fnear,
-         r = ratio * scale,
-         l = -r,
-         t = scale,
-         b = -t;
-   m[0][0] =  2.0f * fnear / (r - l);
-   m[0][1] =  0.0f;
-   m[0][2] =  0.0f;
-   m[0][3] =  0.0f;
-   m[1][0] =  0.0f;
-   m[1][1] =  2.0f * fnear / (t - b);
-   m[1][2] =  0.0f;
-   m[1][3] =  0.0f;
-   m[2][0] =  (r + l) / (r - l);
-   m[2][1] =  (t + b) / (t - b);
-   m[2][2] = -(ffar + fnear) / (ffar - fnear);
-   m[2][3] = -1.0f;
-   m[3][0] =  0.0f;
-   m[3][1] =  0.0f;
-   m[3][2] = -2.0f * ffar * fnear / (ffar - fnear);
-   m[3][3] =  0.0f;
-} 
-static inline void m4x4_translate( f32 m[4][4], f32 v[3] )
-{
-   v4_muladds( m[3], m[0], v[0], m[3] );
-   v4_muladds( m[3], m[1], v[1], m[3] );
-   v4_muladds( m[3], m[2], v[2], m[3] );
-}
-static inline void m4x4_copy( f32 a[4][4], f32 b[4][4] )
-{
-   v4_copy( a[0], b[0] );
-   v4_copy( a[1], b[1] );
-   v4_copy( a[2], b[2] );
-   v4_copy( a[3], b[3] );
-}
-static inline void m4x4_identity( f32 a[4][4] )
-{
-   m4x4_copy( (f32[4][4]){{1,0,0,0},
-                          {0,1,0,0},
-                          {0,0,1,0},
-                          {0,0,0,1}}, a );
-}
-static inline void m4x4_zero( f32 a[4][4] )
-{
-   m4x4_copy( (f32[4][4]){{0,0,0,0},
-                          {0,0,0,0},
-                          {0,0,0,0},
-                          {0,0,0,0}}, a );
-}
-static inline void m4x4_mul( f32 a[4][4], f32 b[4][4], f32 d[4][4] )
-{
-   f32 a00 = a[0][0], a01 = a[0][1], a02 = a[0][2], a03 = a[0][3],
-       a10 = a[1][0], a11 = a[1][1], a12 = a[1][2], a13 = a[1][3],
-       a20 = a[2][0], a21 = a[2][1], a22 = a[2][2], a23 = a[2][3],
-       a30 = a[3][0], a31 = a[3][1], a32 = a[3][2], a33 = a[3][3],
-       b00 = b[0][0], b01 = b[0][1], b02 = b[0][2], b03 = b[0][3],
-       b10 = b[1][0], b11 = b[1][1], b12 = b[1][2], b13 = b[1][3],
-       b20 = b[2][0], b21 = b[2][1], b22 = b[2][2], b23 = b[2][3],
-       b30 = b[3][0], b31 = b[3][1], b32 = b[3][2], b33 = b[3][3];
-  d[0][0] = a00*b00 + a10*b01 + a20*b02 + a30*b03;
-  d[0][1] = a01*b00 + a11*b01 + a21*b02 + a31*b03;
-  d[0][2] = a02*b00 + a12*b01 + a22*b02 + a32*b03;
-  d[0][3] = a03*b00 + a13*b01 + a23*b02 + a33*b03;
-  d[1][0] = a00*b10 + a10*b11 + a20*b12 + a30*b13;
-  d[1][1] = a01*b10 + a11*b11 + a21*b12 + a31*b13;
-  d[1][2] = a02*b10 + a12*b11 + a22*b12 + a32*b13;
-  d[1][3] = a03*b10 + a13*b11 + a23*b12 + a33*b13;
-  d[2][0] = a00*b20 + a10*b21 + a20*b22 + a30*b23;
-  d[2][1] = a01*b20 + a11*b21 + a21*b22 + a31*b23;
-  d[2][2] = a02*b20 + a12*b21 + a22*b22 + a32*b23;
-  d[2][3] = a03*b20 + a13*b21 + a23*b22 + a33*b23;
-  d[3][0] = a00*b30 + a10*b31 + a20*b32 + a30*b33;
-  d[3][1] = a01*b30 + a11*b31 + a21*b32 + a31*b33;
-  d[3][2] = a02*b30 + a12*b31 + a22*b32 + a32*b33;
-  d[3][3] = a03*b30 + a13*b31 + a23*b32 + a33*b33;
-}
-static inline void m4x4_mulv( f32 m[4][4], f32 v[4], f32 d[4] )
-{
-   f32 res[4];
-   res[0] = m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2] + m[3][0]*v[3];
-   res[1] = m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2] + m[3][1]*v[3];
-   res[2] = m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2] + m[3][2]*v[3];
-   res[3] = m[0][3]*v[0] + m[1][3]*v[1] + m[2][3]*v[2] + m[3][3]*v[3];
-   v4_copy( res, d );
-}
-static inline void m4x4_inv( f32 a[4][4], f32 d[4][4] )
-{
-   f32 a00 = a[0][0], a01 = a[0][1], a02 = a[0][2], a03 = a[0][3],
-       a10 = a[1][0], a11 = a[1][1], a12 = a[1][2], a13 = a[1][3],
-       a20 = a[2][0], a21 = a[2][1], a22 = a[2][2], a23 = a[2][3],
-       a30 = a[3][0], a31 = a[3][1], a32 = a[3][2], a33 = a[3][3],
-       det,
-       t[6];
-
-   t[0] = a22*a33 - a32*a23;
-   t[1] = a21*a33 - a31*a23;
-   t[2] = a21*a32 - a31*a22;
-   t[3] = a20*a33 - a30*a23;
-   t[4] = a20*a32 - a30*a22;
-   t[5] = a20*a31 - a30*a21;
-
-   d[0][0] =  a11*t[0] - a12*t[1] + a13*t[2];
-   d[1][0] =-(a10*t[0] - a12*t[3] + a13*t[4]);
-   d[2][0] =  a10*t[1] - a11*t[3] + a13*t[5];
-   d[3][0] =-(a10*t[2] - a11*t[4] + a12*t[5]);
-
-   d[0][1] =-(a01*t[0] - a02*t[1] + a03*t[2]);
-   d[1][1] =  a00*t[0] - a02*t[3] + a03*t[4];
-   d[2][1] =-(a00*t[1] - a01*t[3] + a03*t[5]);
-   d[3][1] =  a00*t[2] - a01*t[4] + a02*t[5];
-
-   t[0] = a12*a33 - a32*a13;
-   t[1] = a11*a33 - a31*a13;
-   t[2] = a11*a32 - a31*a12;
-   t[3] = a10*a33 - a30*a13;
-   t[4] = a10*a32 - a30*a12;
-   t[5] = a10*a31 - a30*a11;
-
-   d[0][2] =  a01*t[0] - a02*t[1] + a03*t[2];
-   d[1][2] =-(a00*t[0] - a02*t[3] + a03*t[4]);
-   d[2][2] =  a00*t[1] - a01*t[3] + a03*t[5];
-   d[3][2] =-(a00*t[2] - a01*t[4] + a02*t[5]);
-
-   t[0] = a12*a23 - a22*a13;
-   t[1] = a11*a23 - a21*a13;
-   t[2] = a11*a22 - a21*a12;
-   t[3] = a10*a23 - a20*a13;
-   t[4] = a10*a22 - a20*a12;
-   t[5] = a10*a21 - a20*a11;
-
-   d[0][3] =-(a01*t[0] - a02*t[1] + a03*t[2]);
-   d[1][3] =  a00*t[0] - a02*t[3] + a03*t[4];
-   d[2][3] =-(a00*t[1] - a01*t[3] + a03*t[5]);
-   d[3][3] =  a00*t[2] - a01*t[4] + a02*t[5];
-   
-   det = 1.0f / (a00*d[0][0] + a01*d[1][0] + a02*d[2][0] + a03*d[3][0]);
-   v4_muls( d[0], det, d[0] );
-   v4_muls( d[1], det, d[1] );
-   v4_muls( d[2], det, d[2] );
-   v4_muls( d[3], det, d[3] );
-}
-/* 
- * http://www.terathon.com/lengyel/Lengyel-Oblique.pdf 
- */
-static inline void m4x4_clip_projection( f32 mat[4][4], f32 plane[4] )
-{
-   f32 c[4] = 
-   {
-      (f32_sign(plane[0]) + mat[2][0]) / mat[0][0],
-      (f32_sign(plane[1]) + mat[2][1]) / mat[1][1],
-      -1.0f,
-      (1.0f + mat[2][2]) / mat[3][2]
-   };
-   v4_muls( plane, 2.0f / v4_dot(plane,c), c );
-   mat[0][2] = c[0];
-   mat[1][2] = c[1];
-   mat[2][2] = c[2] + 1.0f;
-   mat[3][2] = c[3];
-}
-static inline void m4x4_reset_clipping( f32 mat[4][4], f32 far, f32 near )
-{
-   mat[0][2] = 0.0f; 
-   mat[1][2] = 0.0f; 
-   mat[2][2] = -(far + near) / (far - near); 
-   mat[3][2] = -2.0f * far * near / (far - near); 
-}
-
-/* box -------------------------------------------------------------------------------------------------------------- */
-static inline void box_addpt( f32 a[2][3], f32 pt[3] )
-{
-   v3_min( a[0], pt, a[0] );
-   v3_max( a[1], pt, a[1] );
-}
-static inline void box_concat( f32 a[2][3], f32 b[2][3] )
-{
-   v3_min( a[0], b[0], a[0] );
-   v3_max( a[1], b[1], a[1] );
-}
-static inline void box_copy( f32 a[2][3], f32 b[2][3] )
-{
-   v3_copy( a[0], b[0] );
-   v3_copy( a[1], b[1] );
-}
-static inline bool box_overlap( f32 a[2][3], f32 b[2][3] )
-{
-   return ( a[0][0] <= b[1][0] && a[1][0] >= b[0][0] ) &&
-          ( a[0][1] <= b[1][1] && a[1][1] >= b[0][1] ) &&
-          ( a[0][2] <= b[1][2] && a[1][2] >= b[0][2] );
-}
-static inline bool box_within_pt( f32 box[2][3], f32 pt[3] )
-{
-   return (pt[0] >= box[0][0]) && (pt[1] >= box[0][1]) && (pt[2] >= box[0][2]) &&
-          (pt[0] <= box[1][0]) && (pt[1] <= box[1][1]) && (pt[2] <= box[1][2]);
-}
-static inline bool box_within( f32 greater[2][3], f32 lesser[2][3] )
-{
-   f32 a[3], b[3];
-   v3_sub( lesser[0], greater[0], a );
-   v3_sub( lesser[1], greater[1], b );
-   return (a[0] >= 0.0f) && (a[1] >= 0.0f) && (a[2] >= 0.0f) &&
-          (b[0] <= 0.0f) && (b[1] <= 0.0f) && (b[2] <= 0.0f);
-}
-static inline void box_init_inf( f32 box[2][3] )
-{
-   v3_fill( box[0],  INFINITY );
-   v3_fill( box[1], -INFINITY );
-}
-
-/* planes ----------------------------------------------------------------------------------------------------------- */
-static inline void tri_to_plane( f64 a[3], f64 b[3], f64 c[3], f64 p[4] )
-{
-   f64 edge0[3];
-   f64 edge1[3];
-   f64 l;
-   
-   edge0[0] = b[0] - a[0];
-   edge0[1] = b[1] - a[1];
-   edge0[2] = b[2] - a[2];
-   
-   edge1[0] = c[0] - a[0];
-   edge1[1] = c[1] - a[1];
-   edge1[2] = c[2] - a[2];
-   
-   p[0] = edge0[1] * edge1[2] - edge0[2] * edge1[1];
-   p[1] = edge0[2] * edge1[0] - edge0[0] * edge1[2];
-   p[2] = edge0[0] * edge1[1] - edge0[1] * edge1[0];
-   
-   l = sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
-   p[3] = (p[0] * a[0] + p[1] * a[1] + p[2] * a[2]) / l;
-   
-   p[0] = p[0] / l;
-   p[1] = p[1] / l;
-   p[2] = p[2] / l;
-}
-
-// TODO: What is going on here?
-static inline bool plane_intersect3( f32 a[4], f32 b[4], f32 c[4], f32 p[3] )
-{
-   f32 const epsilon = 1e-6f;
-   f32 x[3];
-   v3_cross( a, b, x );
-   f32 d = v3_dot( x, c );
-   if( (d < epsilon) && (d > -epsilon) ) 
-      return 0;
-
-   f32 v0[3], v1[3], v2[3];
-   v3_cross( b, c, v0 );
-   v3_cross( c, a, v1 );
-   v3_cross( a, b, v2 );
-
-   v3_muls(       v0, a[3], p );
-   v3_muladds( p, v1, b[3], p );
-   v3_muladds( p, v2, c[3], p );
-   v3_divs( p, d, p );
-   return 1;
-}
-static inline bool plane_intersect2( f32 a[4], f32 b[4], f32 p[3], f32 n[3] )
-{
-   f32 const epsilon = 1e-6f;
-   f32 c[3];
-   v3_cross( a, b, c );
-   f32 d = v3_length2( c );
-   if( (d < epsilon) && (d > -epsilon) ) 
-      return 0;
-
-   f32 v0[3], v1[3], vx[3];
-   v3_cross( c, b, v0 );
-   v3_cross( a, c, v1 );
-
-   v3_muls( v0, a[3], vx );
-   v3_muladds( vx, v1, b[3], vx );
-   v3_divs( vx, d, p );
-   v3_copy( c, n );
-   return 1;
-}
-static inline bool plane_segment( f32 plane[4], f32 a[3], f32 b[3], f32 co[3] )
-{
-   f32 d0 = v3_dot( a, plane ) - plane[3],
-       d1 = v3_dot( b, plane ) - plane[3];
-   if( d0*d1 < 0.0f )
-   {
-      f32 tot = 1.0f/( fabsf(d0)+fabsf(d1) );
-      v3_muls( a, fabsf(d1) * tot, co );
-      v3_muladds( co, b, fabsf(d0) * tot, co );
-      return 1;
-   }
-   else return 0;
-}
-static inline f64 plane_polarity( f64 p[4], f64 a[3] )
-{
-   return (a[0] * p[0] + a[1] * p[1] + a[2] * p[2])
-          -(p[0]*p[3] * p[0] + p[1]*p[3] * p[1] + p[2]*p[3] * p[2]);
-}
-static inline f32 ray_plane( f32 plane[4], f32 co[3], f32 dir[3] )
-{
-   f32 d = v3_dot( plane, dir );
-   if( fabsf(d) > 1e-6f )
-   {
-      f32 v0[3];
-      v3_muls( plane, plane[3], v0 );
-      v3_sub( v0, co, v0 );
-      return v3_dot( v0, plane ) / d;
-   }
-   else return INFINITY;
-}
-
-
-
-/* 
-
- Time of collision (out t) of line a0 -> a0+b , vs b0->b1 
-
- a0: line start
- b : line trace direction ( non-normalized )
- b0: test segment start
- b1: test segment end
- bOneSided: One way or double sided colliders
- Returns 1/0 hit
-
-*/
-static inline bool segment_segment_time_2d( f32 a0[2], f32 b[2], f32 b0[2], f32 d[2], f32 *t, bool one_sided )
-{
-       const f32 k_epsilon = 0.00001f;
-       
-       f32 c[2];
-       f32 det, u;
-       
-       /* Interior test */
-       det = v2_cross( b, d );
-       if( det <= 0 && one_sided ) 
-      return 0;
-       
-   v2_sub( b0, a0, c );
-       
-       /* Second edge */
-       u = v2_cross( c, b ) / det;
-       if( u < -k_epsilon || u > 1.0f+k_epsilon ) 
-      return 0;
-       
-       /* First edge */
-       *t = v2_cross( c, d ) / det;
-       if( *t < -k_epsilon || *t > 1.0f+k_epsilon ) 
-      return 0;
-       
-       return 1;
-}
-
-/* 
-
- Positional collision HELPER. Dont use if in a loop, calculate b/d outside 
-
- a0: line start
- a1: line end
- b0: line start 2
- b1: line end 2
- dest: collision location
- bOneSided: One way or double sided colliders
- returns: 1/0 Hit
-
-*/
-static inline bool segment_segment_intersect_2d( f32 a0[2], f32 a1[2], f32 b0[2], f32 b1[2], f32 dest[2], bool one_sided )
-{
-       f32 b[2];
-       f32 d[2];
-       f32 t;
-       
-       /* Create trace vectors */
-       v2_sub( a1, a0, b );
-       v2_sub( b1, b0, d );
-       
-       /* Find time */
-       if( !segment_segment_time_2d( a0, b, b0, d, &t, one_sided ) ) 
-      return 0;
-       
-       /* Calculate position */
-       v2_muls( b, t, dest );
-       v2_add( a0, dest, dest );
-
-       return 1;
-}
-
diff --git a/include/maths/rigidbody.h b/include/maths/rigidbody.h
deleted file mode 100644 (file)
index 10bc05a..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2021-2025 Mt.ZERO Software - All Rights Reserved
- */
-
-#define k_friction         0.4f
-#define k_damp_linear      0.1f               /* scale velocity 1/(1+x) */
-#define k_damp_angular     0.1f               /* scale angular  1/(1+x) */
-#define k_penetration_slop 0.01f
-#define k_rb_inertia_scale 4.0f
-#define k_phys_baumgarte   0.2f
-//#define k_gravity          9.6f
-#define k_rb_density       8.0f
-
-extern f32 k_gravity;
-
-enum rb_shape {
-   k_rb_shape_none    = 0,
-   k_rb_shape_box     = 1,
-   k_rb_shape_sphere  = 2,
-   k_rb_shape_capsule = 3,
-};
-
-typedef struct rigidbody rigidbody;
-typedef struct rb_capsule rb_capsule;
-
-struct rb_capsule
-{
-   f32 h, r; 
-};
-
-struct rigidbody
-{
-   v3f co, v, w;
-   v4f q;
-
-   f32 inv_mass;
-
-   m3x3f iI, iIw; /* inertia model and inverse world tensor */
-   m4x3f to_world, to_local;
-};
-
-VG_API void _vg_rigidbody_register(void);
-
-/* 
- * Initialize rigidbody inverse mass and inertia tensor with some common shapes
- */
-void rb_setbody_capsule( rigidbody *rb, f32 r, f32 h, f32 density, f32 inertia_scale );
-void rb_setbody_box( rigidbody *rb, boxf box, f32 density, f32 inertia_scale );
-void rb_setbody_sphere( rigidbody *rb, f32 r, f32 density, f32 inertia_scale );
-
-/*
- * Update ALL matrices and tensors on rigidbody
- */
-void rb_update_matrices( rigidbody *rb );
-
-/* 
- * Extrapolate rigidbody into a transform based on vg accumulator.
- * Useful for rendering
- */
-void rb_extrapolate( rigidbody *rb, v3f co, v4f q );
-void rb_iter( rigidbody *rb );
-void rb_solve_gyroscopic( rigidbody *rb, m3x3f I, f32 h );
-
-/*
- * Creates relative contact velocity vector
- */
-void rb_rcv( rigidbody *rba, rigidbody *rbb, v3f ra, v3f rb, v3f rv );
-
-/*
- * Apply impulse to object
- */
-void rb_linear_impulse( rigidbody *rb, v3f delta, v3f impulse );
-
-/* 
- * Effectors
- */
-void rb_effect_simple_bouyency( rigidbody *ra, v4f plane, f32 amt, f32 drag );
-void rb_effect_spring_target_vector( rigidbody *rba, v3f ra, v3f rt, f32 spring, f32 dampening, f32 timestep );
-
-
-
-/* TODO: Get rid of this! */
-#define VG_MAX_CONTACTS 256
-
-typedef struct rb_ct rb_ct;
-struct rb_ct
-{
-   rigidbody *rba, *rbb;
-   v3f co, n;
-   v3f t[2];
-   float p, bias, norm_impulse, tangent_impulse[2],
-         normal_mass, tangent_mass[2];
-
-   u32 element_id;
-
-   enum contact_type type;
-}
-extern rb_contact_buffer[VG_MAX_CONTACTS];
-extern int rb_contact_count;
-
-int rb_capsule__sphere( m4x3f mtxA, rb_capsule *ca, v3f coB, f32 rb, rb_ct *buf );
-int rb_capsule__capsule( m4x3f mtxA, rb_capsule *ca, m4x3f mtxB, rb_capsule *cb, rb_ct *buf );
-int rb_capsule__box( m4x3f mtxA, rb_capsule *ca, m4x3f mtxB, m4x3f mtxB_inverse, boxf box, rb_ct *buf );
-int rb_sphere__box( v3f coA, f32 ra, m4x3f mtxB, m4x3f mtxB_inverse, boxf box, rb_ct *buf );
-int rb_sphere__sphere( v3f coA, f32 ra, v3f coB, f32 rb, rb_ct *buf );
-int rb_sphere__triangle( m4x3f mtxA, f32 r, v3f tri[3], rb_ct *buf );
-int rb_capsule__triangle( m4x3f mtxA, rb_capsule *c, v3f tri[3], rb_ct *buf );
-int rb_global_has_space( void );
-rb_ct *rb_global_buffer( void );
-int rb_manifold_apply_filtered( rb_ct *man, int len );
-int rb_box_triangle_sat( v3f extent, v3f center, m4x3f to_local, v3f tri_src[3] );
-
-/*
- * Merge two contacts if they are within radius(r) of eachother
- */
-void rb_manifold_contact_weld( rb_ct *ci, rb_ct *cj, float r );
-void rb_manifold_filter_joint_edges( rb_ct *man, int len, float r );
-
-/*
- * Resolve overlapping pairs
- */
-void rb_manifold_filter_pairs( rb_ct *man, int len, float r );
-
-/* 
- * Remove contacts that are facing away from A
- */
-void rb_manifold_filter_backface( rb_ct *man, int len );
-
-/*
- * Filter out duplicate coplanar results. Good for spheres.
- */
-void rb_manifold_filter_coplanar( rb_ct *man, int len, float w );
-
-void rb_debug_contact( rb_ct *ct );
-void rb_solver_reset(void);
-rb_ct *rb_global_ct(void);
-void rb_prepare_contact( rb_ct *ct, f32 dt );
-void rb_depenetrate( rb_ct *manifold, int len, v3f dt );
-void rb_presolve_contacts( rb_ct *buffer, f32 dt, int len );
-void rb_contact_restitution( rb_ct *ct, float cr );
-void rb_solve_contacts( rb_ct *buf, int len );
-
-
-
-typedef struct rb_constr_pos rb_constr_pos;
-typedef struct rb_constr_swingtwist rb_constr_swingtwist;
-
-struct rb_constr_pos
-{
-   rigidbody *rba, *rbb;
-   v3f lca, lcb;
-};
-
-struct rb_constr_swingtwist
-{
-   rigidbody *rba, *rbb;
-
-   v4f conevx, conevy; /* relative to rba */
-   v3f view_offset,    /* relative to rba */
-       coneva, conevxb;/* relative to rbb */
-
-   int tangent_violation, axis_violation;
-   v3f axis, tangent_axis, tangent_target, axis_target;
-
-   float conet;
-   float tangent_mass, axis_mass;
-
-   f32 conv_tangent, conv_axis;
-};
-
-void rb_debug_position_constraints( rb_constr_pos *buffer, int len );
-void rb_presolve_swingtwist_constraints( rb_constr_swingtwist *buf, int len );
-void rb_debug_swingtwist_constraints( rb_constr_swingtwist *buf, int len );
-   
-/*
- * Solve a list of positional constraints
- */
-void rb_solve_position_constraints( rb_constr_pos *buf, int len );
-void rb_solve_swingtwist_constraints( rb_constr_swingtwist *buf, int len );
-void rb_postsolve_swingtwist_constraints( rb_constr_swingtwist *buf, u32 len );
-void rb_solve_constr_angle( rigidbody *rba, rigidbody *rbb, v3f ra, v3f rb );
-
-/*
- * Correct position constraint drift errors
- * [ 0.0 <= amt <= 1.0 ]: the correction amount
- */
-void rb_correct_position_constraints( rb_constr_pos *buf, int len, f32 amt );
-void rb_correct_swingtwist_constraints( rb_constr_swingtwist *buf, int len, float amt );
diff --git a/include/opengl.h b/include/opengl.h
deleted file mode 100644 (file)
index c59e98d..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-#include "vg/dep/glad.4.3/glad/glad.h"
-#include "generated/shaders.h"
-
-GLuint compile_opengl_subshader( GLint type, const c8 *sources[], u32 source_count, bool critical, const c8 *debug_path );
-bool link_opengl_program( GLuint program, bool critical );
-
-void _shader_bind( enum shader_id id );
diff --git a/include/types.h b/include/types.h
deleted file mode 100644 (file)
index 732f839..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-typedef unsigned        char u8;
-typedef char                 c8;
-typedef unsigned short   int u16;
-typedef unsigned         int u32;
-typedef unsigned long    int u64;
-typedef                 char i8;
-typedef signed   short   int i16;
-typedef signed           int i32;
-typedef signed   long    int i64;
-typedef                float f32;
-typedef               double f64;
-typedef unsigned        char bool;
-#define NULL 0
diff --git a/shaders/blit.vs b/shaders/blit.vs
deleted file mode 100644 (file)
index e19a10f..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-layout (location=0) in vec2 a_co;
-out vec2 aUv;
-
-void main()
-{
-   gl_Position = vec4(a_co*2.0-1.0,0.0,1.0);
-
-   if( uFlip == 1 )
-      aUv = vec2(a_co.x,1.0-a_co.y) * uInverseRatio;
-   else
-      aUv = a_co * uInverseRatio;
-}
diff --git a/shaders/blit_blur.fs b/shaders/blit_blur.fs
deleted file mode 100644 (file)
index 592c83f..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-out vec4 FragColor;
-uniform sampler2D uTexMain;
-uniform sampler2D uTexMotion;
-uniform float uBlurStrength;
-uniform vec2 uOverrideDir;
-uniform vec2 uClampUv;
-
-in vec2 aUv;
-
-vec2 rand_hash22( vec2 p )
-{
-   vec3 p3 = fract(vec3(p.xyx) * 213.8976123);
-   p3 += dot(p3, p3.yzx+19.19);
-   return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y));
-}
-
-void main()
-{
-   vec2 vuv = aUv; 
-
-   vec2 vrand = rand_hash22( vuv ) * 2.0 - vec2(1.0);
-   vec2 vrand1 = rand_hash22( vrand ) * 2.0 - vec2(1.0);
-   
-   vec2 vdir = texture( uTexMotion, vuv ).xy * uBlurStrength + uOverrideDir;
-
-   vec4 vcolour0 = texture( uTexMain, min(vuv + vdir*vrand.x,uClampUv) );
-   vec4 vcolour1 = texture( uTexMain, min(vuv + vdir*vrand.y,uClampUv) );
-   vec4 vcolour2 = texture( uTexMain, min(vuv + vdir*vrand1.x,uClampUv) );
-   vec4 vcolour3 = texture( uTexMain, min(vuv + vdir*vrand1.y,uClampUv) );
-
-   FragColor = ( vcolour0 + vcolour1 + vcolour2 + vcolour3 ) * 0.25;
-}
diff --git a/shaders/blit_colour.fs b/shaders/blit_colour.fs
deleted file mode 100644 (file)
index 7eb290e..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-out vec4 FragColor;
-uniform vec4 uColour;
-
-in vec2 aUv;
-
-void main()
-{
-   FragColor = uColour;
-}
diff --git a/shaders/blit_tex.fs b/shaders/blit_tex.fs
deleted file mode 100644 (file)
index 0d39a1b..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-out vec4 FragColor;
-in vec2 aUv;
-
-void main()
-{
-   FragColor = texture( uTexMain, aUv );
-}
diff --git a/shaders/debug_lines.fs b/shaders/debug_lines.fs
deleted file mode 100644 (file)
index 1fcc7ad..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-out vec4 FragColor;
-
-in vec4 s_colour;
-
-void main()
-{
-   FragColor = s_colour;
-}
diff --git a/shaders/debug_lines.vs b/shaders/debug_lines.vs
deleted file mode 100644 (file)
index 2fa61e0..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-uniform mat4 uPv;
-layout (location=0) in vec3 a_co;
-layout (location=1) in vec4 a_colour;
-
-out vec4 s_colour;
-
-void main()
-{
-   vec4 vert_pos = uPv * vec4( a_co, 1.0 );
-   s_colour = a_colour;
-   gl_Position = vert_pos;
-}
diff --git a/shaders/loader.fs b/shaders/loader.fs
deleted file mode 100644 (file)
index dc265dc..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-out vec4 FragColor;
-uniform float uTime;
-uniform float uRatio;
-uniform float uOpacity;
-in vec2 aUv;
-
-float eval_zero( vec2 uv )
-{
-   vec4 vsines = sin( (uTime+uv.y*80.0) * vec4(1.1,2.0234,3.73,2.444) );
-   float gradient = min( uv.y, 0.0 );
-   float offset = vsines.x*vsines.y*vsines.z*vsines.w*gradient;
-
-   vec2 vpos = uv + vec2( offset, 0.0 );
-   float dist = dot( vpos, vpos );
-
-   float fring = step(0.1*0.1,dist) * step(dist,0.15*0.15);
-   return max( 0.0, fring * 1.0+gradient*6.0 );
-}
-
-void main()
-{
-   vec3 col = 0.5+0.5*sin( uTime + aUv.xyx + vec3(0.0,2.0,4.0) );
-   
-   vec2 uvx = aUv - vec2( 0.5 );
-   uvx.x *= uRatio;
-   uvx.y *= 0.75;
-
-   float zero = eval_zero( uvx );
-
-   float dither=fract(dot(vec2(171.0,231.0),gl_FragCoord.xy)/71.0)-0.5;
-   float fmt1 = step( 0.5, zero*zero + dither )*0.8+0.2;
-
-   FragColor = vec4(vec3(fmt1),uOpacity);
-}
diff --git a/shaders/motion_vectors_common.glsl b/shaders/motion_vectors_common.glsl
deleted file mode 100644 (file)
index 7e6c114..0000000
+++ /dev/null
@@ -1 +0,0 @@
-const float k_motion_lerp_amount = 0.01;
diff --git a/shaders/motion_vectors_fs.glsl b/shaders/motion_vectors_fs.glsl
deleted file mode 100644 (file)
index b92ae2d..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#include "../vg/shaders/motion_vectors_common.glsl"
-
-layout (location = 1) out vec4 oMotionVec;
-
-in vec3 aMotionVec0;
-in vec3 aMotionVec1;
-
-void compute_motion_vectors()
-{
-   // Write motion vectors
-   vec2 vmotion0 = aMotionVec0.xy / aMotionVec0.z;
-   vec2 vmotion1 = aMotionVec1.xy / aMotionVec1.z;
-   vec2 vmotion  = (vmotion1-vmotion0) * (1.0/k_motion_lerp_amount);
-
-   oMotionVec = vec4( vmotion, 0.0, 1.0 );
-}
diff --git a/shaders/motion_vectors_vs.glsl b/shaders/motion_vectors_vs.glsl
deleted file mode 100644 (file)
index 4eee0a6..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#include "../vg/shaders/motion_vectors_common.glsl"
-
-out vec3 aMotionVec0;
-out vec3 aMotionVec1;
-
-void vs_motion_out( vec4 vproj0, vec4 vproj1 )
-{
-   // This magically solves some artifacting errors!
-   //
-   vproj1 = vproj0*(1.0-k_motion_lerp_amount) + vproj1*k_motion_lerp_amount;
-
-   aMotionVec0 = vec3( vproj0.xy, vproj0.w );
-   aMotionVec1 = vec3( vproj1.xy, vproj1.w );
-}
diff --git a/shaders/rigidbody_view.fs b/shaders/rigidbody_view.fs
deleted file mode 100644 (file)
index a47829b..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-out vec4 FragColor;
-uniform vec4 uColour;
-
-in vec3 aNorm;
-in vec3 aCo;
-// The MIT License
-// Copyright Â© 2017 Inigo Quilez
-// Permission is hereby granted, free of charge, to any person obtaining a 
-// copy of this software and associated documentation files (the Software),
-// to deal in the Software without restriction, including without limitation
-// the rights to use, copy, modify, merge, publish, distribute, sublicense, 
-// and/or sell copies of the Software, and to permit persons to whom the 
-// Software is furnished to do so, subject to the following conditions: 
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software. THE SOFTWARE IS 
-// PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
-// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 
-// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
-// DEALINGS IN THE SOFTWARE.
-
-// Info: https://iquilezles.org/articles/filterableprocedurals
-//  
-// More filtered patterns:  https://www.shadertoy.com/playlist/l3KXR1
-
-vec3 tri( in vec3 x )
-{
-   return 1.0-abs(2.0*fract(x/2.0)-1.0);
-}
-
-float checkersTextureGrad( in vec3 p, in vec3 ddx, in vec3 ddy )
-{
-  vec3 w = max(abs(ddx), abs(ddy)) + 0.0001; // filter kernel
-  vec3 i = (tri(p+0.5*w)-tri(p-0.5*w))/w;    // analytical integral (box filter)
-  return 0.5 - 0.5*i.x*i.y*i.z;              // xor pattern
-}
-
-void main()
-{
-   vec3 uvw = aCo;
-   vec3 ddx_uvw = dFdx( uvw );
-   vec3 ddy_uvw = dFdy( uvw );
-   float diffuse = checkersTextureGrad( uvw, ddx_uvw, ddy_uvw )*0.5+0.4;
-   float light = dot( vec3(0.8017,0.5345,-0.2672), aNorm )*0.5 + 0.5;
-   FragColor = light * diffuse * uColour;
-}
diff --git a/shaders/rigidbody_view.vs b/shaders/rigidbody_view.vs
deleted file mode 100644 (file)
index 08fc744..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-uniform mat4 uPv;
-uniform mat4x3 uMdl;
-uniform mat4x3 uMdl1;
-layout (location=0) in vec4 a_co;
-layout (location=1) in vec3 a_norm;
-out vec3 aNorm;
-out vec3 aCo;
-
-void main()
-{
-   vec3 world_pos0 = uMdl  * vec4( a_co.xyz, 1.0 );
-   vec3 world_pos1 = uMdl1 * vec4( a_co.xyz, 1.0 );
-   vec3 co = mix( world_pos0, world_pos1, a_co.w );
-   vec4 vert_pos = uPv * vec4( co, 1.0 );
-
-   gl_Position = vert_pos;
-   vec3 l = vec3(length(uMdl[0]),length(uMdl[1]),length(uMdl[2]));
-   aNorm = (mat3(uMdl) * a_norm)/l;
-   aCo = a_co.xyz*l;
-}
diff --git a/shaders/ui.fs b/shaders/ui.fs
deleted file mode 100644 (file)
index bf8525d..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-uniform sampler2D uTexGlyphs;
-uniform sampler2D uTexBG;
-uniform vec4 uColour;
-uniform float uSpread;
-out vec4 FragColor;
-
-in vec4 aTexCoords;
-in vec4 aColour;
-
-vec2 rand_hash22( vec2 p )
-{
-  vec3 p3 = fract(vec3(p.xyx) * 213.8976123);
-  p3 += dot(p3, p3.yzx+19.19);
-  return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y));
-}
-
-void main()
-{
-  vec4 diffuse = aColour;
-  
-  vec4 avg = vec4(0.0);
-
-  if( aColour.a == 0.0 )
-  {
-     avg = aColour;
-       avg.a = texture( uTexGlyphs, aTexCoords.xy ).r;
-  }
-  else
-  {
-     if( uSpread > 0.0001 )
-     {
-        for( int i=0; i<4; i ++ )
-        {
-           vec2 spread = rand_hash22(aTexCoords.zw+vec2(float(i)));
-           avg += texture( uTexBG, aTexCoords.zw + (spread-0.5)*uSpread );
-        }
-        avg *= 0.25;
-        avg.a = 1.0;
-        avg.rgb = mix( avg.rgb, aColour.rgb, aColour.a );
-     }
-     else
-     {
-        avg = aColour;
-     }
-  }
-
-  FragColor = avg * uColour;
-}
diff --git a/shaders/ui.vs b/shaders/ui.vs
deleted file mode 100644 (file)
index 37f8b3b..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-layout (location=0) in vec2 a_co;
-layout (location=1) in vec2 a_uv;
-layout (location=2) in vec4 a_colour;
-uniform mat3 uPv;
-uniform vec2 uBGInverseRatio;
-uniform vec2 uInverseFontSheet;
-
-out vec4 aTexCoords;
-out vec4 aColour;
-
-void main()
-{
-   vec4 proj_pos = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );
-       gl_Position = proj_pos;
-       aTexCoords = vec4( a_uv * uInverseFontSheet, (proj_pos.xy*0.5+0.5) * uBGInverseRatio );
-       aColour = a_colour;
-}
diff --git a/shaders/ui_image.fs b/shaders/ui_image.fs
deleted file mode 100644 (file)
index 5d45c1d..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-uniform sampler2D uTexImage;
-uniform vec4 uColour;
-out vec4 FragColor;
-
-in vec2 aTexCoords;
-in vec4 aColour;
-in vec2 aWsp;
-
-void main()
-{
-       vec4 colour = texture( uTexImage, aTexCoords );
-       FragColor = colour * aColour * uColour;
-}
diff --git a/shaders/ui_image.vs b/shaders/ui_image.vs
deleted file mode 100644 (file)
index 47f1704..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-layout (location=0) in vec2 a_co;
-layout (location=1) in vec2 a_uv;
-layout (location=2) in vec4 a_colour;
-uniform mat3 uPv;
-
-out vec2 aTexCoords;
-out vec4 aColour;
-out vec2 aWsp;
-
-void main()
-{
-       gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );
-       aTexCoords = a_uv * 0.00390625;
-       aColour = a_colour;
-       
-       aWsp = a_co;
-}
diff --git a/shaders/ui_image_grad.fs b/shaders/ui_image_grad.fs
deleted file mode 100644 (file)
index 3201908..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-uniform sampler2D uTexImage;
-uniform vec4 uColour;
-uniform int uLog;
-uniform float uScale;
-
-out vec4 FragColor;
-
-in vec2 aTexCoords;
-in vec4 aColour;
-in vec2 aWsp;
-
-void main()
-{
-       vec4 colour = texture( uTexImage, aTexCoords );
-   float v = colour.r;
-   if( uLog == 1 )
-   {
-      v = log(v);
-   }
-   v *= uScale;
-       FragColor = vec4(vec3(v)*uColour.rgb,1.0);
-}
diff --git a/shaders/ui_image_hsv.fs b/shaders/ui_image_hsv.fs
deleted file mode 100644 (file)
index e98838c..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-uniform float uHue;
-out vec4 FragColor;
-
-in vec2 aTexCoords;
-in vec4 aColour;
-in vec2 aWsp;
-
-void main()
-{
-   vec3 c = vec3( uHue, aTexCoords );
-   vec4 K = vec4(1.0,2.0/3.0,1.0/3.0,3.0);
-   vec3 p = abs(fract(c.xxx+K.xyz)*6.0 - K.www);
-   vec3 colour = c.z*mix(K.xxx,clamp(p-K.xxx,0.0,1.0),c.y);
-       FragColor = vec4( colour, 1.0 );
-}
diff --git a/source/async/async.kv b/source/async/async.kv
new file mode 100644 (file)
index 0000000..dfab494
--- /dev/null
@@ -0,0 +1,13 @@
+include ""
+
+{
+   if SDL
+   add sdl_async.c
+}
+
+hook
+{
+   affinity -20000
+   event START
+   function _async_init
+}
diff --git a/source/async/sdl_async.c b/source/async/sdl_async.c
new file mode 100644 (file)
index 0000000..c1aa445
--- /dev/null
@@ -0,0 +1,295 @@
+#include "foundation.h"
+#include "vg_async.h"
+#include "generated/threads.c"
+#include "SDL3/SDL.h"
+
+#define ASYNC_DEBUG_GROUP_COUNTS
+
+struct thread_context
+{
+   u16 async_groups[ 8 ];
+   u32 async_group_depth;
+}
+_thread_contexts[ k_thread_count ];
+
+struct thread_info *_get_thread_info( enum thread_id thread )
+{
+   ASSERT_CRITICAL( thread < k_thread_count );
+   return &_thread_infos[ thread ];
+}
+
+b8 _thread_has_flags( enum thread_id thread, u32 flags )
+{
+   return (_get_thread_info( thread )->flags & flags) == flags;
+}
+
+void _async_push_groups( u16 groups, b8 exclusive )
+{
+   struct thread_context *context = &_thread_contexts[ _get_thread_id() ];
+
+   ASSERT_CRITICAL( context->async_group_depth < ARRAY_COUNT( context->async_groups ) );
+   context->async_group_depth ++;
+   if( !exclusive )
+      groups |= context->async_groups[ context->async_group_depth-1 ];
+   context->async_groups[ context->async_group_depth ] = groups;
+}
+
+void _async_pop_groups(void)
+{
+   struct thread_context *context = &_thread_contexts[ _get_thread_id() ];
+   ASSERT_CRITICAL( context->async_group_depth );
+   context->async_group_depth --;
+}
+
+u16 _async_get_groups(void)
+{
+   struct thread_context *context = &_thread_contexts[ _get_thread_id() ];
+   return context->async_groups[ context->async_group_depth ];
+}
+
+struct 
+{
+   i16 group_counts[ 16 ];
+   SDL_Mutex *count_lock;
+}
+_async;
+
+struct task_queue
+{
+   u32 count;
+
+   SDL_Condition *blocking_signal, *work_signal;
+   SDL_Mutex *lock, *data_lock;
+   struct queue_allocator queue;
+
+   struct task *allocating_task;
+}
+static _task_queues[ k_thread_count ];
+
+struct task
+{
+   const c8 *alloc_debug_info;
+
+   union
+   {
+      void (*fn)( struct task *task );
+      enum thread_id target_thread;
+   };
+
+   u32 buffer_size;
+   u16 groups, unused0;
+
+   union
+   {
+      u64 _force_8byte_align[];
+      u8 buffer[];
+   };
+};
+
+_Thread_local enum thread_id _thread_id;
+
+void _set_thread_id( enum thread_id id )
+{
+   _thread_id = id;
+}
+
+enum thread_id _get_thread_id(void)
+{
+   return _thread_id;
+}
+void _async_init( void )
+{
+   $log( $info, {"[INIT] _async_init"} );
+   _async.count_lock = SDL_CreateMutex();
+   ASSERT_CRITICAL( _async.count_lock );
+
+   for( u32 i=0; i<k_thread_count; i ++ )
+   {
+      struct thread_info *thread_info = _get_thread_info( i );
+
+      if( thread_info->queue_size_m )
+      {
+         struct task_queue *queue = &_task_queues[ i ];
+         queue->lock = SDL_CreateMutex();
+         queue->data_lock = SDL_CreateMutex();
+         queue->blocking_signal = SDL_CreateCondition();
+         queue->work_signal = SDL_CreateCondition();
+         ASSERT_CRITICAL( queue->lock && queue->data_lock && queue->blocking_signal && queue->work_signal );
+
+         u32 bytes = BYTES_MB(thread_info->queue_size_m);
+         queue->queue.buffer = _heap_allocate( bytes );
+         queue->queue.size = bytes;
+      }
+   }
+}
+
+static void _async_group_increment( u16 groups, i16 dir, const c8 *who )
+{
+   if( !groups )
+      return;
+
+   SDL_LockMutex( _async.count_lock );
+   for( u16 i=0; i<16; i ++ )
+   {
+      if( (groups >> i) & 0x1 )
+      {
+         _async.group_counts[i] += dir;
+         ASSERT_CRITICAL( _async.group_counts[i] >= 0 );
+         ASSERT_CRITICAL( _async.group_counts[i] <= 2048 );
+
+#if defined( ASYNC_DEBUG_GROUP_COUNTS )
+         $log( $warning, {"The task count for group "}, $unsigned(i), {" has "}, {dir>0? "increased": "decreased"}, 
+                         {" to "}, $unsigned(_async.group_counts[i]), {" ("}, {who}, {")"} );
+#endif
+      }
+   }
+   SDL_UnlockMutex( _async.count_lock );
+}
+
+i16 _async_group_count( u16 group )
+{
+   ASSERT_CRITICAL( group );
+   u32 index = __builtin_ctz( (u32)group );
+
+   SDL_LockMutex( _async.count_lock );
+   i16 count = _async.group_counts[ index ];
+   SDL_UnlockMutex ( _async.count_lock );
+   return count;
+}
+
+static struct task_queue *_get_thread_task_queue( enum thread_id thread )
+{
+   ASSERT_CRITICAL( thread < k_thread_count );
+   return &_task_queues[ thread ];
+}
+
+b8 _task_queue_can_fit( enum thread_id thread, u32 bytes )
+{
+   struct task_queue *queue = _get_thread_task_queue( thread );
+   u32 total_size = sizeof(struct task) + bytes;
+   return total_size <= queue->queue.size;
+}
+
+struct task *_task_new( enum thread_id target_thread, u32 buffer_size, u32 async_flags, const c8 *debug_info )
+{
+   ASSERT_CRITICAL( target_thread != _get_thread_id() );
+
+   struct task_queue *queue = _get_thread_task_queue( target_thread );
+   struct queue_allocator *ring = &queue->queue;
+   u32 total_size = sizeof(struct task) + buffer_size;
+   ASSERT_CRITICAL( total_size <= queue->queue.size );
+
+   SDL_LockMutex( queue->data_lock );
+   if( queue->allocating_task )
+   {
+      $log( $fatal, {"Overlapping async allocations. \n"
+                     "  Previous allocation began at: "}, {queue->allocating_task->alloc_debug_info}, {"\n"
+                     "  Overlapping call at: "}, {debug_info} );
+      _fatal_exit();
+   }
+   SDL_LockMutex( queue->lock );
+
+   struct task *task = queue_alloc( ring, total_size );
+   while( ((async_flags & ASYNC_CRITICAL) || !(async_flags & ASYNC_NONBLOCKING)) && !task )
+   {
+      if( async_flags & ASYNC_CRITICAL )
+      {
+         $log( $fatal, { "Too many tasks allocated on this queue, so we cant make this (critical allocation)"} );
+         _fatal_exit();
+      }
+
+      SDL_WaitCondition( queue->blocking_signal, queue->lock );
+      task = queue_alloc( ring, total_size );
+   }
+
+   if( task )
+   {
+      queue->allocating_task = task;
+      task->target_thread = target_thread;
+      task->alloc_debug_info = debug_info;
+      task->buffer_size = buffer_size;
+      task->groups = _async_get_groups();
+      _async_group_increment( task->groups, +1, task->alloc_debug_info );
+
+      SDL_UnlockMutex( queue->lock );
+      return task;
+   }
+   else
+   {
+      SDL_UnlockMutex( queue->lock );
+      return NULL;
+   }
+}
+
+void *task_buffer( struct task *task )
+{
+   return task->buffer;
+}
+
+u32 task_buffer_size( struct task *task )
+{
+   return task->buffer_size;
+}
+
+void task_send( struct task *task, void (*fn)( struct task *task ) )
+{
+   struct task_queue *queue = _get_thread_task_queue( task->target_thread );
+
+   ASSERT_CRITICAL( task );
+   ASSERT_CRITICAL( queue->allocating_task == task );
+
+   if( fn ) task->fn = fn;
+   else     _async_group_increment( task->groups, -1, task->alloc_debug_info );
+   queue->allocating_task = NULL;
+
+   SDL_LockMutex( queue->lock );
+   queue->count ++;
+   SDL_UnlockMutex( queue->lock );
+   SDL_UnlockMutex( queue->data_lock );
+   SDL_SignalCondition( queue->work_signal );
+}
+
+b8 _task_queue_process( b8 blocking )
+{
+   struct task_queue *queue = _get_thread_task_queue( _get_thread_id() );
+   SDL_LockMutex( queue->lock );
+
+   if( blocking )
+   {
+      while( queue->count == 0 )
+         SDL_WaitCondition( queue->work_signal, queue->lock );
+   }
+   else
+   {
+      if( queue->count == 0)
+      {
+         SDL_UnlockMutex( queue->lock );
+         return 0;
+      }
+   }
+
+   struct task *task = queue_tail_data( &queue->queue );
+   SDL_UnlockMutex( queue->lock );
+
+   if( task )
+   {
+      /* task fn can be NULL if it was cancelled (so this is a NOP). Makes code easier if we do this instead of 
+       * reverting the queue to cancel. */
+      if( task->fn )
+      {
+         _async_push_groups( task->groups, 0 );
+         task->fn( task );
+         _async_pop_groups();
+         _async_group_increment( task->groups, -1, task->alloc_debug_info );
+      }
+
+      SDL_LockMutex( queue->lock );
+      queue_pop( &queue->queue );
+      queue->count --;
+      SDL_UnlockMutex( queue->lock );
+      SDL_SignalCondition( queue->blocking_signal );
+      return 1;
+   }
+   else
+      return 0;
+}
diff --git a/source/async/vg_async.c b/source/async/vg_async.c
new file mode 100644 (file)
index 0000000..c52183b
--- /dev/null
@@ -0,0 +1,293 @@
+#include "vg_async.h"
+#include "generated/threads.c"
+
+#include <threads.h>
+
+#define ASYNC_DEBUG_GROUP_COUNTS
+
+struct thread_context
+{
+   u16 async_groups[ 8 ];
+   u32 async_group_depth;
+}
+_thread_contexts[ k_thread_count ];
+
+struct thread_info *_get_thread_info( enum thread_id thread )
+{
+   ASSERT_CRITICAL( thread < k_thread_count );
+   return &_thread_infos[ thread ];
+}
+
+bool _thread_has_flags( enum thread_id thread, u32 flags )
+{
+   return (_get_thread_info( thread )->flags & flags) == flags;
+}
+
+void _async_push_groups( u16 groups, bool exclusive )
+{
+   struct thread_context *context = &_thread_contexts[ _get_thread_id() ];
+
+   ASSERT_CRITICAL( context->async_group_depth < ARRAY_COUNT( context->async_groups ) );
+   context->async_group_depth ++;
+   if( !exclusive )
+      groups |= context->async_groups[ context->async_group_depth-1 ];
+   context->async_groups[ context->async_group_depth ] = groups;
+}
+
+void _async_pop_groups(void)
+{
+   struct thread_context *context = &_thread_contexts[ _get_thread_id() ];
+   ASSERT_CRITICAL( context->async_group_depth );
+   context->async_group_depth --;
+}
+
+u16 _async_get_groups(void)
+{
+   struct thread_context *context = &_thread_contexts[ _get_thread_id() ];
+   return context->async_groups[ context->async_group_depth ];
+}
+
+struct 
+{
+   i16 group_counts[ 16 ];
+   mtx_t count_lock;
+}
+_async;
+
+struct task_queue
+{
+   u32 count;
+
+   cnd_t blocking_signal, work_signal;
+   mtx_t lock, data_lock;
+   struct queue_allocator queue;
+
+   struct task *allocating_task;
+}
+static _task_queues[ k_thread_count ];
+
+struct task
+{
+   const c8 *alloc_debug_info;
+
+   union
+   {
+      void (*fn)( struct task *task );
+      enum thread_id target_thread;
+   };
+
+   u32 buffer_size;
+   u16 groups, unused0;
+
+   union
+   {
+      u64 _force_8byte_align[];
+      u8 buffer[];
+   };
+};
+
+_Thread_local enum thread_id _thread_id;
+
+void _set_thread_id( enum thread_id id )
+{
+   _thread_id = id;
+}
+
+enum thread_id _get_thread_id(void)
+{
+   return _thread_id;
+}
+void _async_init( void )
+{
+   ASSERT_CRITICAL( mtx_init( &_async.count_lock, mtx_plain ) == thrd_success );
+
+   for( u32 i=0; i<k_thread_count; i ++ )
+   {
+      struct thread_info *thread_info = _get_thread_info( i );
+
+      if( thread_info->queue_size_m )
+      {
+         struct task_queue *queue = &_task_queues[ i ];
+         ASSERT_CRITICAL( mtx_init( &queue->lock, mtx_plain ) == thrd_success );
+         ASSERT_CRITICAL( mtx_init( &queue->data_lock, mtx_plain ) == thrd_success );
+         ASSERT_CRITICAL( cnd_init( &queue->blocking_signal ) == thrd_success );
+         ASSERT_CRITICAL( cnd_init( &queue->work_signal ) == thrd_success );
+         u32 bytes = BYTES_MB(thread_info->queue_size_m);
+         queue->queue.buffer = _heap_allocate( bytes );
+         queue->queue.size = bytes;
+      }
+   }
+}
+
+static void _async_group_increment( u16 groups, i16 dir, const c8 *who )
+{
+   if( !groups )
+      return;
+
+   ASSERT_CRITICAL( mtx_lock( &_async.count_lock ) == thrd_success );
+   for( u16 i=0; i<16; i ++ )
+   {
+      if( (groups >> i) & 0x1 )
+      {
+         _async.group_counts[i] += dir;
+         ASSERT_CRITICAL( _async.group_counts[i] >= 0 );
+         ASSERT_CRITICAL( _async.group_counts[i] <= 2048 );
+
+#if defined( ASYNC_DEBUG_GROUP_COUNTS )
+         $log( $warning, {"The task count for group "}, $unsigned(i), {" has "}, {dir>0? "increased": "decreased"}, 
+                         {" to "}, $unsigned(_async.group_counts[i]), {" ("}, {who}, {")"} );
+#endif
+      }
+   }
+   ASSERT_CRITICAL( mtx_unlock( &_async.count_lock ) == thrd_success );
+}
+
+i16 _async_group_count( u16 group )
+{
+   ASSERT_CRITICAL( group );
+   u32 index = __builtin_ctz( (u32)group );
+   ASSERT_CRITICAL( mtx_lock( &_async.count_lock ) == thrd_success );
+   i16 count = _async.group_counts[ index ];
+   ASSERT_CRITICAL( mtx_unlock( &_async.count_lock ) == thrd_success );
+   return count;
+}
+
+static struct task_queue *_get_thread_task_queue( enum thread_id thread )
+{
+   ASSERT_CRITICAL( thread < k_thread_count );
+
+   return &_task_queues[ thread ];
+}
+
+bool _task_queue_can_fit( enum thread_id thread, u32 bytes )
+{
+   struct task_queue *queue = _get_thread_task_queue( thread );
+   u32 total_size = sizeof(struct task) + bytes;
+   return total_size <= queue->queue.size;
+}
+
+struct task *_task_new( enum thread_id target_thread, u32 buffer_size, u32 async_flags, const c8 *debug_info )
+{
+   ASSERT_CRITICAL( target_thread != _get_thread_id() );
+
+   struct task_queue *queue = _get_thread_task_queue( target_thread );
+   struct queue_allocator *ring = &queue->queue;
+   u32 total_size = sizeof(struct task) + buffer_size;
+   ASSERT_CRITICAL( total_size <= queue->queue.size );
+
+   ASSERT_CRITICAL( mtx_lock( &queue->data_lock ) == thrd_success );
+   if( queue->allocating_task )
+   {
+      $log( $fatal, {"Overlapping async allocations. \n"
+                     "  Previous allocation began at: "}, {queue->allocating_task->alloc_debug_info}, {"\n"
+                     "  Overlapping call at: "}, {debug_info} );
+      _fatal_exit();
+   }
+   ASSERT_CRITICAL( mtx_lock( &queue->lock ) == thrd_success );
+
+   struct task *task = queue_alloc( ring, total_size );
+   while( ((async_flags & ASYNC_CRITICAL) || !(async_flags & ASYNC_NONBLOCKING)) && !task )
+   {
+      if( async_flags & ASYNC_CRITICAL )
+      {
+         $log( $fatal, { "Too many tasks allocated on this queue, so we cant make this (critical allocation)"} );
+         _fatal_exit();
+      }
+
+      ASSERT_CRITICAL( cnd_wait( &queue->blocking_signal, &queue->lock ) == thrd_success );
+      task = queue_alloc( ring, total_size );
+   }
+
+   if( task )
+   {
+      queue->allocating_task = task;
+      task->target_thread = target_thread;
+      task->alloc_debug_info = debug_info;
+      task->buffer_size = buffer_size;
+      task->groups = _async_get_groups();
+      _async_group_increment( task->groups, +1, task->alloc_debug_info );
+
+      ASSERT_CRITICAL( mtx_unlock( &queue->lock ) == thrd_success );
+      return task;
+   }
+   else
+   {
+      ASSERT_CRITICAL( mtx_unlock( &queue->lock ) == thrd_success );
+      return NULL;
+   }
+}
+
+void *task_buffer( struct task *task )
+{
+   return task->buffer;
+}
+
+u32 task_buffer_size( struct task *task )
+{
+   return task->buffer_size;
+}
+
+void task_send( struct task *task, void (*fn)( struct task *task ) )
+{
+   struct task_queue *queue = _get_thread_task_queue( task->target_thread );
+
+   ASSERT_CRITICAL( task );
+   ASSERT_CRITICAL( queue->allocating_task == task );
+
+   if( fn ) task->fn = fn;
+   else     _async_group_increment( task->groups, -1, task->alloc_debug_info );
+   queue->allocating_task = NULL;
+
+   ASSERT_CRITICAL( mtx_lock( &queue->lock ) == thrd_success );
+   queue->count ++;
+   ASSERT_CRITICAL( mtx_unlock( &queue->lock ) == thrd_success );
+
+   ASSERT_CRITICAL( mtx_unlock( &queue->data_lock ) == thrd_success );
+   ASSERT_CRITICAL( cnd_signal( &queue->work_signal ) == thrd_success );
+}
+
+bool _task_queue_process( bool blocking )
+{
+   struct task_queue *queue = _get_thread_task_queue( _get_thread_id() );
+   ASSERT_CRITICAL( mtx_lock( &queue->lock ) == thrd_success );
+
+   if( blocking )
+   {
+      while( queue->count == 0 )
+         ASSERT_CRITICAL( cnd_wait( &queue->work_signal, &queue->lock ) == thrd_success );
+   }
+   else
+   {
+      if( queue->count == 0)
+      {
+         ASSERT_CRITICAL( mtx_unlock( &queue->lock ) == thrd_success );
+         return 0;
+      }
+   }
+
+   struct task *task = queue_tail_data( &queue->queue );
+   ASSERT_CRITICAL( mtx_unlock( &queue->lock ) == thrd_success );
+
+   if( task )
+   {
+      /* task fn can be NULL if it was cancelled (so this is a NOP). Makes code easier if we do this instead of 
+       * reverting the queue to cancel. */
+      if( task->fn )
+      {
+         _async_push_groups( task->groups, 0 );
+         task->fn( task );
+         _async_pop_groups();
+         _async_group_increment( task->groups, -1, task->alloc_debug_info );
+      }
+
+      ASSERT_CRITICAL( mtx_lock( &queue->lock ) == thrd_success );
+      queue_pop( &queue->queue );
+      queue->count --;
+      ASSERT_CRITICAL( mtx_unlock( &queue->lock ) == thrd_success );
+
+      ASSERT_CRITICAL( cnd_signal( &queue->blocking_signal ) == thrd_success );
+      return 1;
+   }
+   else
+      return 0;
+}
diff --git a/source/async/vg_async.h b/source/async/vg_async.h
new file mode 100644 (file)
index 0000000..fd3a412
--- /dev/null
@@ -0,0 +1,42 @@
+#pragma once
+#include "generated/threads.h"
+
+struct thread_info
+{
+   const c8 *name;
+   u32 queue_size_m;
+   u32 flags;
+};
+struct thread_info *_get_thread_info( enum thread_id thread );
+
+#define ASYNC_CRITICAL 0x1
+#define ASYNC_NONBLOCKING 0x2
+
+// TODO THIS SHOULD BE PART OF THE METACOMPILER
+#define THREAD_FLAG_MAIN 0x1
+#define THREAD_FLAG_ASYNC 0x2
+#define THREAD_FLAG_OPENGL 0x4
+
+// TODO THIS SHOULD BE PART OF THE METACOMPILER
+#define ASYNC_GROUP_OPENGL 0x1
+#define ASYNC_GROUP_FIRST_LOAD 0x2
+
+void           _set_thread_id( enum thread_id id );
+enum thread_id _get_thread_id(void);
+b8 _thread_has_flags( enum thread_id id, u32 flags );
+
+struct task *_task_new( enum thread_id thread, u32 buffer_size, u32 async_flags, const c8 *debug_info );
+void         task_send( struct task *task, void (*fn)( struct task *task ) );
+
+void *task_buffer( struct task *task );
+u32 task_buffer_size( struct task *task );
+
+b8 _task_queue_process( b8 blocking );
+b8 _task_queue_can_fit( enum thread_id thread, u32 bytes );
+
+void _async_init(void);
+
+void _async_push_groups( u16 groups, b8 exclusive );
+void _async_pop_groups(void);
+u16 _async_get_groups(void);
+i16 _async_group_count( u16 group );
diff --git a/source/console/console_system.c b/source/console/console_system.c
new file mode 100644 (file)
index 0000000..c30470f
--- /dev/null
@@ -0,0 +1,123 @@
+#include "foundation.h"
+#include "console_system.h"
+
+struct console_command 
+{
+   const c8 *alias;
+   i32 (*fn)( struct console_arguments *args );
+};
+
+#include "generated/console.c"
+
+i32 _console_exec_ccmd( struct console_arguments *args )
+{
+   const c8 *path = console_get_argument( args, 0 );
+   if( !path )
+   {
+      $log( $error, {"Usage: exec <path>"} );
+      return -1;
+   }
+
+   struct stream file;
+   if( !stream_open_file( &file, path, k_stream_read ) )
+      return -1;
+
+   b8 more_to_go = 1;
+   while( more_to_go )
+   {
+      u32 temp_frame = _start_temporary_frame();
+
+      struct stream line;
+      stream_open_stack( &line, _temporary_stack_allocator(), k_stream_null_terminate );
+
+      c8 c;
+      more_to_go = 0;
+      while( stream_read( &file, &c, 1 ) )
+      {
+         if( c == '\n' )
+         {
+            more_to_go = 1;
+            break;
+         }
+         string_append_c8( &line, c );
+      }
+
+      if( stream_offset( &line ) )
+         _console_execute( string_get( &line ), 1, 0 );
+
+      _end_temporary_frame( temp_frame );
+   }
+
+   return 0;
+}
+
+void _console_execute( const c8 *command, b8 silent, b8 cheat_allowed )
+{
+   if( !silent )
+      $log( $shell, {command} );
+
+   struct stream string;
+   stream_open_buffer_read( &string, command, buffer_last_index( command, 0, 0 )+1, 0 );
+
+   struct console_arguments args;
+   args.count = 0;
+
+   u32 temp_frame = _start_temporary_frame();
+   struct console_command *target_command = NULL;
+
+   // TODO: warning if we put to many or to few arguments & required ones.
+   for( u32 i=0; i<ARRAY_COUNT(args.arguments)+1; i ++ )
+   {
+      u32 start, length;
+      enum string_parse_result info = string_parse_string( &string, &start, &length, '"' );
+      if( info == k_string_parse_ok )
+      {
+         if( i == 0 )
+         {
+            for( u32 j=0; j<ARRAY_COUNT(_console_commands); j ++ )
+            {
+               struct console_command *cmd = &_console_commands[j];
+               if( compare_buffers( cmd->alias, 0, command+start, length ) )
+               {
+                  target_command = cmd;
+                  break;
+               }
+            }
+
+            if( !target_command )
+            {
+               $log( $error, {"There is no command called '"}, $string( command+start, .length=length ), {"'"} );
+               return;
+            }
+         }
+         else
+         {
+            c8 *buffer = _temporary_allocate( length + 1, 4 );
+            buffer_copy( command + start, length, buffer, length );
+            buffer[ length ] = 0;
+
+            args.arguments[ args.count ] = buffer;
+            args.count ++;
+         }
+      }
+      else break;
+   }
+
+   if( target_command )
+      target_command->fn( &args );
+
+   _end_temporary_frame( temp_frame );
+}
+
+const c8 *console_get_argument( struct console_arguments *args, u32 index )
+{
+   if( index < args->count )
+      return args->arguments[ index ];
+   else 
+      return NULL;
+}
+
+void _console_init(void)
+{
+   _console_execute( "exec cfg/default.cfg", 1, 0 );
+}
diff --git a/source/console/console_system.h b/source/console/console_system.h
new file mode 100644 (file)
index 0000000..f2b2329
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/console.h"
+
+void _console_init(void);
+void _console_execute( const c8 *command, b8 silent, b8 cheat_allowed );
+
+struct console_arguments
+{
+   const c8 *arguments[ 32 ];
+   u32 count;
+};
+const c8 *console_get_argument( struct console_arguments *args, u32 index );
diff --git a/source/console/console_system.kv b/source/console/console_system.kv
new file mode 100644 (file)
index 0000000..8dd9a96
--- /dev/null
@@ -0,0 +1,8 @@
+include ""
+add console_system.c
+
+hook
+{
+   event START
+   function _console_init
+}
diff --git a/source/console_core.c b/source/console_core.c
deleted file mode 100644 (file)
index 5ff9660..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-#include "common_api.h"
-#include "console_core.h"
-
-struct console_command 
-{
-   const c8 *alias;
-   i32 (*fn)( struct console_arguments *args );
-};
-
-#include "generated/console.c"
-
-i32 _console_exec_ccmd( struct console_arguments *args )
-{
-   const c8 *path = console_get_argument( args, 0 );
-   if( !path )
-   {
-      $log( $error, {"Usage: exec <path>"} );
-      return -1;
-   }
-
-   struct stream file;
-   if( !stream_open_file( &file, path, k_stream_read ) )
-      return -1;
-
-   bool more_to_go = 1;
-   while( more_to_go )
-   {
-      u32 temp_frame = _start_temporary_frame();
-
-      struct stream line;
-      stream_open_stack( &line, _temporary_stack_allocator(), k_stream_null_terminate );
-
-      c8 c;
-      more_to_go = 0;
-      while( stream_read( &file, &c, 1 ) )
-      {
-         if( c == '\n' )
-         {
-            more_to_go = 1;
-            break;
-         }
-         string_append_c8( &line, c );
-      }
-
-      if( stream_offset( &line ) )
-         _console_execute( string_get( &line ), 1, 0 );
-
-      _end_temporary_frame( temp_frame );
-   }
-
-   return 0;
-}
-
-void _console_execute( const c8 *command, bool silent, bool cheat_allowed )
-{
-   if( !silent )
-      $log( $shell, {command} );
-
-   struct stream string;
-   stream_open_buffer_read( &string, command, buffer_last_index( command, 0, 0 )+1, 0 );
-
-   struct console_arguments args;
-   args.count = 0;
-
-   u32 temp_frame = _start_temporary_frame();
-   struct console_command *target_command = NULL;
-
-   // TODO: warning if we put to many or to few arguments & required ones.
-   for( u32 i=0; i<ARRAY_COUNT(args.arguments)+1; i ++ )
-   {
-      u32 start, length;
-      enum string_parse_result info = string_parse_string( &string, &start, &length, '"' );
-      if( info == k_string_parse_ok )
-      {
-         if( i == 0 )
-         {
-            for( u32 j=0; j<ARRAY_COUNT(_console_commands); j ++ )
-            {
-               struct console_command *cmd = &_console_commands[j];
-               if( compare_buffers( cmd->alias, 0, command+start, length ) )
-               {
-                  target_command = cmd;
-                  break;
-               }
-            }
-
-            if( !target_command )
-            {
-               $log( $error, {"There is no command called '"}, $string( command+start, .length=length ), {"'"} );
-               return;
-            }
-         }
-         else
-         {
-            c8 *buffer = _temporary_allocate( length + 1, 4 );
-            buffer_copy( command + start, length, buffer, length );
-            buffer[ length ] = 0;
-
-            args.arguments[ args.count ] = buffer;
-            args.count ++;
-         }
-      }
-      else break;
-   }
-
-   if( target_command )
-      target_command->fn( &args );
-
-   _end_temporary_frame( temp_frame );
-}
-
-const c8 *console_get_argument( struct console_arguments *args, u32 index )
-{
-   if( index < args->count )
-      return args->arguments[ index ];
-   else 
-      return NULL;
-}
-
-void _console_init(void)
-{
-   _console_execute( "exec cfg/default.cfg", 1, 0 );
-}
diff --git a/source/console_core.h b/source/console_core.h
deleted file mode 100644 (file)
index 91928ac..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "generated/console.h"
-
-void _console_init(void);
-void _console_execute( const c8 *command, bool silent, bool cheat_allowed );
-
-struct console_arguments
-{
-   const c8 *arguments[ 32 ];
-   u32 count;
-};
-const c8 *console_get_argument( struct console_arguments *args, u32 index );
diff --git a/source/engine/array_file.c b/source/engine/array_file.c
new file mode 100644 (file)
index 0000000..74df834
--- /dev/null
@@ -0,0 +1,333 @@
+#include "foundation.h"
+#include "array_file.h"
+
+u32 af_str_hash( const void *packed_strings, u32 pstr )
+{
+   if( pstr & 0x3 )
+   {
+      $log( $fatal, {"ALIGNMENT ERROR, PSTR INDEX: "}, $unsigned(pstr) );
+   }
+   return *((u32 *)(packed_strings + pstr));
+}
+
+const c8 *af_str( const void *packed_strings, u32 pstr )
+{
+   return packed_strings + pstr + 4;
+}
+
+b8 af_str_eq( const void *packed_strings, u32 pstr, const c8 *str, u32 str_hash )
+{
+   if( af_str_hash( packed_strings, pstr ) == str_hash )
+      if( compare_buffers( str, 0, af_str( packed_strings, pstr ), 0 ))
+         return 1;
+   return 0;
+}
+
+void af_load_array_file_buffer( struct array_file_context *ctx, struct array_file_meta *arr, void *buffer, u32 stride )
+{
+   if( arr->item_count )
+   {
+      zero_buffer( buffer, stride*arr->item_count );
+      u32 read_size = u32_min( stride, arr->item_size );
+      for( u32 i=0; i<arr->item_count; i++ )
+      {
+         stream_seek( ctx->stream, arr->file_offset + i*arr->item_size );
+         stream_read( ctx->stream, buffer+i*stride, read_size );
+      }
+   }
+}
+
+void af_load_array_file( struct array_file_context *ctx, struct array_file_ptr *out_ptr, 
+                         struct array_file_meta *arr, struct stack_allocator *stack, u32 stride )
+{
+   if( arr->item_count )
+   {
+      u32 size = stride*arr->item_count;
+      out_ptr->data = stack_allocate( stack, size, 8, NULL );
+      af_load_array_file_buffer( ctx, arr, out_ptr->data, stride );
+   }
+   else
+      out_ptr->data = NULL;
+   
+   out_ptr->stride = stride;
+   out_ptr->count = arr->item_count;
+}
+
+void *af_arritm( struct array_file_ptr *arr, u32 index )
+{
+   if( index >= arr->count )
+   {
+      $log( $fatal, {"Index out of range"}, $unsigned(index), {" >= "}, $unsigned( arr->count ) );
+      _fatal_exit();
+   }
+
+   return ((u8 *)arr->data) + index*arr->stride;
+}
+
+u32 af_arrcount( struct array_file_ptr *arr )
+{
+   return arr->count;
+}
+
+struct array_file_meta *af_find_array( struct array_file_context *ctx, const c8 *name )
+{
+   for( u32 i=0; i<af_arrcount(&ctx->index); i++ )
+   {
+      struct array_file_meta *arr = af_arritm( &ctx->index, i );
+      if( compare_buffers( arr->name, 0, name, 0 ) )
+         return arr;
+   }
+
+   return NULL;
+}
+
+b8 af_load_array( struct array_file_context *ctx, struct array_file_ptr *ptr, const c8 *name, 
+                   struct stack_allocator *stack, u32 stride )
+{
+   struct array_file_meta *arr = af_find_array( ctx, name );
+
+   if( arr )
+   {
+      af_load_array_file( ctx, ptr, arr, stack, stride );
+      return 1;
+   }
+   else
+   {
+      ptr->data = NULL;
+      ptr->count = 0;
+      ptr->stride = 0;
+      return 0;
+   }
+}
+
+b8 af_open_stream( struct array_file_context *afc, struct stream *stream, u32 min_version, u32 max_version, 
+                     struct stack_allocator *stack )
+{
+   afc->stream = stream;
+   if( stream_read( stream, &afc->header, sizeof(struct array_file_header)) != sizeof(struct array_file_header) )
+   {
+      $log( $error, {"Array file not large enough to contain header."} );
+      return 0;
+   }
+
+   if( (afc->header.version < min_version) || (afc->header.version > max_version) )
+   {
+      $log( $error, {"Array file version is out of range ("}, $unsigned( afc->header.version ),
+                    {"\nAccepted: "}, $unsigned( min_version ), {" -> "}, $unsigned( max_version ) );
+      return 0;
+   }
+
+   af_load_array_file( afc, &afc->index, &afc->header.index, stack, sizeof(struct array_file_meta) );
+   return 1;
+}
+
+/* compiler
+ * ---------------------------------------------------------------------- */
+struct af_compiler_iter
+{
+   u32 i, j;
+   struct af_compiler_index *index;
+   struct af_compiler_item *current_item;
+   void *data;
+};
+
+static void af_init_iterator( struct af_compiler_iter *iter, struct af_compiler_index *index )
+{
+   iter->i = 0;
+   iter->j = 0;
+   iter->index = index;
+   iter->current_item = NULL;
+   iter->data = NULL;
+}
+
+static b8 af_next( struct af_compiler_iter *iter )
+{
+   if( iter->i == 0 )
+   {
+      if( iter->index->first == NULL )
+         return 0;
+      iter->current_item = iter->index->first;
+   }
+
+   if( iter->j >= iter->current_item->count )
+   {
+      if( iter->current_item->next == NULL )
+         return 0;
+
+      iter->current_item = iter->current_item->next;
+      iter->j = 0;
+   }
+
+   iter->data = iter->current_item->data + (iter->j * iter->index->element_size);
+   iter->j ++;
+   iter->i ++;
+   return 1;
+}
+
+struct af_compiler_item *af_compiler_allocate_items( struct af_compiler *compiler, struct af_compiler_index *index, u32 count )
+{
+   struct af_compiler_item *entry = stack_allocate( compiler->stack, sizeof(struct af_compiler_item), 1, "Compiler item" );
+   entry->next = NULL;
+
+   u32 data_size = count * index->element_size;
+   index->element_count += count;
+
+   entry->data = stack_allocate( compiler->stack, data_size, 8, NULL );
+   entry->count = count;
+
+   for( u32 i=0; i<data_size; i ++ )
+      ((u8 *)entry->data)[i] = 0xab;
+
+   if( index->last )
+      index->last->next = entry;
+   index->last = entry;
+
+   if( !index->first )
+      index->first = entry;
+
+   return entry;
+}
+
+static void af_compiler_init_index( struct af_compiler_index *index, const c8 *alias, u32 element_size )
+{
+   ASSERT_CRITICAL( element_size );
+   if( !buffer_copy( alias, 0, index->name, sizeof(index->name) ) )
+   {
+      $log( $fatal, {"Index name overflowed: "}, {alias} );
+      _fatal_exit();
+   }
+   index->element_size = element_size;
+   index->element_count = 0;
+   index->first = NULL;
+   index->last = NULL;
+}
+
+struct af_compiler_index *af_compiler_create_index( struct af_compiler *compiler, const c8 *alias, u32 element_size )
+{
+   struct af_compiler_item *item = af_compiler_allocate_items( compiler, &compiler->index, 1 );
+   struct af_compiler_index *index = item->data;
+   af_compiler_init_index( index, alias, element_size );
+   return index;
+}
+
+u32 af_compile_string( struct af_compiler *compiler, const c8 *string )
+{
+   u32 string_hash = buffer_djb2( string, 0 );
+
+   // TODO: Hash table against existing strings (low priority)
+   u32 offset = offset = compiler->strings_index->element_count;
+   u32 bytes = PAD_TO_4( buffer_first_index( string, 0,0 )+1 + 4 );
+   struct af_compiler_item *item = af_compiler_allocate_items( compiler, compiler->strings_index, bytes );
+   *((u32 *)item->data) = string_hash;
+   buffer_copy( string, 0, item->data+4, 0 );
+   return offset;
+}
+
+void af_compiler_init( struct af_compiler *compiler, struct stack_allocator *stack )
+{
+   compiler->stack = stack;
+   af_compiler_init_index( &compiler->index, "index", sizeof(struct af_compiler_index) );
+   compiler->strings_index = af_compiler_create_index( compiler, "strings", 1 );
+   af_compile_string( compiler, "nul" );
+}
+
+static void af_write_bin( struct af_compiler *compiler, void *data, u32 data_len, u32 padding )
+{
+   if( data )
+   {
+      stream_write( &compiler->stream, data, data_len );
+      compiler->file_offset += data_len;
+   }
+
+   if( padding )
+   {
+      while( compiler->file_offset % padding )
+      {
+         const u8 pad_byte = 0xac;
+         stream_write( &compiler->stream, &pad_byte, 1 );
+         compiler->file_offset ++;
+      }
+   }
+}
+
+struct af_compiler_index *af_get_or_make_index( struct af_compiler *compiler, const c8 *alias, u32 element_size )
+{
+   struct af_compiler_iter iter;
+   af_init_iterator( &iter, &compiler->index );
+   while( af_next( &iter ) )
+   {
+      struct af_compiler_index *index = iter.data;
+      if( compare_buffers( index->name, 0, alias, 0 ) )
+         return index;
+   }
+   
+   return af_compiler_create_index( compiler, alias, element_size );
+}
+
+b8 af_write( struct af_compiler *compiler, const c8 *path, u32 version )
+{
+   u32 indices_to_write = 0;
+   
+   struct af_compiler_iter iter;
+   af_init_iterator( &iter, &compiler->index );
+   while( af_next( &iter ) )
+   {
+      struct af_compiler_index *index = iter.data;
+      if( index->element_count )
+         indices_to_write ++;
+   }
+
+   u32 header_size = PAD_TO_8( sizeof( struct array_file_header ) );
+   u32 index_size = PAD_TO_8( sizeof( struct array_file_meta ) * indices_to_write );
+   
+   if( !stream_open_file( &compiler->stream, path, k_stream_write ) )
+      return 0;
+
+   compiler->file_offset = 0;
+
+   struct array_file_header header;
+   header.version = version;
+   header.index.file_offset = header_size;
+   header.index.item_count = indices_to_write;
+   header.index.item_size = sizeof(struct array_file_meta);
+   buffer_copy( "index", 0, header.index.name, sizeof(header.index.name) );
+   af_write_bin( compiler, &header, sizeof(struct array_file_header), 8 );
+
+   /* write index */
+   u32 file_offset = header_size + index_size;
+   af_init_iterator( &iter, &compiler->index );
+   while( af_next( &iter ) )
+   {
+      struct af_compiler_index *index = iter.data;
+      if( index->element_count )
+      {
+         struct array_file_meta meta;
+         buffer_copy( index->name, sizeof(index->name), meta.name, sizeof(meta.name) );
+         meta.item_count = index->element_count;
+         meta.item_size = index->element_size;
+         meta.file_offset = file_offset;
+         file_offset += PAD_TO_8( meta.item_size*meta.item_count );
+         af_write_bin( compiler, &meta, sizeof(struct array_file_meta), 0 );
+      }
+   }
+   af_write_bin( compiler, NULL, 0, 8 );
+
+   af_init_iterator( &iter, &compiler->index );
+   while( af_next( &iter ) )
+   {
+      struct af_compiler_index *index = iter.data;
+
+      if( index->element_count )
+      {
+         struct af_compiler_iter item_iter;
+         af_init_iterator( &item_iter, index );
+
+         while( af_next( &item_iter ) )
+            af_write_bin( compiler, item_iter.data, index->element_size, 0 );
+         af_write_bin( compiler, NULL, 0, 8 );
+      }
+   }
+
+   stream_close( &compiler->stream );
+   return 1;
+}
diff --git a/source/engine/array_file.h b/source/engine/array_file.h
new file mode 100644 (file)
index 0000000..68f9739
--- /dev/null
@@ -0,0 +1,89 @@
+#pragma once
+
+struct array_file_ptr
+{
+   void *data;
+   u32 count, stride;
+};
+
+struct array_file_meta
+{
+   u32 file_offset,
+       item_count,
+       item_size;
+
+   c8 name[16];
+};
+
+struct array_file_header
+{
+   u32 version;
+   struct array_file_meta index;
+};
+
+struct array_file_context
+{
+   struct stream *stream;
+   struct array_file_header header;
+   struct array_file_ptr index;
+};
+
+/* array loading */
+struct array_file_meta *af_find_array( struct array_file_context *ctx, const c8 *name );
+
+void af_load_array_file( struct array_file_context *ctx, struct array_file_ptr *out_ptr, 
+                         struct array_file_meta *arr, struct stack_allocator *stack, u32 stride );
+
+b8 af_load_array( struct array_file_context *ctx, struct array_file_ptr *ptr, const c8 *name, 
+                    struct stack_allocator *stack, u32 stride );
+void af_load_array_file_buffer( struct array_file_context *ctx, struct array_file_meta *arr, void *buffer, u32 stride );
+
+/* array access */
+void *af_arritm( struct array_file_ptr *arr, u32 index );
+u32 af_arrcount( struct array_file_ptr *arr );
+
+/* packed string buffer access (with djb2 hash prefix) */
+const c8 *af_str( const void *packed_strings, u32 pstr );
+u32 af_str_hash( const void *packed_strings, u32 pstr );
+b8 af_str_eq( const void *packed_strings, u32 pstr, const char *str, u32 str_hash );
+
+#define AF_STR_EQ( CTX, PSTR, CONSTR ) \
+   af_str_eq( CTX, PSTR, CONSTR, vg_strdjb2( CONSTR ) )
+
+/* COmpiler 
+ * ------------------------------------ */
+
+struct af_compiler_item
+{
+   void *data;
+   u32 count;
+   struct af_compiler_item *next;
+};
+
+struct af_compiler_index
+{
+   c8 name[16];
+   u32 element_size, element_count;
+   struct af_compiler_item *first, *last;
+};
+
+struct af_compiler
+{
+   struct stack_allocator *stack;
+   struct af_compiler_index index,
+                           *strings_index;
+   struct af_compiler_item *most_recent_item;
+
+   struct stream stream;
+   u32 file_offset;
+};
+
+void af_compiler_init( struct af_compiler *compiler, struct stack_allocator *stack );
+struct af_compiler_item *af_compiler_allocate_items( struct af_compiler *compiler, struct af_compiler_index *index, u32 count );
+struct af_compiler_index *af_compiler_create_index( struct af_compiler *compiler, const c8 *alias, u32 element_size );
+b8 af_write( struct af_compiler *compiler, const c8 *path, u32 version );
+struct af_compiler_index *af_get_or_make_index( struct af_compiler *compiler, const c8 *alias, u32 element_size );
+u32 af_compile_string( struct af_compiler *compiler, const c8 *string );
+
+b8 af_open_stream( struct array_file_context *afc, struct stream *stream, u32 min_version, u32 max_version, 
+                     struct stack_allocator *stack );
index 56aee244c2272163e41ec132f109f669a8c75602..0209e6278dc47e882bb6a1b8503a948a1e14ce29 100644 (file)
@@ -4,7 +4,7 @@ struct vg_audio _vg_audio =
    .dsp_enabled_ui = 1
 };
 
-_Thread_local static bool _vg_audio_thread_has_lock = 0;
+_Thread_local static b8 _vg_audio_thread_has_lock = 0;
 
 void vg_audio_lock(void)
 {
@@ -30,7 +30,7 @@ static void vg_audio_assert_lock(void)
 /* clip loading from disk
  * -------------------------------------------------------------------------------
  */
-VG_TIER_2 bool vg_audio_clip_load( audio_clip *clip, vg_stack_allocator *stack )
+VG_TIER_2 b8 vg_audio_clip_load( audio_clip *clip, vg_stack_allocator *stack )
 {
    VG_ASSERT( _vg_thread_has_flags( VG_THREAD_BACKGROUND ) );
 
@@ -333,7 +333,7 @@ void vg_audio_sync_ui_master_controls(void)
    _vg_audio.controls.dsp_enabled = _vg_audio.dsp_enabled_ui;
 }
 
-void vg_audio_set_channel_volume( audio_channel_id id, f64 volume, bool instant )
+void vg_audio_set_channel_volume( audio_channel_id id, f64 volume, b8 instant )
 {
    vg_audio_assert_lock();
 
@@ -358,7 +358,7 @@ void vg_audio_set_channel_volume_slew_duration( audio_channel_id id, f64 length_
    controls->volume_slew_rate_per_sample = (f64)AUDIO_VOLUME_100 / (length_seconds * 44100.0);
 }
 
-void vg_audio_set_channel_pan( audio_channel_id id, f64 pan, bool instant )
+void vg_audio_set_channel_pan( audio_channel_id id, f64 pan, b8 instant )
 {
    vg_audio_assert_lock();
 
@@ -431,7 +431,7 @@ audio_channel_id vg_audio_crossfade( audio_channel_id id, audio_clip *new_clip,
    return new_id;
 }
 
-bool vg_audio_is_channel_using_clip( audio_channel_id id, audio_clip *clip )
+b8 vg_audio_is_channel_using_clip( audio_channel_id id, audio_clip *clip )
 {
    vg_audio_assert_lock();
    audio_channel *channel = get_audio_channel( id );
@@ -460,7 +460,7 @@ void vg_audio_fadeout_flagged_audio( u32 flag, f32 length )
    }
 }
 
-bool vg_audio_flagged_stopped( u32 flag )
+b8 vg_audio_flagged_stopped( u32 flag )
 {
    vg_audio_lock();
    for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
@@ -480,14 +480,14 @@ bool vg_audio_flagged_stopped( u32 flag )
    return 1;
 }
 
-void vg_audio_set_channel_pause( audio_channel_id id, bool pause )
+void vg_audio_set_channel_pause( audio_channel_id id, b8 pause )
 {
    vg_audio_assert_lock();
    struct audio_channel_controls *controls = get_audio_channel_controls( id );
    controls->pause = pause;
 }
 
-void vg_audio_set_flagged_pause( u32 flag, bool pause )
+void vg_audio_set_flagged_pause( u32 flag, b8 pause )
 {
    vg_audio_assert_lock();
    for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
@@ -747,8 +747,8 @@ static void audio_channel_mix( audio_channel_id id,
 {
    struct audio_channel_state *state = get_audio_channel_state( id );
 
-   bool is_3d = controls->flags & AUDIO_FLAG_SPACIAL_3D? 1: 0;
-   bool use_doppler = controls->flags & AUDIO_FLAG_NO_DOPPLER? 0: 1;
+   b8 is_3d = controls->flags & AUDIO_FLAG_SPACIAL_3D? 1: 0;
+   b8 use_doppler = controls->flags & AUDIO_FLAG_NO_DOPPLER? 0: 1;
 
    f32 frame_sample_rate = controls->sampling_rate_multiplier;
 
@@ -1103,7 +1103,7 @@ static void _vg_audio_mixer( void *user, u8 *stream, int byte_count )
 
       if( controls->flags & AUDIO_FLAG_RELINQUISHED )
       {
-         bool die = 0;
+         b8 die = 0;
          if( state->activity == k_channel_activity_end ) die = 1;
          if( state->activity == k_channel_activity_error ) die = 1;
          if( state->volume == 0 ) die = 1;
@@ -1153,7 +1153,7 @@ static void cb_vg_audio_view( ui_context *ctx, ui_rect rect, struct vg_magi_pane
       ui_rect row;
       ui_split( panel, k_ui_axis_h, 18, 1, row, panel );
 
-      bool show_row = ui_clip( rect, row, row );
+      b8 show_row = ui_clip( rect, row, row );
 
       if( channel->stage == k_channel_stage_none )
       {
@@ -1445,7 +1445,7 @@ VG_API void _vg_audio_init(void)
 
 void vg_audio_preupdate(void)
 {
-   bool before_working = _vg_audio.working;
+   b8 before_working = _vg_audio.working;
    _vg_audio.working = 1;
    if( _vg_audio.sdl_output_device == 0 )
       _vg_audio.working = 0;
index f493c3a4e530b0f0164a2ce6f39e638afe9087a9..735580fe5b936f1d21c48ad4fedd576b601de4ff 100644 (file)
@@ -1,13 +1,13 @@
-#include "common_api.h"
-#include "console_core.h"
-#include "graphics_api.h"
-#include "engine_interface.h"
-#include "input_api.h"
+#include "foundation.h"
+#include "console_system.h"
+#include "vg_graphics.h"
+#include "vg_engine.h"
+#include "vg_input.h"
 
 struct
 {
    struct queue_allocator input_history, log_history;
-   bool open;
+   b8 open;
 }
 _console;
 
diff --git a/source/engine/engine.kv b/source/engine/engine.kv
new file mode 100644 (file)
index 0000000..0c66c16
--- /dev/null
@@ -0,0 +1,399 @@
+include ""
+append ../maths/maths.kv
+
+include ../../submodules/SDL/include
+include ../../dep/glad.4.3/
+add ../../dep/glad.4.3/glad.c
+
+enable SDL
+append ../foundation/foundation.kv
+
+{
+   add vg_engine.c
+   hook
+   {
+      event OPTIONS
+      function _engine_options
+   }
+   event
+   {
+      name ENGINE_NEW_FRAME
+      prototype "void"
+   }
+   event
+   {
+      name ENGINE_FIXED_UPDATE
+      prototype "void"
+   }
+   event
+   {
+      name ENGINE_RENDER
+      prototype "void"
+   }
+   event
+   {
+      name ENGINE_UI
+      prototype "void"
+   }
+   event
+   {
+      name ENGINE_WINDOW_RESIZE
+      prototype "void"
+   }
+}
+
+{
+   add vg_ui.c
+   hook
+   {
+      event START
+      function _engine_ui_init
+   }
+}
+
+{
+   add vg_input.c
+   hook
+   {
+      event START
+      function _input_init
+   }
+   hook
+   {
+      event ENGINE_NEW_FRAME
+      function _input_update
+   }
+   ccmd
+   {
+      name bind
+      function _input_bind_ccmd
+      description "Bind device input to a button, action or axis"
+
+      parameter
+      {
+         description "Device input alias"
+      }
+
+      parameter
+      {
+         description "button,action,axis name"
+      }
+   }
+   ccmd
+   {
+      name unbind
+      description "Unbind all bindings that match"
+      function _input_unbind_ccmd
+
+      parameter
+      {
+         description "binding"
+      }
+   }
+}
+
+append ../console/console_system.kv
+{
+   add console.c
+   hook
+   {
+      event START
+      function _engine_console_init
+   }
+   hook
+   {
+      event ENGINE_NEW_FRAME
+      function _engine_console_update
+   }
+   hook
+   {
+      event ENGINE_UI
+      function _engine_console_ui
+      affinity 9999
+   }
+}
+
+{
+   add vg_framebuffer.c
+   hook 
+   {
+      event ENGINE_WINDOW_RESIZE
+      function _framebuffer_resize
+   }
+}
+
+{
+   add vg_render.c
+   hook
+   {
+      event START
+      function _vg_render_init
+   }
+}
+
+add vg_shader.c
+
+input_layer
+{
+   name console
+}
+input_layer
+{
+   name ui
+}
+input
+{
+   name console
+   type action
+   layer_mask console
+}
+
+append ../async/async.kv
+thread
+{
+   name main
+   queue_size_m 40
+   flag MAIN
+   flag OPENGL
+}
+thread
+{
+   name async
+   queue_size_m 40
+   flag ASYNC
+}
+thread
+{
+   name audio
+}
+thread
+{
+   name exitor
+   queue_size_m 1
+}
+
+append ../graphics/graphics.kv
+
+ccmd
+{
+   name exec
+   function _console_exec_ccmd
+   description "Execute a configuration file"
+
+   parameter
+   {
+      description "The path to the config"
+   }
+}
+config "bind ALT+GRAVE_ACCENT console"
+
+config "bind BACKSPACE ui_backspace"
+config "bind DELETE ui_delete"
+config "bind ENTER ui_enter"
+config "bind TAB ui_indent"
+config "bind HOME ui_home"
+config "bind END ui_end"
+config "bind LEFT ui_left"
+config "bind RIGHT ui_right"
+config "bind UP ui_up"
+config "bind DOWN ui_down"
+config "bind MOUSE_LEFT ui_click"
+
+config "bind LEFT_SHIFT ui_select"
+config "bind RIGHT_SHIFT ui_select"
+
+input
+{
+   name ui_delete
+   type action
+   layer_mask ui
+}
+input
+{
+   name ui_backspace
+   type action
+   layer_mask ui
+}
+input
+{
+   name ui_enter
+   type action
+   layer_mask ui
+}
+input
+{
+   name ui_indent
+   type action
+   layer_mask ui
+}
+
+input
+{
+   name ui_select
+   type button
+   layer_mask ui
+}
+input
+{
+   name ui_home
+   type button
+   layer_mask ui
+}
+input
+{
+   name ui_end
+   type button
+   layer_mask ui
+}
+input
+{
+   name ui_left
+   type button
+   layer_mask ui
+}
+input
+{
+   name ui_right
+   type button
+   layer_mask ui
+}
+input
+{
+   name ui_up
+   type button
+   layer_mask ui
+}
+input
+{
+   name ui_down
+   type button
+   layer_mask ui
+}
+input 
+{
+   name ui_click
+   type action
+   layer_mask ui
+}
+
+shader
+{
+   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
+      }
+   }
+}
+
+cvar
+{
+   name test
+   type u32
+   default 5
+   cheat 1
+   description "This is just a test variable!"
+}
+
+hook
+{
+   event START
+   function "_shaders_compile"
+}
+}
+
+
+
+
+
+
+add vg_camera.c
+
+
+add vg_tex.c
+hook
+{
+   event START
+   function _vg_tex_init
+}
+
+
+add vg_lines.c
+hook
+{
+   event START
+   function _vg_lines_init
+}
+cvar
+{
+   name debug_lines
+   type u32
+   default 0
+   cheat 1
+   description "Show line debuggers"
+}
+shader
+{
+   name debug_lines
+   subshader
+   {
+      type vertex
+      add shaders/debug_lines.vs
+      uniform
+      {
+         type mat4
+         alias uPv
+      }
+   }
+   subshader
+   {
+      type fragment
+      add shaders/debug_lines.fs
+   }
+}
+
+
+add vg_audio.c
+add vg_audio_dsp.c
+add vg_audio_vorbis.c
+hook
+{
+   affinity -3008
+   event START
+   function _audio_init
+}
+hook
+{
+   affinity -6004
+   event START
+   function _dsp_init
+}
+
+
+add model.c
+add metascene.c
+add array_file.c
diff --git a/source/engine/input.c b/source/engine/input.c
deleted file mode 100644 (file)
index 533d1dc..0000000
+++ /dev/null
@@ -1,450 +0,0 @@
-#include "common_api.h"
-#include "glfw.h"
-#include "engine_interface.h"
-#include "input_api.h"
-#include "console_core.h"
-#include "generated/input.c"
-
-struct input_alias
-{
-   const c8 *alias;
-   u8 device_type;
-   u32 key;
-   u32 alias_hash;
-}
-_input_aliases[] = 
-{
-   { "MOUSE_LEFT", k_input_device_mouse, .key = GLFW_MOUSE_BUTTON_1 },
-   { "MOUSE_RIGHT", k_input_device_mouse, .key = GLFW_MOUSE_BUTTON_2 },
-   { "MOUSE_MIDDLE", k_input_device_mouse, .key = GLFW_MOUSE_BUTTON_3 },
-   { "SPACE", k_input_device_keyboard, .key = GLFW_KEY_SPACE },
-   { "APOSTROPHE", k_input_device_keyboard, .key = GLFW_KEY_APOSTROPHE },
-   { "COMMA", k_input_device_keyboard, .key = GLFW_KEY_COMMA },
-   { "MINUS", k_input_device_keyboard, .key = GLFW_KEY_MINUS },
-   { "PERIOD", k_input_device_keyboard, .key = GLFW_KEY_PERIOD },
-   { "SLASH", k_input_device_keyboard, .key = GLFW_KEY_SLASH },
-   { "0", k_input_device_keyboard, .key = GLFW_KEY_0 },
-   { "1", k_input_device_keyboard, .key = GLFW_KEY_1 },
-   { "2", k_input_device_keyboard, .key = GLFW_KEY_2 },
-   { "3", k_input_device_keyboard, .key = GLFW_KEY_3 },
-   { "4", k_input_device_keyboard, .key = GLFW_KEY_4 },
-   { "5", k_input_device_keyboard, .key = GLFW_KEY_5 },
-   { "6", k_input_device_keyboard, .key = GLFW_KEY_6 },
-   { "7", k_input_device_keyboard, .key = GLFW_KEY_7 },
-   { "8", k_input_device_keyboard, .key = GLFW_KEY_8 },
-   { "9", k_input_device_keyboard, .key = GLFW_KEY_9 },
-   { "SEMICOLON", k_input_device_keyboard, .key = GLFW_KEY_SEMICOLON },
-   { "EQUAL", k_input_device_keyboard, .key = GLFW_KEY_EQUAL },
-   { "A", k_input_device_keyboard, .key = GLFW_KEY_A },
-   { "B", k_input_device_keyboard, .key = GLFW_KEY_B },
-   { "C", k_input_device_keyboard, .key = GLFW_KEY_C },
-   { "D", k_input_device_keyboard, .key = GLFW_KEY_D },
-   { "E", k_input_device_keyboard, .key = GLFW_KEY_E },
-   { "F", k_input_device_keyboard, .key = GLFW_KEY_F },
-   { "G", k_input_device_keyboard, .key = GLFW_KEY_G },
-   { "H", k_input_device_keyboard, .key = GLFW_KEY_H },
-   { "I", k_input_device_keyboard, .key = GLFW_KEY_I },
-   { "J", k_input_device_keyboard, .key = GLFW_KEY_J },
-   { "K", k_input_device_keyboard, .key = GLFW_KEY_K },
-   { "L", k_input_device_keyboard, .key = GLFW_KEY_L },
-   { "M", k_input_device_keyboard, .key = GLFW_KEY_M },
-   { "N", k_input_device_keyboard, .key = GLFW_KEY_N },
-   { "O", k_input_device_keyboard, .key = GLFW_KEY_O },
-   { "P", k_input_device_keyboard, .key = GLFW_KEY_P },
-   { "Q", k_input_device_keyboard, .key = GLFW_KEY_Q },
-   { "R", k_input_device_keyboard, .key = GLFW_KEY_R },
-   { "S", k_input_device_keyboard, .key = GLFW_KEY_S },
-   { "T", k_input_device_keyboard, .key = GLFW_KEY_T },
-   { "U", k_input_device_keyboard, .key = GLFW_KEY_U },
-   { "V", k_input_device_keyboard, .key = GLFW_KEY_V },
-   { "W", k_input_device_keyboard, .key = GLFW_KEY_W },
-   { "X", k_input_device_keyboard, .key = GLFW_KEY_X },
-   { "Y", k_input_device_keyboard, .key = GLFW_KEY_Y },
-   { "Z", k_input_device_keyboard, .key = GLFW_KEY_Z },
-   { "LEFT_BRACKET", k_input_device_keyboard, .key = GLFW_KEY_LEFT_BRACKET },
-   { "BACKSLASH", k_input_device_keyboard, .key = GLFW_KEY_BACKSLASH },
-   { "RIGHT_BRACKET", k_input_device_keyboard, .key = GLFW_KEY_RIGHT_BRACKET },
-   { "GRAVE_ACCENT", k_input_device_keyboard, .key = GLFW_KEY_GRAVE_ACCENT },
-   { "WORLD_1", k_input_device_keyboard, .key = GLFW_KEY_WORLD_1 },
-   { "WORLD_2", k_input_device_keyboard, .key = GLFW_KEY_WORLD_2 },
-   { "ESCAPE", k_input_device_keyboard, .key = GLFW_KEY_ESCAPE },
-   { "ENTER", k_input_device_keyboard, .key = GLFW_KEY_ENTER },
-   { "TAB", k_input_device_keyboard, .key = GLFW_KEY_TAB },
-   { "BACKSPACE", k_input_device_keyboard, .key = GLFW_KEY_BACKSPACE },
-   { "INSERT", k_input_device_keyboard, .key = GLFW_KEY_INSERT },
-   { "DELETE", k_input_device_keyboard, .key = GLFW_KEY_DELETE },
-   { "RIGHT", k_input_device_keyboard, .key = GLFW_KEY_RIGHT },
-   { "LEFT", k_input_device_keyboard, .key = GLFW_KEY_LEFT },
-   { "DOWN", k_input_device_keyboard, .key = GLFW_KEY_DOWN },
-   { "UP", k_input_device_keyboard, .key = GLFW_KEY_UP },
-   { "PAGE_UP", k_input_device_keyboard, .key = GLFW_KEY_PAGE_UP },
-   { "PAGE_DOWN", k_input_device_keyboard, .key = GLFW_KEY_PAGE_DOWN },
-   { "HOME", k_input_device_keyboard, .key = GLFW_KEY_HOME },
-   { "END", k_input_device_keyboard, .key = GLFW_KEY_END },
-   { "CAPS_LOCK", k_input_device_keyboard, .key = GLFW_KEY_CAPS_LOCK },
-   { "SCROLL_LOCK", k_input_device_keyboard, .key = GLFW_KEY_SCROLL_LOCK },
-   { "NUM_LOCK", k_input_device_keyboard, .key = GLFW_KEY_NUM_LOCK },
-   { "PRINT_SCREEN", k_input_device_keyboard, .key = GLFW_KEY_PRINT_SCREEN },
-   { "PAUSE", k_input_device_keyboard, .key = GLFW_KEY_PAUSE },
-   { "F1", k_input_device_keyboard, .key = GLFW_KEY_F1 },
-   { "F2", k_input_device_keyboard, .key = GLFW_KEY_F2 },
-   { "F3", k_input_device_keyboard, .key = GLFW_KEY_F3 },
-   { "F4", k_input_device_keyboard, .key = GLFW_KEY_F4 },
-   { "F5", k_input_device_keyboard, .key = GLFW_KEY_F5 },
-   { "F6", k_input_device_keyboard, .key = GLFW_KEY_F6 },
-   { "F7", k_input_device_keyboard, .key = GLFW_KEY_F7 },
-   { "F8", k_input_device_keyboard, .key = GLFW_KEY_F8 },
-   { "F9", k_input_device_keyboard, .key = GLFW_KEY_F9 },
-   { "F10", k_input_device_keyboard, .key = GLFW_KEY_F10 },
-   { "F11", k_input_device_keyboard, .key = GLFW_KEY_F11 },
-   { "F12", k_input_device_keyboard, .key = GLFW_KEY_F12 },
-   { "F13", k_input_device_keyboard, .key = GLFW_KEY_F13 },
-   { "F14", k_input_device_keyboard, .key = GLFW_KEY_F14 },
-   { "F15", k_input_device_keyboard, .key = GLFW_KEY_F15 },
-   { "F16", k_input_device_keyboard, .key = GLFW_KEY_F16 },
-   { "F17", k_input_device_keyboard, .key = GLFW_KEY_F17 },
-   { "F18", k_input_device_keyboard, .key = GLFW_KEY_F18 },
-   { "F19", k_input_device_keyboard, .key = GLFW_KEY_F19 },
-   { "F20", k_input_device_keyboard, .key = GLFW_KEY_F20 },
-   { "F21", k_input_device_keyboard, .key = GLFW_KEY_F21 },
-   { "F22", k_input_device_keyboard, .key = GLFW_KEY_F22 },
-   { "F23", k_input_device_keyboard, .key = GLFW_KEY_F23 },
-   { "F24", k_input_device_keyboard, .key = GLFW_KEY_F24 },
-   { "F25", k_input_device_keyboard, .key = GLFW_KEY_F25 },
-   { "KP_0", k_input_device_keyboard, .key = GLFW_KEY_KP_0 },
-   { "KP_1", k_input_device_keyboard, .key = GLFW_KEY_KP_1 },
-   { "KP_2", k_input_device_keyboard, .key = GLFW_KEY_KP_2 },
-   { "KP_3", k_input_device_keyboard, .key = GLFW_KEY_KP_3 },
-   { "KP_4", k_input_device_keyboard, .key = GLFW_KEY_KP_4 },
-   { "KP_5", k_input_device_keyboard, .key = GLFW_KEY_KP_5 },
-   { "KP_6", k_input_device_keyboard, .key = GLFW_KEY_KP_6 },
-   { "KP_7", k_input_device_keyboard, .key = GLFW_KEY_KP_7 },
-   { "KP_8", k_input_device_keyboard, .key = GLFW_KEY_KP_8 },
-   { "KP_9", k_input_device_keyboard, .key = GLFW_KEY_KP_9 },
-   { "KP_DECIMAL", k_input_device_keyboard, .key = GLFW_KEY_KP_DECIMAL },
-   { "KP_DIVIDE", k_input_device_keyboard, .key = GLFW_KEY_KP_DIVIDE },
-   { "KP_MULTIPLY", k_input_device_keyboard, .key = GLFW_KEY_KP_MULTIPLY },
-   { "KP_SUBTRACT", k_input_device_keyboard, .key = GLFW_KEY_KP_SUBTRACT },
-   { "KP_ADD", k_input_device_keyboard, .key = GLFW_KEY_KP_ADD },
-   { "KP_ENTER", k_input_device_keyboard, .key = GLFW_KEY_KP_ENTER },
-   { "KP_EQUAL", k_input_device_keyboard, .key = GLFW_KEY_KP_EQUAL },
-   { "LEFT_SHIFT", k_input_device_keyboard, .key = GLFW_KEY_LEFT_SHIFT },
-   { "LEFT_CONTROL", k_input_device_keyboard, .key = GLFW_KEY_LEFT_CONTROL },
-   { "LEFT_ALT", k_input_device_keyboard, .key = GLFW_KEY_LEFT_ALT },
-   { "LEFT_SUPER", k_input_device_keyboard, .key = GLFW_KEY_LEFT_SUPER },
-   { "RIGHT_SHIFT", k_input_device_keyboard, .key = GLFW_KEY_RIGHT_SHIFT },
-   { "RIGHT_CONTROL", k_input_device_keyboard, .key = GLFW_KEY_RIGHT_CONTROL },
-   { "RIGHT_ALT", k_input_device_keyboard, .key = GLFW_KEY_RIGHT_ALT },
-   { "RIGHT_SUPER", k_input_device_keyboard, .key = GLFW_KEY_RIGHT_SUPER },
-   { "MENU", k_input_device_keyboard, .key = GLFW_KEY_MENU },
-   { "SHIFT", k_input_device_modifier, .key = GLFW_MOD_SHIFT },
-   { "CONTROL", k_input_device_modifier, .key = GLFW_MOD_CONTROL },
-   { "ALT", k_input_device_modifier, .key = GLFW_MOD_ALT },
-   { "SUPER", k_input_device_modifier, .key = GLFW_MOD_SUPER },
-};
-
-static struct input_alias *_input_alias_find( const c8 *alias, u32 alias_length )
-{
-   u32 hash = buffer_djb2( alias, alias_length );
-   for( u32 i=0; i<ARRAY_COUNT( _input_aliases ); i ++ )
-   {
-      struct input_alias *alias_i = &_input_aliases[i];
-      if( alias_i->alias_hash == hash )
-         if( compare_buffers( alias_i->alias, 0, alias, alias_length ) )
-            return alias_i;
-   }
-   return NULL;
-}
-
-static i32 _input_page = 0;
-
-union input_state
-{
-   struct
-   {
-      u8 hold_count, activation_count, release_count, repeat_count;
-   }
-   button;
-
-   struct
-   {
-      f32 value;
-   }
-   axis;
-}
-static _input_states[2][k_input_count];
-
-struct bind
-{
-   u16 device, input_index;
-   u32 id;
-   u32 modifiers;
-};
-struct stretchy_allocator _bind_allocator;
-
-i32 _input_bind_ccmd( struct console_arguments *args )
-{
-   const c8 *buttons = console_get_argument( args, 0 );
-   if( !buttons )
-   {
-      $log( $error, {"Usage: bind <button> <action>"} );
-      return -1;
-   }
-
-   const c8 *input_name = console_get_argument( args, 1 );
-   if( !input_name )
-   {
-      $log( $error, {"Usage: bind <button> <action>"} );
-      return -1;
-   }
-
-   i32 input_id = -1;
-   for( u32 i=0; i<k_input_count; i ++ )
-   {
-      struct input_info *input = &_input_infos[ i ];
-      if( compare_buffers( input->name, 0, input_name, 0 ) )
-      {
-         input_id = i;
-         break;
-      }
-   }
-
-   if( input_id == -1 )
-   {
-      $log( $error, {"Don't know an input called '"}, {input_name}, {"'"} );
-      return -1;
-   }
-
-   struct bind new_bind = { .input_index = input_id };
-   while(1)
-   {
-      i32 plus_index = buffer_first_index( buttons, '+', 0 ),
-          length = plus_index == -1? 0: plus_index;
-
-      struct input_alias *alias = _input_alias_find( buttons, length );
-      if( alias )
-      {
-         if( (alias->device_type == k_input_device_keyboard) || (alias->device_type == k_input_device_mouse) )
-         {
-            if( new_bind.device )
-            {
-               $log( $error, {"Cannot specify more than one normal buttons in bind"} );
-               return -1;
-            }
-            new_bind.id = alias->key;
-            new_bind.device = alias->device_type;
-         }
-         else if( alias->device_type == k_input_device_modifier )
-         {
-            if( new_bind.device )
-            {
-               $log( $error, {"Modifiers must come first"} );
-               return -1;
-            }
-            new_bind.modifiers |= alias->key;
-         }
-         else
-         {
-            // TODO
-            $log( $error, {"Currently dont support device type'"}, $unsigned(alias->device_type), {"'"} );
-            return -1;
-         }
-      }
-      else
-      {
-         $log( $error, {"Don't know what a '"}, $string( buttons, .length = length ), {"' is"} );
-         return -1;
-      }
-
-      if( plus_index == -1 )
-         break;
-      else
-         buttons += plus_index+1;
-   }
-
-   if( (new_bind.device == k_input_device_keyboard) && !new_bind.id )
-   {
-      $log( $error, {"No key specified for bind"} );
-      if( new_bind.modifiers )
-         $log( $info, {"If you're trying to bind SHIFT to a normal button, use LEFT_SHIFT or RIGHT_ALT etc."} );
-      return -1;
-   }
-
-   if( !stretchy_count( &_bind_allocator ) )
-      stretchy_init( &_bind_allocator, sizeof(struct bind ) );
-   struct bind *a = stretchy_append( &_bind_allocator );
-   *a = new_bind;
-   return 0;
-}
-
-i32 _input_unbind_ccmd( struct console_arguments *args )
-{
-   return -1;
-}
-
-static void _input_callback_buttonlike( enum input_device device, i32 id, i32 action, i32 mods )
-{
-   for( u32 i=0; i<stretchy_count( &_bind_allocator ); i ++ )
-   {
-      struct bind *bind = stretchy_get( &_bind_allocator, i );
-      if( bind->device == device )
-      {
-         if( bind->id == id )
-         {
-            struct input_info *input = &_input_infos[ bind->input_index ];
-            union input_state *state = &_input_states[ _input_page^0x1 ][ bind->input_index ];
-
-            if( (input->type == k_input_type_action) || (input->type == k_input_type_button) )
-            {
-               bool allow_activation = 1;
-               if( input->type == k_input_type_action )
-                  if( bind->modifiers != mods )
-                     allow_activation = 0;
-
-               if( allow_activation && (action == GLFW_PRESS) )
-               {
-                  ASSERT_CRITICAL( state->button.activation_count < 255 );
-                  state->button.activation_count ++;
-
-                  ASSERT_CRITICAL( state->button.hold_count < 255 );
-                  state->button.hold_count ++;
-               }
-
-               if( state->button.hold_count && allow_activation && (action == GLFW_REPEAT || action == GLFW_PRESS) )
-               {
-                  ASSERT_CRITICAL( state->button.repeat_count < 255 );
-                  state->button.repeat_count ++;
-               }
-               
-               if( action == GLFW_RELEASE )
-               {
-                  if( state->button.hold_count )
-                  {
-                     state->button.hold_count --;
-                     state->button.release_count ++;
-                  }
-               }
-            }
-            else if( input->type == k_input_type_axis )
-            {
-               ASSERT_CRITICAL( 0 );
-            }
-         }
-      }
-   }
-}
-
-static void _input_mouse_callback( GLFWwindow *window, i32 button, i32 action, i32 mods )
-{
-   _input_callback_buttonlike( k_input_device_mouse, button, action, mods );
-}
-
-static void _input_key_callback( GLFWwindow *window, i32 key, i32 scancode, i32 action, i32 mods )
-{
-   _input_callback_buttonlike( k_input_device_keyboard, key, action, mods );
-}
-
-void _input_init(void)
-{
-   glfwSetKeyCallback( _engine.window_handle, _input_key_callback );
-   glfwSetMouseButtonCallback( _engine.window_handle, _input_mouse_callback );
-   for( u32 i=0; i<ARRAY_COUNT( _input_aliases ); i ++ )
-      _input_aliases[i].alias_hash = buffer_djb2( _input_aliases[i].alias, 0 );
-}
-
-void _input_update(void)
-{
-   /* flip to read page and reset the reciever one */
-   _input_page ^= 0x1;
-   for( u32 i=0; i<k_input_count; i ++ )
-   {
-      struct input_info *input = &_input_infos[ i ];
-      union input_state *back_state = &_input_states[ _input_page^0x1 ][ i ],
-                        *state = &_input_states[ _input_page ][ i ];
-
-      if( (input->type == k_input_type_action) || (input->type == k_input_type_button) )
-      {
-         back_state->button.activation_count = 0;
-         back_state->button.repeat_count = 0;
-         back_state->button.release_count = 0;
-         back_state->button.hold_count = state->button.hold_count;
-      }
-   }
-}
-
-static union input_state *_get_input_state( enum input_id id )
-{
-   return &_input_states[ _input_page ][ id ];
-}
-
-static u32 _whitelist = 0x00;
-
-
-bool _input_layer_filter( u32 mask )
-{
-   return (_whitelist & mask) || !_whitelist;
-}
-
-bool _filter_input( enum input_id id )
-{
-   return _input_layer_filter( _input_infos[ id ].layer_mask );
-}
-
-u8 _input_button_down( enum input_id id, bool allow_repeats )
-{
-   if( _filter_input(id) )
-   {
-      union input_state *state = _get_input_state( id );
-      return allow_repeats? state->button.repeat_count: state->button.activation_count;
-   }
-   else return 0;
-}
-
-u8 _input_button_up( enum input_id id )
-{
-   return _filter_input(id) && _get_input_state( id )->button.release_count;
-}
-
-u8 _input_button( enum input_id id )
-{
-   return _filter_input(id) && _get_input_state( id )->button.hold_count;
-}
-
-void _input_layer_whitelist( u32 whitelist )
-{
-   _whitelist = whitelist; 
-}
-
-void _input_string( struct stream *stream, enum input_id id )
-{
-   u32 matched = 0;
-   
-   for( u32 i=0; i<stretchy_count( &_bind_allocator ); i ++ )
-   {
-      struct bind *bind = stretchy_get( &_bind_allocator, i );
-      if( bind->input_index == id )
-      {
-         if( matched )
-            $v_string( stream, {" or "} );
-
-         matched ++;
-
-         for( u32 j=0; j<ARRAY_COUNT( _input_aliases ); j ++ )
-         {
-            struct input_alias *alias_j = &_input_aliases[j];
-            if( (alias_j->device_type == k_input_device_modifier) && (bind->modifiers & alias_j->key) )
-            {
-               $v_string( stream, {alias_j->alias}, {"+"} );
-            }
-         }
-         for( u32 j=0; j<ARRAY_COUNT( _input_aliases ); j ++ )
-         {
-            struct input_alias *alias_j = &_input_aliases[j];
-            if( (alias_j->device_type == bind->device) && (alias_j->key == bind->id) )
-            {
-               $v_string( stream, {alias_j->alias} );
-            }
-         }
-      }
-   }
-}
diff --git a/source/engine/main.c b/source/engine/main.c
deleted file mode 100644 (file)
index aec6498..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-#include "common_api.h"
-#include "common_thread_api.h"
-#include <time.h>
-#include <math.h>
-#include <threads.h>
-#include "engine_interface.h"
-#include "engine_backend.h"
-#include "input_api.h"
-#include "opengl.h"
-#include "glfw.h"
-
-// TODO: temp
-#include "console_core.h"
-#include "generated/hooks.h"
-
-struct _engine _engine;
-
-struct
-{
-   bool console_open;
-}
-_engine_backend;
-
-static void _engine_set_framerate( f64 hz, bool vsync )
-{
-   glfwSwapInterval(vsync);
-   _engine.vsync = vsync;
-
-   if( hz < 24.0 )
-      hz = 24.0;
-   if( hz > 300.0 )
-      hz = 300.0;
-
-   if( vsync )
-      hz += 5.0; // Timing allowance
-
-   _engine.framerate_limit = hz;
-}
-
-void _character_callback( GLFWwindow *window, u32 codepoint )
-{
-   if( _input_layer_filter( 1<<k_input_layer_ui ) )
-      _engine_ui_input_character( codepoint );
-}
-
-void _engine_options(void)
-{
-   const c8 *arg;
-   if( _option_long( "high-performance", "Turn graphics to lowest quality" ) )
-      _engine.quality = k_quality_profile_low;
-
-   //if( vg_long_opt( "no-steam", "Disable Steam integration (Good idea for pirating)" ) )
-   //   _steam_api.disabled = 1;
-}
-
-
-i32 async_thread( void *_ )
-{
-   _set_thread_id( k_thread_async );
-   while( _task_queue_process( 1 ) ) {}
-   $log( $warning, {"Async thread exits"} );
-   return 0;
-}
-
-APIENTRY void _opengl_debug( GLenum source,
-            GLenum type,
-            GLuint id,
-            GLenum severity,
-            GLsizei length,
-            const GLchar *message,
-            const void *userParam)
-{
-   if( type == GL_DEBUG_TYPE_ERROR || type == GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR )
-      $log( $error, {message} );
-
-   if( type == GL_DEBUG_TYPE_ERROR )
-      _fatal_exit();
-}
-
-i32 main( i32 argc, const c8 *argv[] )
-{
-   VG_PRE_MAIN;
-
-   ASSERT_CRITICAL( glfwInit() );
-
-   glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 4 );
-   glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 );
-   glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
-   glfwWindowHint( GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE );
-   _engine.window_handle = glfwCreateWindow(640, 480, "My Title", NULL, NULL);
-   if( !_engine.window_handle )
-   {
-      glfwTerminate();
-      return -1;
-   }
-
-   glfwMakeContextCurrent( _engine.window_handle );
-   gladLoadGLLoader( (GLADloadproc)glfwGetProcAddress );
-   glfwSetCharCallback( _engine.window_handle, _character_callback );
-   glDebugMessageCallback( _opengl_debug, NULL );
-
-   GLFWmonitor *monitor = glfwGetPrimaryMonitor();
-   const GLFWvidmode *vidmode = glfwGetVideoMode( monitor );
-   _engine_set_framerate( vidmode->refreshRate, 0 );
-   f64 next_frame_time = 0.0, fixed_time = 0.0, fixed_accumulator = 0.0;
-   glfwGetFramebufferSize( _engine.window_handle, &_engine.w, &_engine.h );
-   glGetIntegerv( GL_FRAMEBUFFER_BINDING, &_engine.native_fbo );
-
-
-   /* ------------- */
-
-   EVENT_CALL( START );
-
-   thrd_t handle;
-   ASSERT_CRITICAL( thrd_create( &handle, async_thread, NULL ) == thrd_success );
-
-L_new_frame:;
-   f64 now = glfwGetTime();
-   if( now < next_frame_time )
-   {
-      f64 ms = (u32)floor((next_frame_time - now) * 1000.0);
-      if( ms )
-      {
-         struct timespec r = { 0, ms * 1000000 };
-         nanosleep( &r, NULL );
-      }
-      else
-      {
-         __asm__ __volatile__("pause\n");
-      }
-      goto L_new_frame;
-   }
-
-   _engine.time_delta = now - _engine.time;
-   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;
-   next_frame_time = now + 1.0/_engine.framerate_limit;
-
-   while( _task_queue_process( 0 ) ) {}
-
-   /* normal update */
-   EVENT_CALL( ENGINE_NEW_FRAME );
-
-   /* 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;
-         EVENT_CALL( ENGINE_FIXED_UPDATE );
-      }
-      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 );
-   if( (pw != _engine.w) || (ph != _engine.h) )
-   {
-      EVENT_CALL( ENGINE_WINDOW_RESIZE );
-   }
-
-   EVENT_CALL( ENGINE_RENDER );
-   _engine_ui_pre_render();
-   EVENT_CALL( ENGINE_UI );
-   _engine_ui_post_render();
-
-   glfwSwapBuffers(_engine.window_handle);
-
-   glfwPollEvents();
-   if( glfwWindowShouldClose(_engine.window_handle) )
-   {
-      glfwDestroyWindow( _engine.window_handle );
-      glfwTerminate();
-      return 0;
-   }
-
-   goto L_new_frame;
-}
diff --git a/source/engine/metascene.c b/source/engine/metascene.c
new file mode 100644 (file)
index 0000000..f7328ac
--- /dev/null
@@ -0,0 +1,499 @@
+#include "vg_engine.h"
+#include "metascene.h"
+#include "common_maths.h"
+#include "model.h"
+#include "model_entity.h"
+#include "shader_props.h"
+#include "array_file.h"
+#include "vg_async.h"
+
+void metascene_load( struct metascene *ms, const c8 *path, struct stack_allocator *stack )
+{
+   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
+   zero_buffer( ms, sizeof(struct metascene) );
+
+   struct stream stream;
+   ASSERT_CRITICAL( stream_open_file( &stream, path, k_stream_read ) );
+
+   u32 temp_frame = _start_temporary_frame();
+   {
+      struct array_file_context af;
+      ASSERT_CRITICAL( af_open_stream( &af, &stream, MS_VERSION_MIN, MS_VERSION_NR, _temporary_stack_allocator() ) );
+
+      struct array_file_ptr strings;
+      af_load_array( &af, &strings, "strings", stack, 1 );
+      ms->packed_strings = strings.data;
+      af_load_array( &af, &ms->infos, "ms_scene_info", stack, sizeof(struct ms_scene_info) );
+      af_load_array( &af, &ms->instances, "ms_instance", stack, sizeof(struct ms_instance) );
+      af_load_array( &af, &ms->overrides, "ms_override", stack, sizeof(struct ms_override) );
+      af_load_array( &af, &ms->strips, "ms_strip", stack, sizeof(struct ms_strip) );
+      af_load_array( &af, &ms->tracks, "ms_track", stack, sizeof(struct ms_track) );
+      af_load_array( &af, &ms->keyframes, "ms_keyframe", stack, sizeof(struct ms_keyframe) );
+      af_load_array( &af, &ms->cameras, "ent_camera", stack, sizeof(struct ent_camera) );
+      af_load_array( &af, &ms->audios, "ent_audio", stack, sizeof(struct ent_audio) );
+      af_load_array( &af, &ms->audio_clips, "ent_audio_clip", stack, sizeof(struct ent_audio_clip) );
+      af_load_array( &af, &ms->curves, "ms_curves", stack, sizeof(struct ms_curve_keyframe) );
+
+      ASSERT_CRITICAL( af_arrcount( &ms->infos ) );
+      struct ms_scene_info *src_inf = af_arritm( &ms->infos, 0 );
+      ms->info = *src_inf;
+
+      stream_close( &stream );
+   }
+   _end_temporary_frame( temp_frame );
+}
+
+u32 skeleton_bone_id( struct ms_skeleton *skele, const c8 *name )
+{
+   for( u32 i=1; i<skele->bone_count; i++ )
+      if( compare_buffers( skele->bones[i].name, 0, name, 0 ) )
+         return i;
+
+   $log( $fatal, {"skeleton_bone_id( *, "}, {name}, {" ); -> Bone does not exist"} );
+   _fatal_exit();
+   return 0;
+}
+
+void keyframe_copy_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, i32 num )
+{
+   for( i32 i=0; i<num; i++ )
+      kfb[i] = kfa[i];
+}
+
+
+/* apply a rotation from the perspective of root */
+void keyframe_rotate_around( struct ms_keyframe *kf, f32 origin[3], f32 offset[3], f32 q[4] )
+{
+   f32 v0[3], co[3];
+   v3_add( kf->co, offset, co );
+   v3_sub( co, origin, v0 );
+   q_mulv( q, v0, v0 );
+   v3_add( v0, origin, co );
+   v3_sub( co, offset, kf->co );
+   q_mul( q, kf->q, kf->q );
+   q_normalize( kf->q );
+}
+
+void keyframe_lerp( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd )
+{
+   v3_lerp( kfa->co, kfb->co, t, kfd->co );
+   q_nlerp( kfa->q,  kfb->q,  t, kfd->q );
+   v3_lerp( kfa->s,  kfb->s,  t, kfd->s );
+}
+
+/*
+ * Lerp between two sets of keyframes and store in dest. Rotations use Nlerp.
+ */
+void keyframe_lerp_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd, i32 count )
+{
+   if( t <= 0.0001f )
+   {
+      keyframe_copy_pose( kfa, kfd, count );
+      return;
+   }
+   else if( t >= 0.9999f )
+   {
+      keyframe_copy_pose( kfb, kfd, count );
+      return;
+   }
+
+   for( i32 i=0; i<count; i++ )
+      keyframe_lerp( kfa+i, kfb+i, t, kfd+i );
+}
+
+void skeleton_lerp_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd )
+{
+   keyframe_lerp_pose( kfa, kfb, t, kfd, skele->bone_count-1 );
+}
+
+void skeleton_copy_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfd )
+{
+   keyframe_copy_pose( kfa, kfd, skele->bone_count-1 );
+}
+
+/*
+ * Sample animation between 2 closest frames using time value. Output is a
+ * keyframe buffer that is allocated with an appropriate size
+ *
+ * Time is in SECONDS
+ */
+void skeleton_sample_anim( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output )
+{
+   struct ms_strip *strip = anim->strip;
+   f32 animtime  = fmodf( time*anim->framerate, (f32)strip->strip.length ),
+       animframe = floorf( animtime ),
+       t = animtime - animframe;
+
+   u32 frame = (u32)animframe % strip->strip.length,
+       next  = (frame+1) % strip->strip.length;
+
+   struct ms_keyframe *base  = anim->keyframes_base + strip->strip.count*frame,
+                      *nbase = anim->keyframes_base + strip->strip.count*next;
+   skeleton_lerp_pose( skele, base, nbase, t, output );
+}
+
+/* time is in SECONDS */
+i32 skeleton_sample_anim_clamped( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output )
+{
+   struct ms_strip *strip = anim->strip;
+   f32 end = (strip->strip.length-1)/anim->framerate;
+   skeleton_sample_anim( skele, anim, f32_min( end, time ), output );
+   if( time > end ) return 0;
+   else             return 1;
+}
+
+static i32 should_apply_bone( struct ms_skeleton *skele, u32 id, enum anim_apply type )
+{
+   struct ms_skeleton_bone *sb = &skele->bones[ id ],
+                           *sp = &skele->bones[ sb->parent ];
+   if( type == k_anim_apply_defer_ik )
+   {
+      if( ((sp->flags & k_bone_flag_ik) && !(sb->flags & k_bone_flag_ik)) || sp->defer )
+      {
+         sb->defer = 1;
+         return 0;
+      }
+      else
+      {
+         sb->defer = 0;
+         return 1;
+      }
+   }
+   else if( type == k_anim_apply_deffered_only )
+   {
+      if( sb->defer )
+         return 1;
+      else
+         return 0;
+   }
+
+   return 1;
+}
+
+/*
+ * Apply block of keyframes to skeletons final pose
+ */
+void skeleton_apply_pose( struct ms_skeleton *skele, struct ms_keyframe *pose, enum anim_apply passtype, f32 final_mtx[][4][3] )
+{
+   if( passtype == k_anim_apply_absolute )
+   {
+      for( u32 i=1; i<skele->bone_count; i++ )
+      {
+         struct ms_keyframe *kf = &pose[i-1];
+         q_m3x3( kf->q, final_mtx[i] );
+         m3x3_scale( final_mtx[i], kf->s );
+         v3_copy( kf->co, final_mtx[i][3] );
+      }
+      return;
+   }
+
+   m4x3_identity( final_mtx[0] );
+   skele->bones[0].defer = 0;
+   skele->bones[0].flags &= ~k_bone_flag_ik;
+
+   for( u32 i=1; i<skele->bone_count; i++ )
+   {
+      struct ms_skeleton_bone *sb = &skele->bones[i];
+      if( !should_apply_bone( skele, i, passtype ) )
+         continue;
+
+      sb->defer = 0;
+
+      /* process pose */
+      f32 posemtx[4][3];
+      f32 temp_delta[3];
+      v3_sub( skele->bones[i].co, skele->bones[sb->parent].co, temp_delta );
+
+      /* pose matrix */
+      struct ms_keyframe *kf = &pose[i-1];
+      q_m3x3( kf->q, posemtx );
+      m3x3_scale( posemtx, kf->s );
+      v3_copy( kf->co, posemtx[3] );
+      v3_add( temp_delta, posemtx[3], posemtx[3] );
+
+      /* final matrix */
+      m4x3_mul( final_mtx[ sb->parent ], posemtx, final_mtx[i] );
+   }
+}
+
+/* 
+ * Take the final matrices and decompose it into an absolute positioned anim
+ */
+void skeleton_decompose_mtx_absolute( struct ms_skeleton *skele, struct ms_keyframe *anim, f32 final_mtx[][4][3] )
+{
+   for( u32 i=1; i<skele->bone_count; i++ )
+   {
+      struct ms_keyframe *kf = &anim[i-1];
+      m4x3_decompose( final_mtx[i], kf->co, kf->q, kf->s );
+   }
+}
+
+/* 
+ * creates the reference inverse matrix for an IK bone, as it has an initial 
+ * intrisic rotation based on the direction that the IK is setup..
+ */
+void skeleton_inverse_for_ik( struct ms_skeleton *skele, f32 ivaxis[3], u32 id, f32 inverse[3][3] )
+{
+   v3_copy( ivaxis, inverse[0] );
+   v3_copy( skele->bones[id].end, inverse[1] );
+   v3_normalize( inverse[1] );
+   v3_cross( inverse[0], inverse[1], inverse[2] );
+   m3x3_transpose( inverse, inverse );
+}
+
+/*
+ * Creates inverse rotation matrices which the IK system uses.
+ */
+void skeleton_create_inverses( struct ms_skeleton *skele )
+{
+   /* IK: inverse 'plane-bone space' axis '(^axis,^bone,...)[base] */
+   for( u32 i=0; i<skele->ik_count; i++ )
+   {
+      struct ms_skeleton_ik *ik = &skele->ik[i];
+      f32 iv0[3], iv1[3], ivaxis[3];
+      v3_sub( skele->bones[ik->target].co, skele->bones[ik->lower].co, iv0 );
+      v3_sub( skele->bones[ik->pole].co,   skele->bones[ik->lower].co, iv1 );
+      v3_cross( iv0, iv1, ivaxis );
+      v3_normalize( ivaxis );
+
+      skeleton_inverse_for_ik( skele, ivaxis, ik->lower, ik->ia );
+      skeleton_inverse_for_ik( skele, ivaxis, ik->upper, ik->ib );
+   }
+}
+
+/*
+ * Apply a model matrix to all bones, should be done last
+ */
+void skeleton_apply_transform( struct ms_skeleton *skele, f32 transform[4][3], f32 final_mtx[][4][3] )
+{
+   for( u32 i=0; i<skele->bone_count; i++ )
+      m4x3_mul( transform, final_mtx[i], final_mtx[i] );
+}
+
+/*
+ * Apply an inverse matrix to all bones which maps vertices from bind space into
+ * bone relative positions
+ */
+void skeleton_apply_inverses( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
+{
+   for( u32 i=0; i<skele->bone_count; i++ )
+   {
+      struct ms_skeleton_bone *sb = &skele->bones[i];
+      f32 inverse[4][3];
+      m3x3_identity( inverse );
+      v3_negate( sb->co, inverse[3] );
+      m4x3_mul( final_mtx[i], inverse, final_mtx[i] );
+   }
+}
+
+/*
+ * Apply all IK modifiers (2 bone ik reference from blender is supported)
+ */
+void skeleton_apply_ik_pass( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
+{
+   for( u32 i=0; i<skele->ik_count; i++ )
+   {
+      struct ms_skeleton_ik *ik = &skele->ik[i];
+      f32 v0[3], /* base -> target */
+          v1[3], /* base -> pole */
+          vaxis[3];
+      f32 co_base[3],
+          co_target[3],
+          co_pole[3];
+
+      v3_copy( final_mtx[ik->lower][3], co_base );
+      v3_copy( final_mtx[ik->target][3], co_target );
+      v3_copy( final_mtx[ik->pole][3], co_pole );
+
+      v3_sub( co_target, co_base, v0 );
+      v3_sub( co_pole, co_base, v1 );
+      v3_cross( v0, v1, vaxis );
+      v3_normalize( vaxis );
+      v3_normalize( v0 );
+      v3_cross( vaxis, v0, v1 );
+
+      /* localize problem into [x:v0,y:v1] 2d plane */
+      f32 base[2] = { v3_dot( v0, co_base   ), v3_dot( v1, co_base   ) },
+          end[2]  = { v3_dot( v0, co_target ), v3_dot( v1, co_target ) },
+          knee[2];
+
+      /* Compute angles (basic trig)*/
+      f32 delta[2];
+      v2_sub( end, base, delta );
+
+      f32 l1 = v3_length( skele->bones[ik->lower].end ),
+          l2 = v3_length( skele->bones[ik->upper].end ),
+          d = f32_clamp( v2_length(delta), fabsf(l1 - l2), l1+l2-0.00001f ),
+          c = acosf( (l1*l1 + d*d - l2*l2) / (2.0f*l1*d) ),
+          rot = atan2f( delta[1], delta[0] ) + c - VG_PIf/2.0f;
+
+      knee[0] = sinf(-rot) * l1;
+      knee[1] = cosf(-rot) * l1;
+
+      m4x3_identity( final_mtx[ik->lower] );
+      m4x3_identity( final_mtx[ik->upper] );
+
+      /* create rotation matrix */
+      f32 co_knee[3];
+      v3_muladds( co_base, v0, knee[0], co_knee );
+      v3_muladds( co_knee, v1, knee[1], co_knee );
+      // vg_line( co_base, co_knee, 0xff00ff00 );
+
+      f32 transform[4][3];
+      v3_copy( vaxis, transform[0] );
+      v3_muls( v0, knee[0], transform[1] );
+      v3_muladds( transform[1], v1, knee[1], transform[1] );
+      v3_normalize( transform[1] );
+      v3_cross( transform[0], transform[1], transform[2] );
+      v3_copy( co_base, transform[3] );
+
+      m3x3_mul( transform, ik->ia, transform );
+      m4x3_copy( transform, final_mtx[ik->lower] );
+
+      /* upper/knee bone */
+      v3_copy( vaxis, transform[0] );
+      v3_sub( co_target, co_knee, transform[1] );
+      v3_normalize( transform[1] );
+      v3_cross( transform[0], transform[1], transform[2] );
+      v3_copy( co_knee, transform[3] );
+
+      m3x3_mul( transform, ik->ib, transform );
+      m4x3_copy( transform, final_mtx[ik->upper] );
+   }
+}
+
+/*
+ * Applies the typical operations that you want for an IK rig: 
+ *    Pose, IK, Pose(deferred), Inverses, Transform
+ */
+void skeleton_apply_standard( struct ms_skeleton *skele, struct ms_keyframe *pose, f32 transform[4][3], f32 final_mtx[][4][3] )
+{
+   skeleton_apply_pose( skele, pose, k_anim_apply_defer_ik, final_mtx );
+   skeleton_apply_ik_pass( skele, final_mtx );
+   skeleton_apply_pose( skele, pose, k_anim_apply_deffered_only, final_mtx );
+   skeleton_apply_inverses( skele, final_mtx );
+   skeleton_apply_transform( skele, transform, final_mtx );
+}
+
+void skeleton_alloc_from( struct ms_skeleton *skele, struct stack_allocator *stack, 
+                          struct vg_model *model, struct mdl_armature *armature )
+{
+   skele->bone_count     = armature->bone_count+1;
+   skele->ik_count       = 0;
+   skele->collider_count = 0;
+
+   for( u32 i=0; i<armature->bone_count; i++ )
+   {
+      struct mdl_bone *bone = &model->bones[ armature->bone_start+i ];
+      if( bone->flags & k_bone_flag_ik )
+         skele->ik_count ++;
+      if( bone->collider )
+         skele->collider_count ++;
+   }
+
+   u32 bone_size = sizeof(struct ms_skeleton_bone) * skele->bone_count,
+       ik_size   = sizeof(struct ms_skeleton_ik)   * skele->ik_count;
+
+   skele->bones = stack_allocate( stack, bone_size, 8, NULL );
+   skele->ik    = stack_allocate( stack, ik_size, 8, NULL );
+
+   zero_buffer( skele->bones, bone_size );
+   zero_buffer( skele->ik, ik_size );
+}
+
+/* Setup a skeleton from model. mdl's metadata should stick around */
+void skeleton_setup( struct ms_skeleton *skele, struct vg_model *model, u32 index, struct stack_allocator *stack )
+{
+   u32 ik_count = 0, collider_count = 0;
+   skele->bone_count = 0;
+   skele->bones = NULL;
+
+   if( !model->armature_count )
+   {
+      $log( $fatal, {"No skeleton in model"} );
+      _fatal_exit();
+   }
+
+   struct mdl_armature *armature = &model->armatures[ index ];
+   skeleton_alloc_from( skele, stack, model, armature );
+
+   for( u32 i=0; i<armature->bone_count; i++ )
+   {
+      struct mdl_bone *bone = &model->bones[ armature->bone_start+i ];
+      struct ms_skeleton_bone *sb = &skele->bones[i+1];
+
+      v3_copy( bone->co, sb->co );
+      v3_copy( bone->end, sb->end );
+
+      sb->parent = bone->parent;
+      sb->name   = af_str( model->packed_strings, bone->pstr_name );
+      sb->flags  = bone->flags;
+      sb->collider = bone->collider;
+      sb->orig_bone = bone;
+
+      if( sb->flags & k_bone_flag_ik )
+      {
+         skele->bones[ sb->parent ].flags |= k_bone_flag_ik;
+         
+         if( ik_count == skele->ik_count )
+         {
+            $log( $fatal, {"Too many ik bones, corrupt model file"} );
+            _fatal_exit();
+         }
+
+         struct ms_skeleton_ik *ik = &skele->ik[ ik_count ++ ];
+         ik->upper = i+1;
+         ik->lower = bone->parent;
+         ik->target = bone->ik_target;
+         ik->pole = bone->ik_pole;
+      }
+
+      box_copy( bone->hitbox, sb->hitbox );
+      if( bone->collider )
+      {
+         if( collider_count == skele->collider_count )
+         {
+            $log( $fatal, {"Too many collider bones"} );
+            _fatal_exit();
+         }
+         collider_count ++;
+      }
+   }
+
+   /* fill in implicit root bone */
+   v3_fill( skele->bones[0].co, 0 );
+   v3_copy( (f32[3]){0.0f,1.0f,0.0f}, skele->bones[0].end );
+   skele->bones[0].parent = 0xffffffff;
+   skele->bones[0].flags = 0;
+   skele->bones[0].name = "[root]";
+
+   skeleton_create_inverses( skele );
+   $log( $ok, {"Loaded skeleton with "}, $unsigned( skele->bone_count ), {" bones"} );
+   $log( $ok, {"                     "}, $unsigned( skele->collider_count), {" colliders"} );
+}
+
+#if 0
+void skeleton_debug( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
+{
+   for( u32 i=1; i<skele->bone_count; i ++ )
+   {
+      struct ms_skeleton_bone *sb = &skele->bones[i];
+      f32 p0[3], p1[3];
+      v3_copy( sb->co, p0 );
+      v3_add( p0, sb->end, p1 );
+
+      m4x3_mulv( final_mtx[i], p0, p0 );
+      m4x3_mulv( final_mtx[i], p1, p1 );
+
+      if( sb->flags & k_bone_flag_deform )
+      {
+         if( sb->flags & k_bone_flag_ik )
+            vg_line( p0, p1, 0xff0000ff );
+         else
+            vg_line( p0, p1, 0xffcccccc );
+      }
+      else
+         vg_line( p0, p1, 0xff00ffff );
+   }
+}
+#endif
diff --git a/source/engine/metascene.h b/source/engine/metascene.h
new file mode 100644 (file)
index 0000000..a82063d
--- /dev/null
@@ -0,0 +1,186 @@
+#pragma once
+#include "foundation.h"
+#include "array_file.h"
+#include "model.h"
+
+#define MS_VERSION_NR 2
+#define MS_VERSION_MIN 2
+
+struct ms_scene_info
+{
+   f32 framerate;
+   u32 end_frame;
+};
+
+struct metascene
+{
+   const void *packed_strings;
+   struct ms_scene_info info;
+   struct array_file_ptr infos,
+                         instances,
+                         overrides,
+                         strips,
+                         tracks,
+                         keyframes,
+                         curves,
+
+                         audios, /* kinda temp? */
+                         audio_clips,
+                         cameras;
+};
+
+struct ms_instance
+{
+   u32 pstr_name,
+       override_start,
+       override_count;
+};
+
+struct ms_override
+{
+   u32 entity_type, pstr_name;
+   struct mdl_transform transform;
+};
+
+enum ms_strip_mode
+{
+   k_ms_strip_mode_keyframes = 0x1,
+   k_ms_strip_mode_curves = 0x2,
+   k_ms_strip_mode_animation = 0x1|0x2,
+
+   k_ms_strip_mode_camera = 0x10,
+   k_ms_strip_mode_event = 0x20,
+   k_ms_strip_mode_subtitle = 0x40,
+   k_ms_strip_mode_fadeout = 0x80,
+};
+
+struct ms_strip
+{
+   u8  mode;
+   i32 offset;
+
+   union
+   {
+      struct
+      {
+         u32 start, count, length,
+             pstr_name,
+             pstr_internal_name,
+             instance_id, object_id;
+         f32 timing_offset;
+      }
+      strip;
+
+      struct
+      {
+         u32 entity_id;
+      }
+      camera;
+
+      struct
+      {
+         u32 pstr_en, res0, res1, res2;
+         u8  character;
+      }
+      subtitle;
+
+      struct
+      {
+         u32 pstr_string;
+      }
+      event;
+   };
+};
+
+struct ms_track
+{
+   u32 keyframe_start,
+       keyframe_count,
+       pstr_datapath,
+       semantic_type;
+};
+
+struct ms_curve_keyframe
+{
+   f32 co[2], l[2], r[2];
+};
+
+struct ms_keyframe
+{
+   f32 co[3], s[3];
+   f32 q[4];
+};
+
+/* skeletons 
+ * ------------------------------------------------------------------------------------------------------------------ */
+
+struct ms_skeleton
+{
+   struct ms_skeleton_bone
+   {
+      f32 co[3], end[3];
+      u32 parent;
+
+      u32 flags;
+      int defer;
+
+      //ms_keyframe kf;
+      struct mdl_bone *orig_bone;
+      u32 collider;     // TODO: SOA
+      f32 hitbox[2][3]; // TODO: SOA
+      const char *name; // TODO: SOA
+   }
+   *bones;
+   u32 bone_count;
+
+   struct ms_skeleton_ik
+   {
+      u32 lower, upper, target, pole;
+      f32 ia[3][3], ib[3][3];
+   }
+   *ik;
+   u32 ik_count;
+   u32 collider_count,
+       bindable_count;
+};
+
+void metascene_load( struct metascene *ms, const c8 *path, struct stack_allocator *stack );
+void keyframe_copy_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, i32 num );
+void keyframe_rotate_around( struct ms_keyframe *kf, f32 origin[3], f32 offset[3], f32 q[4] );
+void keyframe_lerp( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd );
+void keyframe_lerp_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd, i32 count );
+void skeleton_alloc_from( struct ms_skeleton *skele, struct stack_allocator *stack, 
+                          struct vg_model *model, struct mdl_armature *armature );
+u32 skeleton_bone_id( struct ms_skeleton *skele, const c8 *name );
+void skeleton_lerp_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd );
+void skeleton_copy_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfd );
+
+struct ms_skeletal_animation
+{
+   struct ms_strip *strip;
+   struct ms_keyframe *keyframes_base;
+   f32 framerate;
+};
+
+void skeleton_sample_anim( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output );
+int skeleton_sample_anim_clamped( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output );
+
+enum anim_apply
+{
+   k_anim_apply_always,
+   k_anim_apply_defer_ik,
+   k_anim_apply_deffered_only,
+   k_anim_apply_absolute
+}
+anim_apply;
+
+void skeleton_apply_pose( struct ms_skeleton *skele, struct ms_keyframe *pose, enum anim_apply passtype, f32 final_mtx[][4][3] );
+void skeleton_decompose_mtx_absolute( struct ms_skeleton *skele, struct ms_keyframe *anim, f32 final_mtx[][4][3] );
+void skeleton_inverse_for_ik( struct ms_skeleton *skele, f32 ivaxis[3], u32 id, f32 inverse[3][3] );
+void skeleton_create_inverses( struct ms_skeleton *skele );
+void skeleton_apply_transform( struct ms_skeleton *skele, f32 transform[4][3], f32 final_mtx[][4][3] );
+void skeleton_apply_inverses( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
+void skeleton_apply_ik_pass( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
+void skeleton_apply_standard( struct ms_skeleton *skele, struct ms_keyframe *pose, f32 transform[4][3], f32 final_mtx[][4][3] );
+void skeleton_setup( struct ms_skeleton *skele, struct vg_model *model, u32 index, struct stack_allocator *stack );
+void skeleton_debug( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
diff --git a/source/engine/model.c b/source/engine/model.c
new file mode 100644 (file)
index 0000000..5e34c1c
--- /dev/null
@@ -0,0 +1,549 @@
+#include "foundation.h"
+#include "common_maths.h"
+#include "vg_async.h"
+
+#include "model.h"
+#include "array_file.h"
+#include "vg_opengl.h"
+#include "shader_props.h"
+#include <stddef.h>
+
+struct stream *vg_model_stream_pack_stream( struct vg_model_stream_context *ctx, struct mdl_file *file )
+{
+   if( !file->pack_size )
+   {
+      $log( $fatal, {"Packed file is only a header; it is not packed\n"},
+                    {"Path: "}, {af_str( &ctx->model->packed_strings, file->pstr_path )} );
+      _fatal_exit();
+   }
+
+   stream_seek( &ctx->stream, ctx->model->pack_base_offset + file->pack_offset );
+
+   // WE dont ever read backwards so this just sets us up an uppper bound to read against
+   ctx->stream.buffer_length = ctx->model->pack_base_offset + file->pack_offset + file->pack_size;
+   return &ctx->stream;
+}
+
+/* This also compiles them */
+static void vg_model_stream_materials( struct vg_model_stream_context *ctx, struct stack_allocator *stack )
+{
+   struct array_file_ptr mats_ptr;
+   af_load_array( &ctx->af, &mats_ptr, "mdl_material", stack, sizeof(struct mdl_material) );
+   ctx->model->materials = mats_ptr.data;
+   ctx->model->material_count = mats_ptr.count;
+
+   u32 size = sizeof(union shader_props) * mats_ptr.count;
+   ctx->model->shader_props = stack_allocate( stack, size, 8, "Compiled shader properties" );
+
+   /* 
+    * Step 0:
+    *  Acquiring the data source
+    *
+    * Step 1:
+    *  Converting into formal KV structure
+    *  We have 3 different modes;
+    *    v101+: old simple binary structures, requires 'generating' correct kvs
+    *    v106+: deprecated 'vg_msg' similar to kvs, only requires conversion
+    *    v110+: text KV's, direct parsing into vg_kvs
+    *
+    * Step 2:
+    *  Formal KV structure is then compiled into the binary union
+    */
+
+   /* step0 ----------------------------- */
+   u32 temp_frame = _start_temporary_frame();
+   {
+#if (VG_MODEL_VERSION_MIN <= 101)
+      struct array_file_ptr v101_materials;
+#endif
+#if (VG_MODEL_VERSION_MIN <= 106)
+      struct array_file_ptr v106_data;
+#endif
+      struct array_file_ptr v110_data;
+
+      if( ctx->model->version <= 105 )
+#if (VG_MODEL_VERSION_MIN <= 105)
+         af_load_array( &ctx->af, &v101_materials, "mdl_material", _temporary_stack_allocator(), sizeof(struct mdl_material_v101) );
+#else
+      {
+         $log( $fatal, {"Unsupported model version: "}, $unsigned( ctx->model->version ) );
+      }
+#endif
+#if (VG_MODEL_VERSION_MIN <= 109)
+      else if( ctx->model->version <= 109 )
+         af_load_array( &ctx->af, &v106_data, "shader_data", _temporary_stack_allocator(), 1 );
+#endif
+      else
+         af_load_array( &ctx->af, &v110_data, "shader_props", _temporary_stack_allocator(), 1 );
+
+      struct keyvalues kvs;
+      keyvalues_init( &kvs, _temporary_stack_allocator() );
+
+      /* step1 ----------------------------- */
+      if( ctx->model->version <= 105 )
+      {
+#if (VG_MODEL_VERSION_MIN <= 105)
+         for( u32 i=0; i<ctx->model->material_count; i ++ )
+         {
+            struct mdl_material *mat = &ctx->model->materials[ i ];
+            struct mdl_material_v101 *old = af_arritm( &v101_materials, i );
+
+            mat->props.kv_root = keyvalues_append_frame( &kvs, 0, NULL );
+
+            keyvalues_append_string( &kvs, mat->props.kv_root, "version", "101" );
+            keyvalues_append_u32s( &kvs, mat->props.kv_root, "tex_diffuse", &old->tex_diffuse, 1 );
+
+            if( mat->shader == k_shader_cubemap )
+            {
+               keyvalues_append_u32s( &kvs, mat->props.kv_root, "cubemap", &old->tex_none0, 1 );
+               keyvalues_append_f32s( &kvs, mat->props.kv_root, "tint", old->colour, 4 );
+            }
+            else if( mat->shader == k_shader_terrain_blend )
+            {
+               keyvalues_append_f32s( &kvs, mat->props.kv_root, "sand_colour", old->colour, 4 );
+               keyvalues_append_f32s( &kvs, mat->props.kv_root, "blend_offset", old->colour1, 2 );
+            }
+            else if( mat->shader == k_shader_standard_vertex_blend )
+            {
+               keyvalues_append_f32s( &kvs, mat->props.kv_root, "blend_offset", old->colour1, 2 );
+            }
+            else if( mat->shader == k_shader_water )
+            {
+               keyvalues_append_f32s( &kvs, mat->props.kv_root, "shore_colour", old->colour, 4 );
+               keyvalues_append_f32s( &kvs, mat->props.kv_root, "deep_colour", old->colour1, 4 );
+            }
+         }
+#else
+         ASSERT_CRITICAL( 0 );
+#endif
+      }
+      else if( ctx->model->version <= 109 )
+      {
+#if (VG_MODEL_VERSION_MIN <= 109)
+         for( u32 i=0; i<ctx->model->material_count; i ++ )
+         {
+            struct mdl_material *mat = &ctx->model->materials[ i ];
+            u32 root = keyvalues_append_frame( &kvs, 0, NULL );
+            keyvalues_append_string( &kvs, root, "version", "106" );
+
+            void *buffer = NULL;
+            if( v106_data.data )
+               buffer = v106_data.data + mat->props.kvs.offset;
+
+            vg_kvs_append_from_legacy_msg2( &kvs, root, buffer, mat->props.kvs.size );
+            mat->props.kv_root = root;
+         }
+#else
+         ASSERT_CRITICAL( 0 );
+#endif
+      }
+      else
+      {
+         for( u32 i=0; i<ctx->model->material_count; i ++ )
+         {
+            struct mdl_material *mat = &ctx->model->materials[ i ];
+            u32 root = keyvalues_append_frame( &kvs, 0, NULL );
+            keyvalues_append_string( &kvs, root, "version", "110" );
+
+            const c8 *buffer = NULL;
+            if( v110_data.data )
+            {
+               buffer = v110_data.data + mat->props.kvs.offset;
+               struct stream kv_stream;
+               stream_open_buffer_read( &kv_stream, buffer, mat->props.kvs.size, 0 );
+               keyvalues_parse_stream( &kvs, root, &kv_stream );
+            }
+            mat->props.kv_root = root;
+         }
+      }
+
+      /* step2 ----------------------------- */
+      for( u32 i=0; i<ctx->model->material_count; i ++ )
+      {
+         struct mdl_material *mat = &ctx->model->materials[ i ];
+         u32 root = mat->props.kv_root;
+         union shader_props *props = &ctx->model->shader_props[ i ];
+
+         if( mat->shader == k_shader_standard || 
+             mat->shader == k_shader_standard_cutout || 
+             mat->shader == k_shader_foliage ||
+             mat->shader == k_shader_fxglow )
+         {
+            keyvalues_read_u32s( &kvs, root, "tex_diffuse", (u32[]){0}, &props->standard.tex_diffuse, 1 );
+            keyvalues_read_u32s( &kvs, root, "render_flags", (u32[]){0}, &props->standard.render_flags, 1 );
+         }
+         else if( mat->shader == k_shader_standard_vertex_blend )
+         {
+            keyvalues_read_u32s( &kvs, root, "tex_diffuse", (u32[]){0}, &props->vertex_blend.tex_diffuse, 1 );
+            keyvalues_read_f32s( &kvs, root, "blend_offset", (f32[]){ 0.5, 0.0 }, props->vertex_blend.blend_offset, 2 );
+         }
+         else if( mat->shader == k_shader_cubemap )
+         {
+            keyvalues_read_u32s( &kvs, root, "tex_diffuse", (u32[]){0}, &props->cubemapped.tex_diffuse, 1 );
+            keyvalues_read_u32s( &kvs, root, "cubemap_entity", (u32[]){0}, &props->cubemapped.cubemap_entity, 1 );
+            keyvalues_read_f32s( &kvs, root, "tint", (f32[]){1.0,1.0,1.0,1.0}, props->cubemapped.tint, 4 );
+         }
+         else if( mat->shader == k_shader_terrain_blend )
+         {
+            keyvalues_read_u32s( &kvs, root, "tex_diffuse", (u32[]){0}, &props->terrain.tex_diffuse, 1 );
+            keyvalues_read_f32s( &kvs, root, "sand_colour", (f32[]){ 0.79, 0.63, 0.48, 1.0 }, props->terrain.sand_colour, 4 );
+            keyvalues_read_f32s( &kvs, root, "blend_offset", (f32[]){ 0.5, 0.0 }, props->terrain.blend_offset, 2 );
+         }
+         else if( mat->shader == k_shader_water )
+         {
+            keyvalues_read_f32s( &kvs, root, "shore_colour", (f32[]){0.03,0.32,0.61,1.0}, props->water.shore_colour, 4 );
+            keyvalues_read_f32s( &kvs, root, "deep_colour", (f32[]){0.0,0.006,0.03,1.0}, props->water.deep_colour, 4 );
+            keyvalues_read_f32s( &kvs, root, "fog_scale", (f32[]){0.04}, &props->water.fog_scale, 1 );
+            keyvalues_read_f32s( &kvs, root, "fresnel", (f32[]){5.0}, &props->water.fresnel, 1 );
+            keyvalues_read_f32s( &kvs, root, "water_scale", (f32[]){ 0.008 }, &props->water.water_sale, 1 );
+            keyvalues_read_f32s( &kvs, root, "wave_speed", (f32[]){0.008,0.006,0.003,0.03}, props->water.wave_speed, 4 );
+         }
+         else if( mat->shader == k_shader_workshop )
+         {
+            const c8 *_shader_prop_workshop_keys[] =
+            {
+               [k_workshop_shader_part_truck1  ] = "truck1",
+               [k_workshop_shader_part_truck2  ] = "truck2",
+               [k_workshop_shader_part_wheel1  ] = "wheel1",
+               [k_workshop_shader_part_wheel2  ] = "wheel2",
+               [k_workshop_shader_part_wheel3  ] = "wheel3",
+               [k_workshop_shader_part_wheel4  ] = "wheel4",
+               [k_workshop_shader_part_edge    ] = "edge",
+               [k_workshop_shader_part_griptape] = "griptape",
+               [k_workshop_shader_part_deck    ] = "deck"
+            };
+
+            for( u32 j=0; j<k_workshop_shader_part_max; j ++ )
+               keyvalues_read_u32s( &kvs, root, _shader_prop_workshop_keys[j], (u32[]){0}, &props->workshop.tex_all[j], 1 );
+         }
+      }
+   }
+   _end_temporary_frame( temp_frame );
+}
+
+void vg_model_stream_metadata( struct vg_model_stream_context *ctx, struct stack_allocator *stack )
+{
+   struct array_file_ptr strings;
+   af_load_array( &ctx->af, &strings, "strings", stack, 1 );
+   ctx->model->packed_strings = strings.data;
+   ctx->model->flags |= VG_MODEL_CPU_METADATA;
+
+   struct array_file_meta *pack = af_find_array( &ctx->af, "pack" );
+   if( pack ) ctx->model->pack_base_offset = pack->file_offset;
+   else       ctx->model->pack_base_offset = 0;
+
+   struct array_file_ptr ptr;
+   af_load_array( &ctx->af, &ptr, "mdl_mesh", stack, sizeof(struct mdl_mesh) );
+   ctx->model->meshes = ptr.data;
+   ctx->model->mesh_count = ptr.count;
+
+   af_load_array( &ctx->af, &ptr, "mdl_submesh", stack, sizeof(struct mdl_submesh) );
+   ctx->model->submeshes = ptr.data;
+   ctx->model->submesh_count = ptr.count;
+
+   af_load_array( &ctx->af, &ptr, "mdl_texture", stack, sizeof(union mdl_texture) );
+   ctx->model->textures = ptr.data;
+   ctx->model->texture_count = ptr.count;
+
+   af_load_array( &ctx->af, &ptr, "mdl_armature", stack, sizeof(struct mdl_armature) );
+   ctx->model->armatures = ptr.data;
+   ctx->model->armature_count = ptr.count;
+
+   af_load_array( &ctx->af, &ptr, "mdl_bone", stack, sizeof(struct mdl_bone) );
+   ctx->model->bones = ptr.data;
+   ctx->model->bone_count = ptr.count;
+
+   vg_model_stream_materials( ctx, stack );
+}
+
+void vg_model_stream_meshes_cpu( struct vg_model_stream_context *ctx, struct stack_allocator *stack )
+{
+   ASSERT_CRITICAL( ctx->model->flags & VG_MODEL_CPU_METADATA );
+   ctx->model->flags |= VG_MODEL_CPU_MESHES;
+
+   struct array_file_ptr ptr;
+   af_load_array( &ctx->af, &ptr, "mdl_vert", stack, sizeof(struct mdl_vert) );
+   ctx->model->verts = ptr.data;
+   ctx->model->vert_count = ptr.count;
+
+   af_load_array( &ctx->af, &ptr, "mdl_indice", stack, sizeof(u32) );
+   ctx->model->indices = ptr.data;
+   ctx->model->indice_count = ptr.count;
+}
+
+struct model_upload_task
+{
+   struct vg_model *model;
+   struct mdl_vert *vert_buffer;
+   u32 *indice_buffer;
+};
+
+static void vg_model_upload_task( struct task *task )
+{
+   struct model_upload_task *in_args = task_buffer(task);
+   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_MAIN ) );
+
+   glGenVertexArrays( 1, &in_args->model->vao );
+   glBindVertexArray( in_args->model->vao );
+   glGenBuffers( 1, &in_args->model->vbo );
+   glGenBuffers( 1, &in_args->model->ebo );
+
+   u32 stride = sizeof(struct mdl_vert);
+   glBindBuffer( GL_ARRAY_BUFFER, in_args->model->vbo );
+   glBufferData( GL_ARRAY_BUFFER, in_args->model->vert_count*stride, in_args->vert_buffer, GL_STATIC_DRAW );
+
+   glBindVertexArray( in_args->model->vao );
+   glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, in_args->model->ebo );
+   glBufferData( GL_ELEMENT_ARRAY_BUFFER, in_args->model->indice_count*sizeof(u32), 
+                                          in_args->indice_buffer, GL_STATIC_DRAW );
+   
+   /* 0: coordinates */
+   glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0 );
+   glEnableVertexAttribArray( 0 );
+
+   /* 1: normal */
+   glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, stride, (void *)offsetof(struct mdl_vert, norm) );
+   glEnableVertexAttribArray( 1 );
+
+   /* 2: uv */
+   glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE, stride, (void *)offsetof(struct mdl_vert, uv) );
+   glEnableVertexAttribArray( 2 );
+
+   /* 3: colour */
+   glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void *)offsetof(struct mdl_vert, colour) );
+   glEnableVertexAttribArray( 3 );
+
+   /* 4: weights */
+   glVertexAttribPointer( 4, 4, GL_UNSIGNED_SHORT, GL_TRUE, stride, (void *)offsetof(struct mdl_vert, weights) );
+   glEnableVertexAttribArray( 4 );
+
+   /* 5: groups */
+   glVertexAttribIPointer( 5, 4, GL_UNSIGNED_BYTE, stride, (void *)offsetof(struct mdl_vert, groups) );
+   glEnableVertexAttribArray( 5 );
+}
+
+void vg_model_stream_meshes_gpu( struct vg_model_stream_context *ctx, u32 *fixup_table )
+{
+   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
+   ASSERT_CRITICAL( ctx->model->flags & VG_MODEL_CPU_METADATA );
+   ctx->model->flags |= VG_MODEL_GPU_MESHES;
+
+   /* NOTE: We could check here if we already have CPU meshes and use those buffers. 
+    *       In very rare instances (1 time) we use both paths. */
+
+   struct array_file_meta *arr_vertices = af_find_array( &ctx->af, "mdl_vert" );
+   struct array_file_meta *arr_indices = af_find_array( &ctx->af, "mdl_indice" );
+
+   if( arr_vertices && arr_indices )
+   {
+      u32 size_verts   = PAD_TO_8(sizeof(struct mdl_vert)*arr_vertices->item_count),
+          size_indices = PAD_TO_8(sizeof(u32)*arr_indices->item_count),
+          size_hdr     = PAD_TO_8(sizeof(struct model_upload_task)),
+          total        = size_hdr + size_verts + size_indices;
+
+      struct task *upload_task = _task_new( k_thread_main, total, 0, "Model upload to GPU task" );
+      struct model_upload_task *args = task_buffer( upload_task );
+
+      args->model = ctx->model;
+      args->vert_buffer   = ((void *)args) + size_hdr;
+      args->indice_buffer = ((void *)args) + size_hdr + size_verts;
+      ctx->model->vert_count = arr_vertices->item_count;
+      ctx->model->indice_count = arr_indices->item_count;
+
+      af_load_array_file_buffer( &ctx->af, arr_vertices, args->vert_buffer, sizeof(struct mdl_vert) );
+      af_load_array_file_buffer( &ctx->af, arr_indices, args->indice_buffer, sizeof(u32) );
+
+      if( fixup_table )
+      {
+         for( u32 i=0; i<ctx->model->vert_count; i ++ )
+         {
+            struct mdl_vert *vert = &args->vert_buffer[i];
+            for( u32 j=0; j<4; j++ )
+               vert->groups[j] = fixup_table[vert->groups[j]];
+         }
+      }
+
+      /*
+       * Unpack the indices (if there are meshes)
+       * ---------------------------------------------------------
+       */
+      if( ctx->model->submesh_count )
+      {
+         struct mdl_submesh *sm = &ctx->model->submeshes[ 0 ];
+         u32 offset = sm->vertex_count;
+
+         for( u32 i=1; i<ctx->model->submesh_count; i++ )
+         {
+            struct mdl_submesh *sm = &ctx->model->submeshes[ i ];
+            u32           *indices = args->indice_buffer + sm->indice_start;
+
+            for( u32 j=0; j<sm->indice_count; j++ )
+               indices[j] += offset;
+            offset += sm->vertex_count;
+         }
+      }
+
+      task_send( upload_task, vg_model_upload_task );
+   }
+   else
+   {
+      $log( $fatal, {"No vertex/indice data in model file"} );
+      _fatal_exit();
+   }
+}
+
+void vg_model_stream_textures_gpu( struct vg_model_stream_context *ctx )
+{
+   ASSERT_CRITICAL( ctx->model->flags & VG_MODEL_CPU_METADATA );
+   ctx->model->flags |= VG_MODEL_GPU_TEXTURES;
+   for( u32 i=0; i<ctx->model->texture_count; i ++ )
+   {
+      union mdl_texture *tex = &ctx->model->textures[ i ];
+      struct mdl_file pack_info = tex->file;
+      _vg_tex_load_stream( &tex->tex, vg_model_stream_pack_stream( ctx, &pack_info ), 
+                           VG_TEX_REPEAT | VG_TEX_NEAREST | VG_TEX_NOMIP );
+   }
+}
+
+b8 vg_model_stream_open( struct vg_model_stream_context *ctx, struct vg_model *model, const c8 *path )
+{
+   zero_buffer( ctx, sizeof(struct vg_model_stream_context) );
+   zero_buffer( model, sizeof(struct vg_model) );
+   if( stream_open_file( &ctx->stream, path, k_stream_read ) )
+   {
+      ctx->model = model;
+      ctx->temp_frame = _start_temporary_frame();
+      if( !af_open_stream( &ctx->af, &ctx->stream, VG_MODEL_VERSION_MIN, VG_MODEL_VERSION_NR, _temporary_stack_allocator()) )
+      {
+         stream_close( &ctx->stream );
+         _end_temporary_frame( ctx->temp_frame );
+         return 0;
+      }
+      ctx->model->version = ctx->af.header.version;
+      return 1;
+   }
+   else return 0;
+}
+
+void vg_model_stream_close( struct vg_model_stream_context *ctx )
+{
+   stream_close( &ctx->stream );
+   _end_temporary_frame( ctx->temp_frame );
+}
+
+b8 vg_model_load( struct vg_model *model, u32 model_flags, const c8 *path, struct stack_allocator *stack )
+{
+   b8 success = 0;
+   model_flags |= VG_MODEL_CPU_METADATA;
+
+   struct vg_model_stream_context ctx;
+   if( vg_model_stream_open( &ctx, model, path ) )
+   {
+      if( model_flags & VG_MODEL_CPU_METADATA )
+         vg_model_stream_metadata( &ctx, stack );
+
+      if( model_flags & VG_MODEL_CPU_MESHES )
+         vg_model_stream_meshes_cpu( &ctx, stack );
+
+      ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
+      if( model_flags & VG_MODEL_GPU_MESHES )
+         vg_model_stream_meshes_gpu( &ctx, NULL );
+
+      if( model_flags & VG_MODEL_GPU_TEXTURES )
+         vg_model_stream_textures_gpu( &ctx );
+
+      vg_model_stream_close( &ctx );
+      success = 1;
+   }
+   return success;
+}
+
+void vg_model_unload_gpu( struct vg_model *model )
+{
+   if( model->flags & VG_MODEL_GPU_MESHES )
+   {
+      model->flags &= ~(u32)(VG_MODEL_GPU_MESHES);
+      glDeleteVertexArrays( 1, &model->vao );
+      glDeleteBuffers( 1, &model->ebo );
+      glDeleteBuffers( 1, &model->vbo );
+   }
+
+   if( model->flags & VG_MODEL_GPU_TEXTURES )
+      for( u32 i=0; i<model->texture_count; i ++ )
+         vg_tex_delete( &model->textures[i].tex );
+}
+
+void mdl_transform_m4x3( struct mdl_transform *transform, f32 mtx[4][3] )
+{
+   q_m3x3( transform->q, mtx );
+   v3_muls( mtx[0], transform->s[0], mtx[0] );
+   v3_muls( mtx[1], transform->s[1], mtx[1] );
+   v3_muls( mtx[2], transform->s[2], mtx[2] );
+   v3_copy( transform->co, mtx[3] );
+}
+
+void mdl_transform_identity( struct mdl_transform *transform )
+{
+   v3_fill( transform->co, 0 );
+   q_identity( transform->q );
+   v3_fill( transform->s, 1.0f );
+}
+
+void mdl_transform_vector( struct mdl_transform *transform, f32 vec[3], f32 dest[3] )
+{
+   v3_mul( transform->s, vec, dest );
+   q_mulv( transform->q, dest, dest );
+}
+
+void mdl_transform_point( struct mdl_transform *transform, f32 co[3], f32 dest[3] )
+{
+   mdl_transform_vector( transform, co, dest );
+   v3_add( transform->co, dest, dest );
+}
+
+void mdl_transform_mul( struct mdl_transform *a, struct mdl_transform *b, struct mdl_transform *d )
+{
+   mdl_transform_point( a, b->co, d->co );
+   q_mul( a->q, b->q, d->q );
+   q_normalize( d->q );
+   v3_mul( a->s, b->s, d->s );
+}
+
+void vg_model_bind_mesh( struct vg_model *model )
+{
+   glBindVertexArray( model->vao );
+}
+
+void vg_model_draw_elements( u32 start, u32 count )
+{
+   glDrawElements( GL_TRIANGLES, count, GL_UNSIGNED_INT, (void *)(start*sizeof(u32)) );
+}
+
+void vg_model_draw_submesh( struct mdl_submesh *sm )
+{
+   vg_model_draw_elements( sm->indice_start, sm->indice_count );
+}
+
+i32 vg_model_get_mesh_index( struct vg_model *model, const c8 *name )
+{
+   u32 hash = buffer_djb2( name, 0 );
+   for( u32 i=0; i<model->mesh_count; i++ )
+   {
+      struct mdl_mesh *mesh = &model->meshes[ i ];
+      if( af_str_eq( model->packed_strings, mesh->pstr_name, name, hash ) ) 
+         return i;
+   }
+   return -1;
+}
+
+i32 vg_model_get_submesh_index( struct vg_model *model, const c8 *mesh_name )
+{
+   i32 mesh_index = vg_model_get_mesh_index( model, mesh_name );
+   if( mesh_index == -1 )
+      return -1;
+
+   struct mdl_mesh *mesh = &model->meshes[ mesh_index ];
+   if( !mesh->submesh_count ) 
+      return -1;
+   return  mesh->submesh_start;
+}
diff --git a/source/engine/model.h b/source/engine/model.h
new file mode 100644 (file)
index 0000000..fe90a95
--- /dev/null
@@ -0,0 +1,274 @@
+#pragma once
+#include "vg_tex.h"
+#include "shader_props.h"
+#include "array_file.h"
+
+#define VG_MODEL_VERSION_MIN 110
+#define VG_MODEL_VERSION_NR 110
+
+enum mdl_shader
+{
+   k_shader_standard                = 0,
+   k_shader_standard_cutout         = 1,
+   k_shader_terrain_blend           = 2,
+   k_shader_standard_vertex_blend   = 3,
+   k_shader_water                   = 4,
+   k_shader_invisible               = 5,
+   k_shader_boundary                = 6,
+   k_shader_fxglow                  = 7,
+   k_shader_cubemap                 = 8,
+   k_shader_walking                 = 9,
+   k_shader_foliage                 = 10,
+   k_shader_workshop                = 11,
+   k_shader_override                = 30000
+};
+
+enum mdl_surface_prop
+{
+   k_surface_prop_concrete = 0,
+   k_surface_prop_wood     = 1,
+   k_surface_prop_grass    = 2,
+   k_surface_prop_tiles    = 3,
+   k_surface_prop_metal    = 4,
+   k_surface_prop_snow     = 5,
+   k_surface_prop_sand     = 6
+};
+
+enum material_flag
+{
+   k_material_flag_skate_target     = 0x0001,
+   k_material_flag_collision        = 0x0002,
+   k_material_flag_grow_grass       = 0x0004,
+   k_material_flag_grindable        = 0x0008,
+   k_material_flag_invisible        = 0x0010,
+   k_material_flag_boundary         = 0x0020,
+   k_material_flag_preview_visibile = 0x0040,
+   k_material_flag_walking          = 0x0080,
+
+   k_material_flag_ghosts      =
+      k_material_flag_boundary|
+      k_material_flag_invisible|
+      k_material_flag_walking
+};
+
+#pragma pack(push,1)
+
+/* 48 byte */
+struct mdl_vert
+{
+   f32 co[3],     /* 3*32 */
+       norm[3];   /* 3*32 */
+   f32 uv[2];     /* 2*32 */
+
+   u8  colour[4]; /* 4*8 */
+   u16 weights[4];/* 4*16 */
+   u8  groups[4]; /* 4*8 */
+};
+
+#pragma pack(pop)
+
+struct mdl_transform
+{
+   f32 co[3], s[3];
+   f32 q[4];
+};
+
+void mdl_transform_identity( struct mdl_transform *transform );
+void mdl_transform_vector( struct mdl_transform *transform, f32 vec[3], f32 dest[3] );
+void mdl_transform_point( struct mdl_transform *transform, f32 co[3], f32 dest[3] );
+void mdl_transform_mul( struct mdl_transform *a, struct mdl_transform *b, struct mdl_transform *d );
+
+#if (VG_MODEL_VERSION_MIN <= 105)
+struct mdl_material_v101
+{
+   u32 pstr_name,
+       shader,
+       flags,
+       surface_prop;
+
+   f32 colour[4],
+       colour1[4];
+
+   u32 tex_diffuse, /* Indexes start from 1. 0 if missing. */
+       tex_none0,
+       tex_none1;
+};
+#endif
+
+struct mdl_material
+{
+   u32 pstr_name,
+       shader,
+       flags,
+       surface_prop;
+
+   union 
+   {
+      struct 
+      {
+         u32 offset, size; /* indexes shader data */
+      }
+      kvs;
+      u32 kv_root; /* runtime */
+   } 
+   props;
+};
+
+struct mdl_bone
+{
+   f32 co[3], end[3];
+   u32 parent,
+       collider,
+       ik_target,
+       ik_pole,
+       flags,
+       pstr_name;
+
+   f32 hitbox[2][3];
+   f32 conevx[3], conevy[3], coneva[3];
+   f32 conet;
+};
+
+enum bone_flag
+{
+   k_bone_flag_deform               = 0x00000001,
+   k_bone_flag_ik                   = 0x00000002,
+   k_bone_flag_cone_constraint      = 0x00000004
+};
+
+enum bone_collider
+{
+   k_bone_collider_none = 0,
+   k_bone_collider_box = 1,
+   k_bone_collider_capsule = 2
+};
+         
+struct mdl_armature
+{
+   struct mdl_transform transform;
+   u32 bone_start,
+       bone_count,
+       anim_start_OBSOLETE_107, // obsolete 107+
+       anim_count_OBSOLETE_107, // .
+       pstr_name; // v107+
+};
+
+struct mdl_submesh
+{
+   u32 indice_start,
+       indice_count,
+       vertex_start,
+       vertex_count;
+
+   f32 bbx[2][3];
+   u16 material_id, flags;
+};
+
+enum esubmesh_flags
+{
+   k_submesh_flag_none     = 0x0000,
+   k_submesh_flag_consumed = 0x0001
+};
+
+struct mdl_mesh
+{
+   struct mdl_transform transform;
+   u32 submesh_start,
+       submesh_count,
+       pstr_name,
+       entity_id,    /* upper 16 bits: type, lower 16 bits: index. hgn: 11.06.2025 Is this still used??? */
+       armature_id;
+};
+
+struct mdl_file
+{
+   u32 pstr_path,
+       pack_offset,
+       pack_size;
+};
+
+union mdl_texture
+{
+   struct mdl_file file;
+   struct vg_tex tex;
+};
+
+struct vg_model
+{
+   u32 version;
+   u32 flags;
+
+   /* VG_MODEL_CPU_METADATA ---------------------- */
+   const void *packed_strings;
+   union shader_props *shader_props;
+
+   struct mdl_mesh *meshes;
+   u32 mesh_count;
+
+   struct mdl_submesh *submeshes;
+   u32 submesh_count;
+
+   struct mdl_material *materials;
+   u32 material_count;
+
+   union mdl_texture *textures;
+   u32 texture_count;
+
+   struct mdl_armature *armatures;
+   u32 armature_count;
+
+   struct mdl_bone *bones;
+   u32 bone_count;
+
+   /* VG_MODEL_CPU_MESHES ---------------------- */
+   struct mdl_vert *verts;
+   u32 vert_count;
+   u32 *indices;
+   u32 indice_count;
+
+   u32 pack_base_offset;
+
+   /* VG_MODEL_GPU_MESHES ----------------------- */
+   GLuint vao, vbo, ebo;
+};
+
+#define VG_MODEL_CPU_METADATA   0x4
+#define VG_MODEL_CPU_MESHES     0x8
+# define VG_MODEL_GPU_TEXTURES   0x1
+# define VG_MODEL_GPU_MESHES     0x2
+# define VG_MODEL_ENGINE_STANDARD (VG_MODEL_CPU_METADATA|VG_MODEL_GPU_TEXTURES|VG_MODEL_GPU_MESHES)
+# define VG_MODEL_ENGINE_PROCEDURAL_SOURCE (VG_MODEL_CPU_METADATA|VG_MODEL_GPU_TEXTURES|VG_MODEL_CPU_MESHES)
+
+b8 vg_model_load( struct vg_model *model, u32 model_flags, const c8 *path, struct stack_allocator *stack );
+void vg_model_unload_gpu( struct vg_model *model );
+
+/* Sub functions for each part of the load process
+ * ---------------------------------------------------------------------------------------------------------------  */
+struct vg_model_stream_context
+{
+   struct stream stream;
+   struct array_file_context af;
+   struct vg_model *model;
+   u32 temp_frame;
+};
+
+b8 vg_model_stream_open( struct vg_model_stream_context *ctx, struct vg_model *model, const c8 *path );
+void vg_model_stream_close( struct vg_model_stream_context *ctx );
+
+/* Parts which you might want (currently they all require metadata to be explicitly loaded) */
+void vg_model_stream_metadata( struct vg_model_stream_context *ctx, struct stack_allocator *stack );
+void vg_model_stream_meshes_cpu( struct vg_model_stream_context *ctx, struct stack_allocator *stack );
+
+void vg_model_stream_meshes_gpu( struct vg_model_stream_context *ctx, u32 *fixup_table );
+void vg_model_stream_textures_gpu( struct vg_model_stream_context *ctx );
+
+struct stream *vg_model_stream_pack_stream( struct vg_model_stream_context *ctx, struct mdl_file *file );
+
+/* Rendering operations
+ * ----------------------------------------------------------------------------------------------------------------- */
+void vg_model_bind_mesh( struct vg_model *model );
+void vg_model_draw_elements( u32 start, u32 count );
+void vg_model_draw_submesh( struct mdl_submesh *sm );
+i32  vg_model_get_mesh_index( struct vg_model *model, const c8 *name );
+i32  vg_model_get_submesh_index( struct vg_model *model, const c8 *mesh_name );
+void mdl_transform_m4x3( struct mdl_transform *transform, f32 mtx[4][3] );
diff --git a/source/engine/model_compiler.c b/source/engine/model_compiler.c
new file mode 100644 (file)
index 0000000..7760939
--- /dev/null
@@ -0,0 +1,135 @@
+#include "compiler.h"
+
+void mdl_compiler_init( struct mdl_compiler *compiler )
+{
+   stack_init( &compiler->stack, NULL, VG_MB(10), "MDL Compiler" );
+   af_compiler_init( &compiler->af, &compiler->stack );
+   compiler->meshes = af_compiler_create_index( &compiler->af, "mdl_mesh", sizeof(struct mdl_mesh) );
+   compiler->submeshes = af_compiler_create_index( &compiler->af, "mdl_submesh", sizeof(struct mdl_submesh) );
+   compiler->vertices = af_compiler_create_index( &compiler->af, "mdl_vert", sizeof(struct mdl_vert) );
+   compiler->indices = af_compiler_create_index( &compiler->af, "mdl_indice", sizeof(u32) );
+   compiler->bones = af_compiler_create_index( &compiler->af, "mdl_bone", sizeof(struct mdl_bone) );
+   compiler->materials = af_compiler_create_index( &compiler->af, "mdl_material", sizeof(struct mdl_material) );
+   compiler->shader_data = af_compiler_create_index( &compiler->af, "shader_data", 1 );
+   compiler->armatures = af_compiler_create_index( &compiler->af, "mdl_armature", sizeof(struct mdl_armature) );
+   compiler->textures = af_compiler_create_index( &compiler->af, "mdl_texture", sizeof(struct mdl_texture) );
+   compiler->pack_data = af_compiler_create_index( &compiler->af, "pack", 1 );
+}
+
+u32 mdl_compiler_push_entity( struct mdl_compiler *compiler, u32 entity_type, const c8 *entity_type_string,
+                              void *data, u32 data_size )
+{
+   struct af_compiler_index *index = af_get_or_make_index( &compiler->af, entity_type_string, data_size );
+
+   u32 index_part = index->element_count,
+       id = (entity_type & 0xfffff)<<16 | (index_part & 0xfffff); //TODO
+
+   struct af_compiler_item *item = af_compiler_allocate_items( &compiler->af, index, 1 );
+   buffer_copy( item->data, data_size, data, data_size );
+   return jd;
+}
+
+void mdl_compiler_start_mesh( struct mdl_compiler *compiler, const c8 *name, u32 associated_entity, u32 associated_armature )
+{
+   struct mdl_mesh *mesh = af_compiler_allocate_items( &compiler->af, compiler->meshes, 1 )->data;
+   mdl_transform_identity( &mesh->transform );
+   mesh->submesh_start = compiler->submeshes->element_count;
+   mesh->submesh_count = 0;
+   mesh->pstr_name = af_compile_string( &compiler->af, name );
+   mesh->entity_id = associated_entity;
+   mesh->armature_id = associated_armature;
+}
+
+void mdl_compiler_start_submesh( struct mdl_compiler *compiler, u32 material_id, u32 flags )
+{
+   struct mdl_mesh *current_mesh = compiler->meshes->last->data;
+   current_mesh->submesh_count ++;
+
+   struct mdl_submesh *sm = af_compiler_allocate_items( &compiler->af, compiler->submeshes, 1 )->data;
+   sm->indice_start = compiler->indices->element_count;
+   sm->vertex_start = compiler->vertices->element_count;
+   sm->indice_count = 0;
+   sm->vertex_count = 0;
+   sm->material_id = material_id;
+   sm->flags = flags;
+   box_init_inf( sm->bbx );
+}
+
+void mdl_compiler_push_meshdata( struct mdl_compiler *compiler, struct mdl_vert *vertex_buffer, u32 vertex_count,
+                                 u32 *indice_buffer, u32 indice_count )
+{
+   struct mdl_submesh *current_submesh = compiler->submeshes->last->data;
+   current_submesh->vertex_count += vertex_count;
+   current_submesh->indice_count += indice_count;
+
+   struct mdl_vert *dest_verts = af_compiler_allocate_items( &compiler->af, compiler->vertices, vertex_count )->data;
+   u32 *dest_indices = af_compiler_allocate_items( &compiler->af, compiler->indices, indice_count )->data;
+
+   for( u32 i=0; i<vertex_count; i ++ )
+      box_addpt( current_submesh->bbx, vertex_buffer[i].co );
+
+   u32 vert_size = vertex_count * sizeof(mdl_vert);
+   buffer_copy( vertex_buffer, vert_size, dest_verts, vert_size );
+
+   u32 indice_size = indice_count * sizeof(u32);
+   buffer_copy( indice_buffer, indice_size, dest_indices, indice_size );
+}
+
+static void mdl_compiler_pack_data( struct mdl_compiler *compiler, const c8 *path, void *data, 
+                                    u32 data_length, struct mdl_file *out_file )
+{
+   out_file->pstr_path = af_compile_string( &compiler->af, path );
+   out_file->pack_offset = compiler->pack_data->element_count;
+   out_file->pack_size = data_length;
+   void *dest = af_compiler_allocate_items( &compiler->af, compiler->pack_data, vg_align16(data_length) )->data;
+   buffer_copy( data, data_length, dest, data_length );
+}
+
+u32 mdl_compiler_start_material( struct mdl_compiler *compiler, const c8 *name )
+{
+   u32 material_id = compiler->materials->element_count + 1;
+
+   struct mdl_material *material = af_compiler_allocate_items( &compiler->af, compiler->materials, 1 )->data;
+   material->pstr_name = af_compile_string( &compiler->af, name );
+   material->shader = 0;
+   material->flags = 0;
+   material->surface_prop = 0;
+   material->props.kvs.offset = 0;
+   material->props.kvs.size = 0;
+   return material_id;
+}
+
+void mdl_compiler_set_surface_info( struct mdl_compiler *compiler, u32 flags, u32 surface_prop )
+{
+   struct mdl_material *current_material = compiler->materials->last->data;
+   current_material->flags = flags;
+   current_material->surface_prop = surface_prop;
+}
+
+u32 mdl_compiler_compile_texture_qoi( struct mdl_compiler *compiler, const c8 *name, void *data, u32 data_len )
+{
+   u32 texture_id = compiler->textures->element_count + 1;
+   struct mdl_texture *texture = af_compiler_allocate_items( &compiler->af, compiler->textures, 1 )->data;
+   mdl_compiler_pack_data( compiler, name, data, data_len, &texture->file );
+   return texture_id;
+}
+
+void mdl_compiler_push_shaderdata( struct mdl_compiler *compiler, u32 shader_id, struct keyvalues *shader_kvs )
+{
+   struct mdl_material *current_material = compiler->materials->last->data;
+   current_material->shader = shader_id;
+   current_material->props.kvs.offset = compiler->shader_data->element_count;
+
+   // FIXME FIXME FIXME FIXME FIXME NEED TO FLATTEN OUT THE KVS INTO STRINGYMABOB!!!!!!!!!!!!!!!!
+#if 0
+   current_material->props.kvs.size = shader_kvs->cur.co;
+
+   void *dest = af_compiler_allocate_items( &compiler->af, compiler->shader_data, vg_align8(shader_kvs->cur.co) )->data;
+   memcpy( dest, shader_kvs->buf, shader_kvs->cur.co );
+#endif
+}
+
+void mdl_compiler_free( struct mdl_compiler *compiler )
+{
+   stack_free( &compiler->stack );
+}
diff --git a/source/engine/model_compiler.h b/source/engine/model_compiler.h
new file mode 100644 (file)
index 0000000..7435de4
--- /dev/null
@@ -0,0 +1,15 @@
+struct mdl_compiler
+{
+   struct af_compiler af;
+   struct af_compiler_index *meshes,
+                            *submeshes,
+                            *vertices,
+                            *indices,
+                            *bones,
+                            *materials,
+                            *shader_data,
+                            *armatures,
+                            *textures,
+                            *pack_data;
+   struct stack_allocator stack;
+};
diff --git a/source/engine/model_entity.h b/source/engine/model_entity.h
new file mode 100644 (file)
index 0000000..65e26af
--- /dev/null
@@ -0,0 +1,751 @@
+#pragma once
+#include "model.h"
+
+enum entity_alias
+{
+   k_ent_none        = 0,
+   k_ent_gate        = 1,
+   k_ent_spawn       = 2,
+   k_ent_route_node  = 3,
+   k_ent_route       = 4,
+   k_ent_water       = 5,
+   k_ent_volume      = 6,
+   k_ent_audio       = 7,
+   k_ent_marker      = 8,
+   k_ent_font        = 9,
+   k_ent_font_variant= 10,
+   k_ent_traffic     = 11,
+   k_ent_skateshop   = 12,
+   k_ent_camera      = 13,
+   k_ent_swspreview  = 14,
+   k_ent_deleted1    = 15, //k_ent_menuitem    = 15,
+   k_ent_worldinfo   = 16,
+   k_ent_ccmd        = 17,
+   k_ent_objective   = 18,
+   k_ent_challenge   = 19,
+   k_ent_deleted0    = 20, //k_ent_relay       = 20,
+   k_ent_cubemap     = 21,
+   k_ent_miniworld   = 22,
+   k_ent_prop        = 23,
+   k_ent_list        = 24,
+   k_ent_region      = 25,
+   k_ent_glider      = 26,
+   k_ent_npc         = 27,
+   k_ent_armature    = 28,
+   k_ent_script      = 29, 
+   k_ent_atom        = 30,
+   k_ent_cutscene    = 31,
+   k_ent_light       = 32,
+   k_mdl_mesh        = 33,
+   k_editer_property = 34,
+   k_editer_item     = 35,
+   k_ent_max
+};
+
+const char *_entity_alias_str[] =
+{
+   [k_ent_none] = "none/null",
+   [k_ent_gate] = "ent_gate",
+   [k_ent_spawn] = "ent_spawn",
+   [k_ent_route_node] = "ent_route_node",
+   [k_ent_route] = "ent_route",
+   [k_ent_water] = "ent_water",
+   [k_ent_volume] = "ent_volume",
+   [k_ent_audio] = "ent_audio",
+   [k_ent_marker] = "ent_marker",
+   [k_ent_font] = "ent_font",
+   [k_ent_font_variant] = "ent_font_variant",
+   [k_ent_traffic] = "ent_traffic",
+   [k_ent_skateshop] = "ent_skateshop",
+   [k_ent_camera] = "ent_camera",
+   [k_ent_swspreview] = "ent_swspreview",
+   //[k_ent_menuitem] = "ent_menuitem",
+   [k_ent_worldinfo] = "ent_worldinfo",
+   [k_ent_ccmd] = "ent_ccmd",
+   [k_ent_objective] = "ent_objective",
+   [k_ent_challenge] = "ent_challenge",
+   //[k_ent_relay] = "ent_relay",
+   [k_ent_cubemap] = "ent_cubemap",
+   [k_ent_miniworld] = "ent_miniworld",
+   [k_ent_prop] = "ent_prop",
+   [k_ent_region] = "ent_region",
+   [k_ent_glider] = "ent_glider",
+   [k_ent_npc] = "ent_npc",
+   [k_ent_armature] = "mdl_armature",
+   [k_ent_script] = "ent_script",
+   [k_ent_atom] = "ent_atom",
+   [k_ent_cutscene] = "ent_cutscene",
+   [k_ent_light] = "ent_light"
+};
+
+static inline u32 mdl_entity_id_type( u32 entity_id )
+{
+   return (entity_id & 0x0fff0000) >> 16;
+}
+
+static inline u32 mdl_entity_id_id( u32 entity_id )
+{
+   return entity_id & 0x0000ffff;
+}
+
+static inline u32 mdl_entity_id( u32 type, u32 index )
+{
+   return (type & 0xfffff)<<16 | (index & 0xfffff);
+}
+
+enum ent_spawn_flag
+{
+   k_ent_spawn_flag_locked = 0x1,
+   k_ent_spawn_flag_group_1 = 0x10,
+   k_ent_spawn_flag_group_2 = 0x20,
+   k_ent_spawn_flag_group_3 = 0x40,
+   k_ent_spawn_flag_group_4 = 0x80
+};
+
+struct ent_spawn
+{
+   struct mdl_transform transform;
+   u32 pstr_name;
+   u32 flags;
+};
+
+enum light_type
+{
+   k_light_type_point = 0,
+   k_light_type_spot = 1
+};
+
+struct ent_light
+{
+   struct mdl_transform transform;
+   u32 daytime,
+       type;
+   f32 colour[4];
+   f32 angle,
+       range;
+   f32 inverse_world[4][3];
+   f32 angle_sin_cos[2];
+};
+
+/* v101 */
+#if 0
+enum gate_type{
+   k_gate_type_unlinked = 0,
+   k_gate_type_teleport = 1,
+   k_gate_type_nonlocal_unlinked = 2,
+   k_gate_type_nonlocel = 3
+};
+#endif
+
+enum list_alias_type
+{
+   k_list_alias_none = 0,
+   k_list_alias_string = 1
+};
+
+struct ent_list
+{
+   u16 entity_ref_start, entity_ref_count;
+   u8  alias_type, none0, none1, none2;
+   u32 pstr_alias;
+};
+
+struct file_entity_ref
+{
+   u32 entity_id, pstr_alias;
+};
+
+/* v102+ */
+enum ent_gate_flag
+{
+   k_ent_gate_linked      = 0x1, /* this is a working portal */
+   k_ent_gate_nonlocal    = 0x2, /* use the key string to link this portal.
+                                       NOTE: if set, it adds the flip flag. */
+   k_ent_gate_flip        = 0x4, /* flip direction 180* for exiting portal */
+   k_ent_gate_custom_mesh = 0x8, /* use a custom submesh instead of default */
+   k_ent_gate_locked      = 0x10,/* has to be unlocked to be useful */
+
+   k_ent_gate_clean_pass  = 0x20,/* player didn't rewind while getting here */
+   k_ent_gate_no_linkback = 0x40,/* NONLOCAL Recievers are not allowed to linkback through this gate */
+   k_ent_gate_passive     = 0x80
+};
+
+struct ent_gate
+{
+   u32 flags,
+       target, 
+       key;
+
+   f32 dimensions[3],
+       co[2][3];
+
+   f32 q[2][4];
+
+   /* runtime */
+   f32 to_world[4][3], transport[4][3];
+   union
+   {
+      u32 timing_version;
+      u16 remote_addon_id;
+
+      struct
+      {
+         u8 ref_count;
+      };
+   };
+
+   union
+   {
+      f64 timing_time;
+      u32 cubemap_id;
+   };
+
+   u16 routes[4];       /* routes that pass through this gate */
+   u8 route_count;
+
+   /* v102+ */
+   u32 submesh_start, submesh_count;
+};
+
+struct ent_route_node
+{
+   f32 co[3];
+   u8 ref_count, ref_total;
+};
+
+struct ent_path_index
+{
+   u16 index;
+};
+
+struct ent_checkpoint
+{
+   u16 gate_index,
+       path_start,
+       path_count;
+
+   /* EXTENSION */
+   f32 best_time;
+};
+
+enum ent_route_flag 
+{
+   k_ent_route_flag_achieve_silver = 0x1,
+   k_ent_route_flag_achieve_gold   = 0x2,
+
+   k_ent_route_flag_out_of_zone    = 0x10,
+   k_ent_region_flag_hasname       = 0x20,
+   k_ent_route_flag_target         = 0x40
+};
+
+struct ent_route
+{
+   union
+   {
+      struct mdl_transform transform;
+      u32 official_track_id;   /* TODO: remove this */
+   }
+   anon;
+
+   u32 pstr_name;
+   u16 checkpoints_start,
+       checkpoints_count;
+
+   f32 colour[4];
+
+   /* runtime */
+   u16 active_checkpoint, 
+       valid_checkpoints;
+
+   f32 factive;
+   f32 board_transform[4][3];
+   struct mdl_submesh sm;
+   f64 timing_base;
+
+   u32 id_camera; /* v103+ */
+
+   /* v104+, but always accessible */
+   u32 flags;
+   f64 best_laptime;
+   f32 ui_stopper, ui_residual;
+   i16 ui_first_block_width, ui_residual_block_w;
+};
+
+struct ent_water
+{
+   struct mdl_transform transform;
+   f32 max_dist;
+   u32 reserved0, reserved1;
+};
+
+struct ent_audio_clip
+{
+   union
+   {
+      struct mdl_file file;
+      // struct audio_clip clip; FIXME, MUST ALLOCATE THIS LATER INSTEAD OF PUTTING IT IN THE FUCKING FILE STRUCT. WHO CARES IF WE LOSE HALF A KB
+   }
+   _;
+
+   f32 probability;
+};
+
+struct volume_particles
+{
+   u32 blank, blank2;
+};
+
+struct volume_trigger
+{
+   i32 blank, blank2;
+};
+
+struct volume_interact
+{
+   i32 blank;
+   u32 pstr_text;
+};
+
+enum ent_volume_flag
+{
+   k_ent_volume_flag_particles = 0x1,
+   k_ent_volume_flag_disabled  = 0x2,
+   k_ent_volume_flag_removed0  = 0x4,
+   k_ent_volume_flag_interact  = 0x8,
+   k_ent_volume_flag_water     = 0x10,
+   k_ent_volume_flag_repeatable= 0x20
+};
+
+struct ent_volume
+{
+   struct mdl_transform transform;
+   f32 to_world[4][3];
+
+   union
+   {
+      f32 to_local[4][3];
+      f32 particle_co[3];
+   };
+
+   u32 flags;
+   u32 deleted0;
+   union
+   {
+      struct volume_trigger trigger;
+      struct volume_interact interact;
+      struct volume_particles particles;
+   };
+};
+
+struct ent_audio
+{
+   struct mdl_transform transform;
+   u32 flags,
+       clip_start,
+       clip_count;
+   f32 volume, crossfade;
+   u32 behaviour,
+       group,
+       probability_curve,
+       max_channels;
+};
+
+enum
+{
+   k_ent_marker_flag_hidden = 0x1,
+   k_ent_marker_flag_gui_icon = 0x8
+};
+
+struct ent_marker
+{
+   struct mdl_transform transform;
+   u32 pstr_alias;
+   u32 flags;
+};
+
+enum skateshop_type
+{
+   k_skateshop_type_boardshop = 0,
+   k_skateshop_type_charshop = 1,
+   k_skateshop_type_worldshop = 2,
+   k_skateshop_type_DELETED = 3,
+   k_skateshop_type_server = 4
+};
+
+struct ent_skateshop
+{
+   struct mdl_transform transform;
+   u32 type, id_camera;
+
+   union
+   {
+      struct
+      {
+         u32 id_display,
+             id_info,
+             id_rack;
+      }
+      boards;
+
+      struct
+      {
+         u32 id_display,
+             id_info,
+             id_rack;
+      }
+      character;
+
+      struct
+      {
+         u32 id_display,
+             id_info;
+      }
+      worlds;
+
+      struct
+      {
+         u32 id_lever;
+      }
+      server;
+   };
+};
+
+struct ent_swspreview
+{
+   u32 id_camera, id_display, id_display1;
+};
+
+struct ent_traffic
+{
+   struct mdl_transform transform;
+   u32 submesh_start,
+       submesh_count,
+       start_node,
+       node_count;
+   f32 speed,
+       t;
+   u32 index;     /* into the path */
+};
+
+struct ent_camera
+{
+   f32 co[3], r[3];
+   f32 fov;
+};
+
+#if (VG_MODEL_VERSION_MIN <= 107)
+struct ent_camera_v107
+{
+   struct mdl_transform transform;
+   f32 fov;
+};
+
+static inline void fix_ent_camera_v107( struct ent_camera_v107 *old, struct ent_camera *new )
+{
+   f32 dir[3] = {0.0f,-1.0f,0.0f};
+   mdl_transform_vector( &old->transform, dir, dir );
+   v3_angles( dir, new->r );
+   v3_copy( old->transform.co, new->co );
+   new->fov = old->fov;
+}
+#endif
+
+enum world_flag
+{
+   k_world_flag_fixed_time = 0x1,
+   k_world_flag_water_is_safe = 0x2,
+   k_world_flag_no_skating = 0x4,
+   k_world_flag_no_rewind = 0x8
+};
+
+struct ent_worldinfo
+{
+   u32 pstr_name, pstr_author, pstr_desc;
+   f32 timezone;
+   u32 pstr_skybox;
+   u32 flags;
+   f32 wind_scale;
+};
+
+enum channel_behaviour
+{
+   k_channel_behaviour_unlimited = 0,
+   k_channel_behaviour_discard_if_full = 1,
+   k_channel_behaviour_crossfade_if_full = 2
+};
+
+enum probability_curve{
+   k_probability_curve_constant = 0,
+   k_probability_curve_wildlife_day = 1,
+   k_probability_curve_wildlife_night = 2
+};
+
+struct ent_font
+{
+   u32 alias,
+       variant_start,
+       variant_count,
+       glyph_start,
+       glyph_count,
+       glyph_utf32_base;
+};
+
+struct ent_font_variant
+{
+   u32 name,
+       material_id;
+};
+
+struct ent_glyph
+{
+   f32 size[2];
+   u32 indice_start,
+       indice_count;
+};
+
+struct ent_ccmd
+{
+   u32 pstr_command;
+};
+
+enum ent_script_flag
+{
+   k_ent_script_flag_linked = 0x1
+};
+
+struct ent_script
+{
+   union
+   {
+      u32 pstr_script_name,  /* When its in the file */
+          script_id;         /* When its runtime */
+   };
+
+   u32 entity_list_id;
+   u32 flags;
+};
+
+enum ent_atom_flag
+{
+   k_ent_atom_global = 0x1,
+   k_ent_atom_scrap  = 0x2
+};
+
+struct ent_atom
+{
+   union
+   {
+      u32 pstr_alias;
+      i32 scrap_value;
+   };
+
+   u32 flags;
+};
+
+enum ent_cutscene_flag
+{
+   k_ent_cutscene_freeze_player = 0x1,
+};
+
+struct ent_cutscene
+{
+   u32 pstr_path,
+       flags;
+};
+
+enum ent_objective_filter{
+   k_ent_objective_filter_none            = 0x00000000,
+   k_ent_objective_filter_trick_shuvit    = 0x00000001,
+   k_ent_objective_filter_trick_kickflip  = 0x00000002,
+   k_ent_objective_filter_trick_treflip   = 0x00000004,
+   k_ent_objective_filter_trick_any       = 
+      k_ent_objective_filter_trick_shuvit|
+      k_ent_objective_filter_trick_treflip|
+      k_ent_objective_filter_trick_kickflip,
+   k_ent_objective_filter_flip_back       = 0x00000008,
+   k_ent_objective_filter_flip_front      = 0x00000010,
+   k_ent_objective_filter_flip_any        =
+      k_ent_objective_filter_flip_back|
+      k_ent_objective_filter_flip_front,
+   k_ent_objective_filter_grind_truck_any = 0x00000020,
+   k_ent_objective_filter_grind_board_any = 0x00000040,
+   k_ent_objective_filter_grind_any       =
+      k_ent_objective_filter_grind_truck_any|
+      k_ent_objective_filter_grind_board_any,
+   k_ent_objective_filter_footplant       = 0x00000080,
+   k_ent_objective_filter_passthrough     = 0x00000100,
+   k_ent_objective_filter_glider          = 0x00000200
+};
+
+enum ent_objective_flag 
+{
+   k_ent_objective_hidden = 0x1,
+   k_ent_objective_passed = 0x2,
+   k_ent_objective_failed = 0x4
+};
+
+struct ent_objective
+{
+   struct mdl_transform transform;
+   u32 submesh_start,
+       submesh_count,
+       flags,
+       id_next,
+       filter,filter2,
+       deleted0;
+   i32 deleted1;
+   f32 time_limit;
+   u32 pstr_description_ui;
+};
+
+enum ent_challenge_flag 
+{
+   k_ent_challenge_timelimit = 0x1,
+   //k_ent_challenge_is_story  = 0x2,
+   k_ent_challenge_locked    = 0x4,
+   k_ent_challenge_any_order = 0x8,
+   k_ent_challenge_target    = 0x10
+};
+
+struct ent_challenge
+{
+   struct mdl_transform transform;
+   u32 pstr_alias,
+       flags;
+
+   u32 deleted0;//on_activate_id;
+   u32 deleted1;//on_activate_event;
+   u32 deleted2;//on_complete_id;
+   i32 deleted3;//on_complete_event;
+
+   u32 first_objective_id;
+   u32 camera_id;
+
+   u32 status;
+   u32 reset_spawn_id;
+};
+
+struct ent_cubemap 
+{
+   f32 co[3];
+   u32 resolution, live, texture_id, 
+       framebuffer_id, renderbuffer_id, placeholder[2];
+};
+
+enum prop_flag
+{
+   k_prop_flag_hidden = 0x1,
+   k_prop_flag_spinning = 0x2,
+   k_prop_flag_collider = 0x4,
+   k_prop_flag_spinning_fast = 0x8,
+};
+
+struct ent_prop 
+{
+   struct mdl_transform transform;
+   u32 submesh_start, submesh_count, flags, pstr_alias;
+};
+
+enum editer_type
+{
+   k_editer_type_toggle = 0,
+   k_editer_type_slider = 1,
+   k_editer_type_selecter = 2
+};
+
+struct editer_property
+{
+   u32 pstr_alias;
+   u8  ui_type;
+
+   union 
+   {
+      u32 _u32;
+      f32 _f32;
+      u32 pstr_options;
+   }
+   max;
+};
+
+struct editer_item
+{
+   struct mdl_transform transform;
+   u32 submesh_start, submesh_count;
+
+   u32 pstr_visibility,
+       pstr_uv_x,
+       pstr_uv_y;
+   u16 discard_send,
+       discard_mask;
+};
+
+struct ent_region 
+{
+   struct mdl_transform transform;
+   u32 submesh_start, submesh_count, pstr_title, flags;
+
+   union
+   {
+      struct{ u32 zone_volume; } v105;
+      struct{ u32 id_list; } v109;
+   };
+
+   /* 105+ */
+   u32 deleted01[2];
+};
+
+enum ent_glider_flag
+{
+   k_ent_glider_flag_locked = 0x1
+};
+struct ent_glider 
+{
+   struct mdl_transform transform;
+   u32 flags;
+   f32 cooldown;
+};
+
+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
+};
+
+enum ent_event_flags
+{
+   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,
+       pstr_recieve_event,
+       source_entity_id,
+       recieve_entity_id,
+       flags;
+
+   f32 delay;
+   u32 unused0;
+
+   union
+   {
+      i32 const_i32;
+      f32 const_f32;
+      u32 const_entity_id;
+      u32 const_pstr;
+      u32 pstr_data_alias;
+   }
+   data;
+};
index f4bd4fcf45658fb1d957977f2c2555614fe08bbf..a03f85ec225378136b6b960baac4cb8d62c010e1 100644 (file)
@@ -122,7 +122,7 @@ VG_API void _vg_profiler_exit_block( u32 profiler_id )
    profiler->segment_last = seg_end;
 }
 
-VG_API void _vg_profiler_draw( ui_context *ctx, u32 profiler_id, f32 budget_ms, ui_rect panel, i32 dir, bool normalize )
+VG_API void _vg_profiler_draw( ui_context *ctx, u32 profiler_id, f32 budget_ms, ui_rect panel, i32 dir, b8 normalize )
 {
    struct vg_profiler *profiler = _vg_profiler_get( profiler_id );
    if( panel[2] == 0 )
diff --git a/source/engine/shader.c b/source/engine/shader.c
deleted file mode 100644 (file)
index ec32512..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-#include "common_api.h"
-#include "opengl.h"
-
-GLuint compile_opengl_subshader( GLint type, const c8 *sources[], u32 source_count, bool critical, const c8 *name )
-{
-       GLuint shader = glCreateShader( type );
-   if( shader == 0 )
-   {
-      $log( $fatal, {"glCreateShader returned 0.\n"} );
-      _fatal_exit();
-   }
-
-       glShaderSource( shader, source_count, sources, NULL );
-       glCompileShader( shader );
-
-       GLint status;
-       glGetShaderiv( shader, GL_COMPILE_STATUS, &status );
-   if( status == GL_TRUE )
-      return shader;
-   else
-   {
-      GLchar info[1024];
-      GLsizei len;
-      glGetShaderInfoLog( shader, sizeof(info), &len, info );
-
-      const c8 *type_str = "?";
-           if( type == GL_VERTEX_SHADER )   type_str = "GL_VERTEX_SHADER";
-      else if( type == GL_FRAGMENT_SHADER ) type_str = "GL_FRAGMENT_SHADER";
-      if( critical ) 
-      {
-         $log( $fatal, {"subshader compile error\n"}, {name}, {": "}, {type_str}, {info} );
-         _fatal_exit();
-      }
-      return 0;
-   }
-}
-
-bool link_opengl_program( GLuint program, bool critical )
-{
-       glLinkProgram( program );
-       GLint success;
-       glGetProgramiv( program, GL_LINK_STATUS, &success );
-       if( success ) 
-      return 1;
-   else
-       {
-      char info[ 512 ];
-               glGetProgramInfoLog( program, sizeof(info), NULL, info );
-      if( critical ) 
-      {
-         $log( $fatal, {"Shader link error\n"}, {info} );
-         _fatal_exit();
-      }
-               return 0;
-       }
-}
-
-#if 0
-GLuint compile_opengl_shader( const c8 *vs, const c8 *fs )
-{
-   GLuint vert = compile_opengl_subshader( GL_VERTEX_SHADER, vs, 1, NULL ),
-          frag = compile_opengl_subshader( GL_FRAGMENT_SHADER, fs, 1, NULL ),
-              program = glCreateProgram();
-       glAttachShader( program, vert );
-       glAttachShader( program, frag );
-   link_opengl_program( program, 1 );
-       glDeleteShader( vert );
-       glDeleteShader( frag );
-   return program;
-}
-#endif
-
-GLuint _shader_programs[ k_shader_count ];
-GLuint _uniform_locations[ k_shader_uniform_count ];
-
-struct shader
-{
-   const c8 *name;
-   u32 subshader_count, uniform_start, uniform_count;
-
-   struct subshader 
-   {
-      enum subshader_type
-      {
-         k_subshader_vertex,
-         k_subshader_fragment,
-         k_subshader_geometry
-      }
-      type;
-      u32 source_count;
-      const c8 **source_list;
-      const c8 *source_static;
-      const c8 *source_uniforms;
-   }
-   *subshaders;
-};
-
-#include "generated/shaders.c"
-
-void _shaders_compile(void)
-{
-   for( u32 i=0; i<k_shader_count; i ++ )
-   {
-      struct shader *shader = &_shader_definitions[ i ];
-      GLuint new_program = glCreateProgram();
-      GLuint sub_programs[3];
-
-      for( u32 j=0; j<shader->subshader_count; j ++ )
-      {
-         struct subshader *subshader = &shader->subshaders[j];
-
-         const c8 *sources[32] = { "#version 330 core\n", NULL };
-         u32 source_count = 1;
-         sources[ source_count ++ ] = subshader->source_uniforms;
-         sources[ source_count ++ ] = subshader->source_static;
-
-         ASSERT_CRITICAL( subshader->type != k_subshader_geometry );
-         sub_programs[j] = compile_opengl_subshader( 
-               (subshader->type == k_subshader_vertex? GL_VERTEX_SHADER: GL_FRAGMENT_SHADER), 
-               sources, source_count, 1, shader->name );
-         glAttachShader( new_program, sub_programs[j] );
-      }
-
-      link_opengl_program( new_program, 1 );
-      _shader_programs[i] = new_program;
-
-      for( u32 j=0; j<shader->subshader_count; j ++ )
-         glDeleteShader( sub_programs[j] );
-
-      for( u32 j=0; j<shader->uniform_count; j ++ )
-      {
-         u32 index = shader->uniform_start+j;
-         _uniform_locations[ index ] = glGetUniformLocation( new_program, _uniform_aliases[ index ] );
-      }
-   }
-}
-
-void _shader_bind( enum shader_id id )
-{
-   glUseProgram( _shader_programs[ id ] );
-}
diff --git a/source/engine/shader_props.h b/source/engine/shader_props.h
new file mode 100644 (file)
index 0000000..d383c0d
--- /dev/null
@@ -0,0 +1,72 @@
+#pragma once
+
+enum material_render_flag
+{
+   k_material_render_additive = 0x20
+};
+
+enum workshop_shader_part
+{
+   k_workshop_shader_part_truck1,
+   k_workshop_shader_part_truck2,
+   k_workshop_shader_part_wheel1,
+   k_workshop_shader_part_wheel2,
+   k_workshop_shader_part_wheel3,
+   k_workshop_shader_part_wheel4,
+   k_workshop_shader_part_edge,
+   k_workshop_shader_part_griptape,
+   k_workshop_shader_part_deck,
+   k_workshop_shader_part_max
+};
+
+union shader_props
+{
+   struct shader_props_standard
+   {
+      u32 tex_diffuse;
+      u32 render_flags;
+   }
+   standard;
+
+   struct shader_props_terrain
+   {
+      u32 tex_diffuse;
+      f32 blend_offset[2];
+      f32 sand_colour[4];
+   }
+   terrain;
+
+   struct shader_props_vertex_blend
+   {
+      u32 tex_diffuse;
+      f32 blend_offset[2];
+   }
+   vertex_blend;
+
+   struct shader_props_water
+   {
+      f32 shore_colour[4];
+      f32 deep_colour[4];
+      f32 fog_scale;
+      f32 fresnel;
+      f32 water_sale;
+      f32 wave_speed[4];
+   }
+   water;
+
+   struct shader_props_cubemapped
+   {
+      u32 tex_diffuse;
+      u32 cubemap_entity;
+      f32 tint[4];
+   }
+   cubemapped;
+
+   struct shader_props_workshop
+   {
+      u32 tex_all[k_workshop_shader_part_max];
+   }
+   workshop;
+};
+
+extern const char *_shader_prop_workshop_keys[k_workshop_shader_part_max];
diff --git a/source/engine/shaders/blit.vs b/source/engine/shaders/blit.vs
new file mode 100644 (file)
index 0000000..e19a10f
--- /dev/null
@@ -0,0 +1,12 @@
+layout (location=0) in vec2 a_co;
+out vec2 aUv;
+
+void main()
+{
+   gl_Position = vec4(a_co*2.0-1.0,0.0,1.0);
+
+   if( uFlip == 1 )
+      aUv = vec2(a_co.x,1.0-a_co.y) * uInverseRatio;
+   else
+      aUv = a_co * uInverseRatio;
+}
diff --git a/source/engine/shaders/blit_tex.fs b/source/engine/shaders/blit_tex.fs
new file mode 100644 (file)
index 0000000..0d39a1b
--- /dev/null
@@ -0,0 +1,7 @@
+out vec4 FragColor;
+in vec2 aUv;
+
+void main()
+{
+   FragColor = texture( uTexMain, aUv );
+}
diff --git a/source/engine/shaders/debug_lines.fs b/source/engine/shaders/debug_lines.fs
new file mode 100644 (file)
index 0000000..1fcc7ad
--- /dev/null
@@ -0,0 +1,8 @@
+out vec4 FragColor;
+
+in vec4 s_colour;
+
+void main()
+{
+   FragColor = s_colour;
+}
diff --git a/source/engine/shaders/debug_lines.vs b/source/engine/shaders/debug_lines.vs
new file mode 100644 (file)
index 0000000..b2f8e17
--- /dev/null
@@ -0,0 +1,11 @@
+layout (location=0) in vec3 a_co;
+layout (location=1) in vec4 a_colour;
+
+out vec4 s_colour;
+
+void main()
+{
+   vec4 vert_pos = uPv * vec4( a_co, 1.0 );
+   s_colour = a_colour;
+   gl_Position = vert_pos;
+}
diff --git a/source/engine/steamworks.c b/source/engine/steamworks.c
deleted file mode 100644 (file)
index 1c14bbd..0000000
+++ /dev/null
@@ -1,364 +0,0 @@
-struct vg_steam_api _steam_api;
-
-static void cb_steam_warning( i32 severity, const c8 *pchMessage )
-{
-   if( severity == 0 )
-      vg_logsteam( "[message]" KBLK " '%s'\n", pchMessage );
-   else
-      vg_logsteam( "[message]" KYEL " '%s'\n", pchMessage );
-}
-
-#if defined( VG_ENGINE )
-static void cb_auth_ticket_recieved( void *result, void *context )
-{
-   EncryptedAppTicketResponse_t *response = result;
-
-   if( response->m_eResult == k_EResultOK )
-      vg_logsteam( "  New app ticket ready\n" );
-   else
-      vg_logsteam( KYEL "  Could not request new encrypted app ticket (%u)\n", response->m_eResult );
-   
-   if( SteamAPI_ISteamUser_GetEncryptedAppTicket( _steam_api.pSteamUser, _steam_api.app_symmetric_key,
-            VG_ARRAY_LEN(_steam_api.app_symmetric_key), &_steam_api.app_key_length ))
-   {
-      vg_logsteam( KGRN "  Loaded app ticket\n" );
-   }
-   else
-   {
-      vg_logsteam( KRED "  No ticket availible\n" );
-      _steam_api.app_key_length = 0;
-   }
-}
-#endif
-
-#if defined( VG_SERVER )
-static u8 vg_char_base16( c8 c )
-{
-   if( c >= '0' && c <= '9' )
-      return c-'0';
-   if( c >= 'a' && c <= 'f' )
-      return (c-'a') + 10;
-   return 0;
-}
-#endif
-
-#if defined( VG_SERVER )
-VG_API bool _vg_steam_init( u32 unIP, u16 usGamePort, u16 usQueryPort, EServerMode eServerMode, 
-                            const c8 *pchVersionString, const c8 *appid_str )
-#else
-VG_API bool _vg_steam_init(void)
-#endif
-{
-   if( _steam_api.disabled )
-      return 0;
-
-   SteamErrMsg err;
-
-   /* Steamworks init step
-    * ---------------------------------------------------------------------------- */
-#if defined( VG_ENGINE )
-   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_STEAM ) );
-
-       const char *pszInternalCheckInterfaceVersions = 
-               STEAMUTILS_INTERFACE_VERSION "\0"
-               STEAMNETWORKINGUTILS_INTERFACE_VERSION "\0"
-               STEAMAPPS_INTERFACE_VERSION "\0"
-               STEAMCONTROLLER_INTERFACE_VERSION "\0"
-               STEAMFRIENDS_INTERFACE_VERSION "\0"
-               STEAMGAMESEARCH_INTERFACE_VERSION "\0"
-               STEAMHTMLSURFACE_INTERFACE_VERSION "\0"
-               STEAMHTTP_INTERFACE_VERSION "\0"
-               STEAMINPUT_INTERFACE_VERSION "\0"
-               STEAMINVENTORY_INTERFACE_VERSION "\0"
-               STEAMMATCHMAKINGSERVERS_INTERFACE_VERSION "\0"
-               STEAMMATCHMAKING_INTERFACE_VERSION "\0"
-               STEAMMUSICREMOTE_INTERFACE_VERSION "\0"
-               STEAMMUSIC_INTERFACE_VERSION "\0"
-               STEAMNETWORKINGMESSAGES_INTERFACE_VERSION "\0"
-               STEAMNETWORKINGSOCKETS_INTERFACE_VERSION "\0"
-               STEAMNETWORKING_INTERFACE_VERSION "\0"
-               STEAMPARENTALSETTINGS_INTERFACE_VERSION "\0"
-               STEAMPARTIES_INTERFACE_VERSION "\0"
-               STEAMREMOTEPLAY_INTERFACE_VERSION "\0"
-               STEAMREMOTESTORAGE_INTERFACE_VERSION "\0"
-               STEAMSCREENSHOTS_INTERFACE_VERSION "\0"
-               STEAMUGC_INTERFACE_VERSION "\0"
-               STEAMUSERSTATS_INTERFACE_VERSION "\0"
-               STEAMUSER_INTERFACE_VERSION "\0"
-               STEAMVIDEO_INTERFACE_VERSION "\0"
-
-               "\0";
-
-       if( SteamInternal_SteamAPI_Init( pszInternalCheckInterfaceVersions, &err ) != k_ESteamAPIInitResult_OK )
-   {
-      _steam_api.disabled = 1;
-      vg_logsteam( KRED "SteamInternal_SteamAPI_Init() failed: '%s'\nAll steam interactions disabled for this session\n", err );
-      return 0;
-   }
-#endif
-
-#if defined( VG_SERVER )
-   FILE *txt = fopen( "steam_appid.txt", "w" );
-   fputs( appid_str, txt );
-   fclose( txt );
-
-   // FIXME: Add no-auth launch option (hoist from skaterift, as we have it there, to vg steam module?)
-   vg_stack_allocator stack;
-   vg_stack_init( &stack, NULL, VG_KB(256), NULL );
-   u32 size;
-   c8 *src = vg_file_read( &stack, "application_key", &size, 0 );
-   if( src )
-   {
-      if( size < k_nSteamEncryptedAppTicketSymmetricKeyLen )
-      {
-         vg_error( "Application key was invalid size\n" );
-         return 0;
-      }
-      
-      for( int i=0; i<k_nSteamEncryptedAppTicketSymmetricKeyLen; i++ )
-         _steam_api.server_symmetric_key[i] = (vg_char_base16( src[i*2+0] ) << 4) | vg_char_base16( src[i*2+1] );
-   }
-   else
-   {
-      vg_error( "No application_key file\n" );
-      return 0;
-   }
-
-       const char *pszInternalCheckInterfaceVersions = 
-               STEAMUTILS_INTERFACE_VERSION "\0"
-               STEAMNETWORKINGUTILS_INTERFACE_VERSION "\0"
-
-               STEAMGAMESERVER_INTERFACE_VERSION "\0"
-               STEAMGAMESERVERSTATS_INTERFACE_VERSION "\0"
-               STEAMHTTP_INTERFACE_VERSION "\0"
-               STEAMINVENTORY_INTERFACE_VERSION "\0"
-               STEAMNETWORKING_INTERFACE_VERSION "\0"
-               STEAMNETWORKINGMESSAGES_INTERFACE_VERSION "\0"
-               STEAMNETWORKINGSOCKETS_INTERFACE_VERSION "\0"
-               STEAMUGC_INTERFACE_VERSION "\0"
-               "\0";
-       
-   ESteamAPIInitResult init_result = SteamInternal_GameServer_Init_V2( 
-         unIP, usGamePort, usQueryPort, eServerMode, pchVersionString, pszInternalCheckInterfaceVersions, &err );
-
-   if( init_result != k_ESteamAPIInitResult_OK )
-   {
-      _steam_api.disabled = 1;
-      vg_logsteam( KRED "SteamInternal_GameServer_Init_V2() failed: '%s'\n", err );
-      return 0;
-   }
-#endif
-
-   /* Manual dispatch step 
-    * ----------------------------------------------------------------------------- */
-   SteamAPI_ManualDispatch_Init();
-
-   /*
-    * Interface Init
-    * ----------------------------------------------------------------------------- */
-
-#if defined( VG_ENGINE )
-   _steam_api.hPipe = SteamAPI_GetHSteamPipe();
-   _steam_api.pSteamUtils = SteamAPI_SteamUtils_v010();
-   _steam_api.pSteamFriends = SteamAPI_SteamFriends_v018();
-   _steam_api.pSteamUGC = SteamAPI_SteamUGC_v021();
-   _steam_api.pSteamUser = SteamAPI_SteamUser_v023();
-   _steam_api.pSteamUserStats = SteamAPI_SteamUserStats_v013();
-   _steam_api.pSteamNetworkingSockets = SteamAPI_SteamNetworkingSockets_SteamAPI_v012();
-#endif
-
-#if defined( VG_SERVER )
-   _steam_api.hPipe = SteamGameServer_GetHSteamPipe();
-   _steam_api.pSteamGameServer = SteamAPI_SteamGameServer_v015();
-   _steam_api.pSteamUtils = SteamAPI_SteamGameServerUtils_v010();
-   _steam_api.pSteamUGC = SteamAPI_SteamGameServerUGC_v021();
-   _steam_api.pSteamNetworkingSockets = SteamAPI_SteamGameServerNetworkingSockets_SteamAPI_v012();
-#endif
-
-   _steam_api.pSteamNetworkingUtils = SteamAPI_SteamNetworkingUtils_SteamAPI_v004();
-
-   SteamAPI_ISteamUtils_SetWarningMessageHook( _steam_api.pSteamUtils, cb_steam_warning );
-
-#if defined( VG_SERVER )
-   SteamAPI_ISteamGameServer_LogOnAnonymous( _steam_api.pSteamGameServer );
-#endif
-
-
-#if defined( VG_ENGINE ) 
-   strcpy( _steam_api.username_at_startup, "[unassigned]" );
-   const c8 *username = SteamAPI_ISteamFriends_GetPersonaName( _steam_api.pSteamFriends );
-   str_utf8_collapse( username, _steam_api.username_at_startup, VG_ARRAY_LEN(_steam_api.username_at_startup) );
-
-# if defined( VG_MULTIPLAYER )
-   vg_logsteam( "Requesting new authorization ticket\n" );
-
-   vg_steam_api_call *call = vg_alloc_async_steam_api_call();
-   if( call )
-   {
-      call->userdata = NULL;
-      call->cb = cb_auth_ticket_recieved;
-      call->id = SteamAPI_ISteamUser_RequestEncryptedAppTicket( _steam_api.pSteamUser, NULL, 0 );
-   }
-# endif
-#endif
-
-   return 1;
-}
-
-static const c8 *string_ESteamNetworkingConnectionState( ESteamNetworkingConnectionState s )
-{
-   if( s == k_ESteamNetworkingConnectionState_None ) return "None";
-   if( s == k_ESteamNetworkingConnectionState_Connecting) return "Connecting"; 
-   if( s == k_ESteamNetworkingConnectionState_FindingRoute) return "Finding route"; 
-   if( s == k_ESteamNetworkingConnectionState_Connected) return "Connected"; 
-   if( s == k_ESteamNetworkingConnectionState_ClosedByPeer) return "Closed By Peer"; 
-   if( s == k_ESteamNetworkingConnectionState_ProblemDetectedLocally) return "Problem detected locally"; 
-   if( s == k_ESteamNetworkingConnectionState_FinWait) return "Finwait"; 
-   if( s == k_ESteamNetworkingConnectionState_Linger) return "Linger"; 
-   if( s == k_ESteamNetworkingConnectionState_Dead) return "Dead"; 
-   return "enum-out-of-range";
-}
-
-static const c8 *string_ESteamAPICallFailure( ESteamAPICallFailure e )
-{
-   if( e == k_ESteamAPICallFailureNone ) return "None";
-   if( e == k_ESteamAPICallFailureSteamGone ) return "Steam Gone";
-   if( e == k_ESteamAPICallFailureNetworkFailure ) return "Network Failure";
-   if( e == k_ESteamAPICallFailureInvalidHandle ) return KBLK "Invalid Handle";
-   if( e == k_ESteamAPICallFailureMismatchedCallback ) return "Mismatched Callback";
-   return "enum-out-of-range";
-}
-
-void vg_steam_frame(void)
-{
-   if( _steam_api.disabled )
-      return;
-
-   SteamAPI_ManualDispatch_RunFrame( _steam_api.hPipe );
-
-   CallbackMsg_t callback;
-   while( SteamAPI_ManualDispatch_GetNextCallback( _steam_api.hPipe, &callback ) )
-   {
-      /* Check for dispatching API call results */
-      i32 type = callback.m_iCallback;
-      if( type == k_iSteamUtils_SteamAPICallCompleted )
-      {
-         SteamAPICallCompleted_t *inf = callback.m_pubParam;
-
-         bool bFailed;
-         void *call_data = alloca( inf->m_cubParam );
-         
-         if( SteamAPI_ManualDispatch_GetAPICallResult( _steam_api.hPipe, inf->m_hAsyncCall, 
-                                                       call_data, inf->m_cubParam, 
-                                                       inf->m_iCallback, &bFailed ) )
-         {
-            vg_logsteam( "api_call_completed %lu\n", inf->m_hAsyncCall );
-            
-            int j=0;
-            for( int i=0; i<_steam_api.api_call_count; i++ )
-            {
-               if( _steam_api.api_calls[i].id != inf->m_hAsyncCall )
-                  _steam_api.api_calls[j ++] = _steam_api.api_calls[i];
-               else
-                  _steam_api.api_calls[i].cb( call_data, _steam_api.api_calls[i].userdata );
-            }
-
-            if( _steam_api.api_call_count == j )
-               vg_error( "No tracker was register for API call\n" );
-
-            _steam_api.api_call_count = j;
-         } 
-         else 
-         { 
-            enum ESteamAPICallFailure e = 
-               SteamAPI_ISteamUtils_GetAPICallFailureReason( _steam_api.pSteamUtils, inf->m_hAsyncCall );
-            const c8 *fail_str = string_ESteamAPICallFailure( e );
-            vg_logsteam( KRED "Error getting call result on %lu (code %d)\n", inf->m_hAsyncCall, fail_str );
-         }
-      } 
-      else 
-      {
-         /* 
-          * Look at callback.m_iCallback to see what kind of callback it is,
-          * and dispatch to appropriate handler(s)
-          * void *data = callback.m_pubParam;
-          */
-         if( type == k_iSteamUser_SteamServersConnected )
-            vg_success( "Steam servers connected" );
-         else if( type == k_iSteamUser_SteamConnectFailure )
-         {
-            SteamServerConnectFailure_t *inf = callback.m_pubParam;
-            vg_logsteam( KRED "Steam server connect failure, code %d, retrying: %d\n", inf->m_eResult, inf->m_bStillRetrying );
-         }
-         else if( type == k_iSteamUser_SteamServersDisconnected )
-         {
-            SteamServersDisconnected_t *inf = callback.m_pubParam;
-            vg_logsteam( "Steam servers disconnect, code %d\n", inf->m_eResult );
-         }
-         else if( type == k_iSteamNetworkingSockets_SteamNetConnectionStatusChangedCallback )
-         {
-            SteamNetConnectionStatusChangedCallback_t *inf = callback.m_pubParam;
-            const c8 *status_string = string_ESteamNetworkingConnectionState( inf->m_info.m_eState );
-            vg_logsteam( "Connection status changed: %x -> %s\n   Debug: '%s'\n   EndDebug: '%s'\n", 
-                           inf->m_hConn, status_string, inf->m_info.m_szConnectionDescription,
-                           inf->m_info.m_szEndDebug );
-            
-            if( _steam_api.cb_connection_changed )
-               _steam_api.cb_connection_changed( inf );
-         }
-         else if( type == k_iSteamNetworkingSockets_SteamNetAuthenticationStatus )
-         {
-            SteamNetAuthenticationStatus_t *inf = callback.m_pubParam;
-            vg_logsteam( "Steam Authentication Status: %d\n   Debug: '%s'\n", inf->m_eAvail, inf->m_debugMsg );
-         }
-      }
-      
-      SteamAPI_ManualDispatch_FreeLastCallback( _steam_api.hPipe );
-   }
-}
-
-VG_API void _vg_steam_shutdown(void)
-{
-#if defined( VG_SERVER )
-   if( _steam_api.is_connected )
-   {
-      SteamAPI_ISteamGameServer_LogOff( _steam_api.pSteamGameServer );
-      _steam_api.is_connected = 0;
-   }
-   SteamGameServer_Shutdown();
-#else
-   SteamAPI_Shutdown();
-#endif
-}
-
-vg_steam_api_call *vg_alloc_async_steam_api_call(void)
-{
-   if( _steam_api.api_call_count == VG_ARRAY_LEN(_steam_api.api_calls) )
-      return NULL;
-   return &_steam_api.api_calls[ _steam_api.api_call_count ++ ];
-}
-
-#if defined( VG_ENGINE )
-void vg_steam_set_achievement( const c8 *name, bool yes )
-{
-   if( _steam_api.disabled || _steam_api.demo_mode )
-      return;
-   
-   if( yes )
-   {
-      if( SteamAPI_ISteamUserStats_SetAchievement( _steam_api.pSteamUserStats, name ) )
-      {
-         vg_logsteam( KGRN "Set achievement '%s'\n", name ); 
-         SteamAPI_ISteamUserStats_StoreStats( _steam_api.pSteamUserStats );
-      }
-   }
-   else
-   {
-      if( SteamAPI_ISteamUserStats_ClearAchievement( _steam_api.pSteamUserStats, name ) )
-      {
-         vg_logsteam( KBLK "Clear achievement '%s'\n", name ); 
-         SteamAPI_ISteamUserStats_StoreStats( _steam_api.pSteamUserStats );
-      }
-   }
-}
-#endif
diff --git a/source/engine/steamworks.c_UNUSED b/source/engine/steamworks.c_UNUSED
new file mode 100644 (file)
index 0000000..549baf9
--- /dev/null
@@ -0,0 +1,364 @@
+struct vg_steam_api _steam_api;
+
+static void cb_steam_warning( i32 severity, const c8 *pchMessage )
+{
+   if( severity == 0 )
+      vg_logsteam( "[message]" KBLK " '%s'\n", pchMessage );
+   else
+      vg_logsteam( "[message]" KYEL " '%s'\n", pchMessage );
+}
+
+#if defined( VG_ENGINE )
+static void cb_auth_ticket_recieved( void *result, void *context )
+{
+   EncryptedAppTicketResponse_t *response = result;
+
+   if( response->m_eResult == k_EResultOK )
+      vg_logsteam( "  New app ticket ready\n" );
+   else
+      vg_logsteam( KYEL "  Could not request new encrypted app ticket (%u)\n", response->m_eResult );
+   
+   if( SteamAPI_ISteamUser_GetEncryptedAppTicket( _steam_api.pSteamUser, _steam_api.app_symmetric_key,
+            VG_ARRAY_LEN(_steam_api.app_symmetric_key), &_steam_api.app_key_length ))
+   {
+      vg_logsteam( KGRN "  Loaded app ticket\n" );
+   }
+   else
+   {
+      vg_logsteam( KRED "  No ticket availible\n" );
+      _steam_api.app_key_length = 0;
+   }
+}
+#endif
+
+#if defined( VG_SERVER )
+static u8 vg_char_base16( c8 c )
+{
+   if( c >= '0' && c <= '9' )
+      return c-'0';
+   if( c >= 'a' && c <= 'f' )
+      return (c-'a') + 10;
+   return 0;
+}
+#endif
+
+#if defined( VG_SERVER )
+VG_API b8 _vg_steam_init( u32 unIP, u16 usGamePort, u16 usQueryPort, EServerMode eServerMode, 
+                            const c8 *pchVersionString, const c8 *appid_str )
+#else
+VG_API b8 _vg_steam_init(void)
+#endif
+{
+   if( _steam_api.disabled )
+      return 0;
+
+   SteamErrMsg err;
+
+   /* Steamworks init step
+    * ---------------------------------------------------------------------------- */
+#if defined( VG_ENGINE )
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_STEAM ) );
+
+       const char *pszInternalCheckInterfaceVersions = 
+               STEAMUTILS_INTERFACE_VERSION "\0"
+               STEAMNETWORKINGUTILS_INTERFACE_VERSION "\0"
+               STEAMAPPS_INTERFACE_VERSION "\0"
+               STEAMCONTROLLER_INTERFACE_VERSION "\0"
+               STEAMFRIENDS_INTERFACE_VERSION "\0"
+               STEAMGAMESEARCH_INTERFACE_VERSION "\0"
+               STEAMHTMLSURFACE_INTERFACE_VERSION "\0"
+               STEAMHTTP_INTERFACE_VERSION "\0"
+               STEAMINPUT_INTERFACE_VERSION "\0"
+               STEAMINVENTORY_INTERFACE_VERSION "\0"
+               STEAMMATCHMAKINGSERVERS_INTERFACE_VERSION "\0"
+               STEAMMATCHMAKING_INTERFACE_VERSION "\0"
+               STEAMMUSICREMOTE_INTERFACE_VERSION "\0"
+               STEAMMUSIC_INTERFACE_VERSION "\0"
+               STEAMNETWORKINGMESSAGES_INTERFACE_VERSION "\0"
+               STEAMNETWORKINGSOCKETS_INTERFACE_VERSION "\0"
+               STEAMNETWORKING_INTERFACE_VERSION "\0"
+               STEAMPARENTALSETTINGS_INTERFACE_VERSION "\0"
+               STEAMPARTIES_INTERFACE_VERSION "\0"
+               STEAMREMOTEPLAY_INTERFACE_VERSION "\0"
+               STEAMREMOTESTORAGE_INTERFACE_VERSION "\0"
+               STEAMSCREENSHOTS_INTERFACE_VERSION "\0"
+               STEAMUGC_INTERFACE_VERSION "\0"
+               STEAMUSERSTATS_INTERFACE_VERSION "\0"
+               STEAMUSER_INTERFACE_VERSION "\0"
+               STEAMVIDEO_INTERFACE_VERSION "\0"
+
+               "\0";
+
+       if( SteamInternal_SteamAPI_Init( pszInternalCheckInterfaceVersions, &err ) != k_ESteamAPIInitResult_OK )
+   {
+      _steam_api.disabled = 1;
+      vg_logsteam( KRED "SteamInternal_SteamAPI_Init() failed: '%s'\nAll steam interactions disabled for this session\n", err );
+      return 0;
+   }
+#endif
+
+#if defined( VG_SERVER )
+   FILE *txt = fopen( "steam_appid.txt", "w" );
+   fputs( appid_str, txt );
+   fclose( txt );
+
+   // FIXME: Add no-auth launch option (hoist from skaterift, as we have it there, to vg steam module?)
+   vg_stack_allocator stack;
+   vg_stack_init( &stack, NULL, VG_KB(256), NULL );
+   u32 size;
+   c8 *src = vg_file_read( &stack, "application_key", &size, 0 );
+   if( src )
+   {
+      if( size < k_nSteamEncryptedAppTicketSymmetricKeyLen )
+      {
+         vg_error( "Application key was invalid size\n" );
+         return 0;
+      }
+      
+      for( int i=0; i<k_nSteamEncryptedAppTicketSymmetricKeyLen; i++ )
+         _steam_api.server_symmetric_key[i] = (vg_char_base16( src[i*2+0] ) << 4) | vg_char_base16( src[i*2+1] );
+   }
+   else
+   {
+      vg_error( "No application_key file\n" );
+      return 0;
+   }
+
+       const char *pszInternalCheckInterfaceVersions = 
+               STEAMUTILS_INTERFACE_VERSION "\0"
+               STEAMNETWORKINGUTILS_INTERFACE_VERSION "\0"
+
+               STEAMGAMESERVER_INTERFACE_VERSION "\0"
+               STEAMGAMESERVERSTATS_INTERFACE_VERSION "\0"
+               STEAMHTTP_INTERFACE_VERSION "\0"
+               STEAMINVENTORY_INTERFACE_VERSION "\0"
+               STEAMNETWORKING_INTERFACE_VERSION "\0"
+               STEAMNETWORKINGMESSAGES_INTERFACE_VERSION "\0"
+               STEAMNETWORKINGSOCKETS_INTERFACE_VERSION "\0"
+               STEAMUGC_INTERFACE_VERSION "\0"
+               "\0";
+       
+   ESteamAPIInitResult init_result = SteamInternal_GameServer_Init_V2( 
+         unIP, usGamePort, usQueryPort, eServerMode, pchVersionString, pszInternalCheckInterfaceVersions, &err );
+
+   if( init_result != k_ESteamAPIInitResult_OK )
+   {
+      _steam_api.disabled = 1;
+      vg_logsteam( KRED "SteamInternal_GameServer_Init_V2() failed: '%s'\n", err );
+      return 0;
+   }
+#endif
+
+   /* Manual dispatch step 
+    * ----------------------------------------------------------------------------- */
+   SteamAPI_ManualDispatch_Init();
+
+   /*
+    * Interface Init
+    * ----------------------------------------------------------------------------- */
+
+#if defined( VG_ENGINE )
+   _steam_api.hPipe = SteamAPI_GetHSteamPipe();
+   _steam_api.pSteamUtils = SteamAPI_SteamUtils_v010();
+   _steam_api.pSteamFriends = SteamAPI_SteamFriends_v018();
+   _steam_api.pSteamUGC = SteamAPI_SteamUGC_v021();
+   _steam_api.pSteamUser = SteamAPI_SteamUser_v023();
+   _steam_api.pSteamUserStats = SteamAPI_SteamUserStats_v013();
+   _steam_api.pSteamNetworkingSockets = SteamAPI_SteamNetworkingSockets_SteamAPI_v012();
+#endif
+
+#if defined( VG_SERVER )
+   _steam_api.hPipe = SteamGameServer_GetHSteamPipe();
+   _steam_api.pSteamGameServer = SteamAPI_SteamGameServer_v015();
+   _steam_api.pSteamUtils = SteamAPI_SteamGameServerUtils_v010();
+   _steam_api.pSteamUGC = SteamAPI_SteamGameServerUGC_v021();
+   _steam_api.pSteamNetworkingSockets = SteamAPI_SteamGameServerNetworkingSockets_SteamAPI_v012();
+#endif
+
+   _steam_api.pSteamNetworkingUtils = SteamAPI_SteamNetworkingUtils_SteamAPI_v004();
+
+   SteamAPI_ISteamUtils_SetWarningMessageHook( _steam_api.pSteamUtils, cb_steam_warning );
+
+#if defined( VG_SERVER )
+   SteamAPI_ISteamGameServer_LogOnAnonymous( _steam_api.pSteamGameServer );
+#endif
+
+
+#if defined( VG_ENGINE ) 
+   strcpy( _steam_api.username_at_startup, "[unassigned]" );
+   const c8 *username = SteamAPI_ISteamFriends_GetPersonaName( _steam_api.pSteamFriends );
+   str_utf8_collapse( username, _steam_api.username_at_startup, VG_ARRAY_LEN(_steam_api.username_at_startup) );
+
+# if defined( VG_MULTIPLAYER )
+   vg_logsteam( "Requesting new authorization ticket\n" );
+
+   vg_steam_api_call *call = vg_alloc_async_steam_api_call();
+   if( call )
+   {
+      call->userdata = NULL;
+      call->cb = cb_auth_ticket_recieved;
+      call->id = SteamAPI_ISteamUser_RequestEncryptedAppTicket( _steam_api.pSteamUser, NULL, 0 );
+   }
+# endif
+#endif
+
+   return 1;
+}
+
+static const c8 *string_ESteamNetworkingConnectionState( ESteamNetworkingConnectionState s )
+{
+   if( s == k_ESteamNetworkingConnectionState_None ) return "None";
+   if( s == k_ESteamNetworkingConnectionState_Connecting) return "Connecting"; 
+   if( s == k_ESteamNetworkingConnectionState_FindingRoute) return "Finding route"; 
+   if( s == k_ESteamNetworkingConnectionState_Connected) return "Connected"; 
+   if( s == k_ESteamNetworkingConnectionState_ClosedByPeer) return "Closed By Peer"; 
+   if( s == k_ESteamNetworkingConnectionState_ProblemDetectedLocally) return "Problem detected locally"; 
+   if( s == k_ESteamNetworkingConnectionState_FinWait) return "Finwait"; 
+   if( s == k_ESteamNetworkingConnectionState_Linger) return "Linger"; 
+   if( s == k_ESteamNetworkingConnectionState_Dead) return "Dead"; 
+   return "enum-out-of-range";
+}
+
+static const c8 *string_ESteamAPICallFailure( ESteamAPICallFailure e )
+{
+   if( e == k_ESteamAPICallFailureNone ) return "None";
+   if( e == k_ESteamAPICallFailureSteamGone ) return "Steam Gone";
+   if( e == k_ESteamAPICallFailureNetworkFailure ) return "Network Failure";
+   if( e == k_ESteamAPICallFailureInvalidHandle ) return KBLK "Invalid Handle";
+   if( e == k_ESteamAPICallFailureMismatchedCallback ) return "Mismatched Callback";
+   return "enum-out-of-range";
+}
+
+void vg_steam_frame(void)
+{
+   if( _steam_api.disabled )
+      return;
+
+   SteamAPI_ManualDispatch_RunFrame( _steam_api.hPipe );
+
+   CallbackMsg_t callback;
+   while( SteamAPI_ManualDispatch_GetNextCallback( _steam_api.hPipe, &callback ) )
+   {
+      /* Check for dispatching API call results */
+      i32 type = callback.m_iCallback;
+      if( type == k_iSteamUtils_SteamAPICallCompleted )
+      {
+         SteamAPICallCompleted_t *inf = callback.m_pubParam;
+
+         b8 bFailed;
+         void *call_data = alloca( inf->m_cubParam );
+         
+         if( SteamAPI_ManualDispatch_GetAPICallResult( _steam_api.hPipe, inf->m_hAsyncCall, 
+                                                       call_data, inf->m_cubParam, 
+                                                       inf->m_iCallback, &bFailed ) )
+         {
+            vg_logsteam( "api_call_completed %lu\n", inf->m_hAsyncCall );
+            
+            int j=0;
+            for( int i=0; i<_steam_api.api_call_count; i++ )
+            {
+               if( _steam_api.api_calls[i].id != inf->m_hAsyncCall )
+                  _steam_api.api_calls[j ++] = _steam_api.api_calls[i];
+               else
+                  _steam_api.api_calls[i].cb( call_data, _steam_api.api_calls[i].userdata );
+            }
+
+            if( _steam_api.api_call_count == j )
+               vg_error( "No tracker was register for API call\n" );
+
+            _steam_api.api_call_count = j;
+         } 
+         else 
+         { 
+            enum ESteamAPICallFailure e = 
+               SteamAPI_ISteamUtils_GetAPICallFailureReason( _steam_api.pSteamUtils, inf->m_hAsyncCall );
+            const c8 *fail_str = string_ESteamAPICallFailure( e );
+            vg_logsteam( KRED "Error getting call result on %lu (code %d)\n", inf->m_hAsyncCall, fail_str );
+         }
+      } 
+      else 
+      {
+         /* 
+          * Look at callback.m_iCallback to see what kind of callback it is,
+          * and dispatch to appropriate handler(s)
+          * void *data = callback.m_pubParam;
+          */
+         if( type == k_iSteamUser_SteamServersConnected )
+            vg_success( "Steam servers connected" );
+         else if( type == k_iSteamUser_SteamConnectFailure )
+         {
+            SteamServerConnectFailure_t *inf = callback.m_pubParam;
+            vg_logsteam( KRED "Steam server connect failure, code %d, retrying: %d\n", inf->m_eResult, inf->m_bStillRetrying );
+         }
+         else if( type == k_iSteamUser_SteamServersDisconnected )
+         {
+            SteamServersDisconnected_t *inf = callback.m_pubParam;
+            vg_logsteam( "Steam servers disconnect, code %d\n", inf->m_eResult );
+         }
+         else if( type == k_iSteamNetworkingSockets_SteamNetConnectionStatusChangedCallback )
+         {
+            SteamNetConnectionStatusChangedCallback_t *inf = callback.m_pubParam;
+            const c8 *status_string = string_ESteamNetworkingConnectionState( inf->m_info.m_eState );
+            vg_logsteam( "Connection status changed: %x -> %s\n   Debug: '%s'\n   EndDebug: '%s'\n", 
+                           inf->m_hConn, status_string, inf->m_info.m_szConnectionDescription,
+                           inf->m_info.m_szEndDebug );
+            
+            if( _steam_api.cb_connection_changed )
+               _steam_api.cb_connection_changed( inf );
+         }
+         else if( type == k_iSteamNetworkingSockets_SteamNetAuthenticationStatus )
+         {
+            SteamNetAuthenticationStatus_t *inf = callback.m_pubParam;
+            vg_logsteam( "Steam Authentication Status: %d\n   Debug: '%s'\n", inf->m_eAvail, inf->m_debugMsg );
+         }
+      }
+      
+      SteamAPI_ManualDispatch_FreeLastCallback( _steam_api.hPipe );
+   }
+}
+
+VG_API void _vg_steam_shutdown(void)
+{
+#if defined( VG_SERVER )
+   if( _steam_api.is_connected )
+   {
+      SteamAPI_ISteamGameServer_LogOff( _steam_api.pSteamGameServer );
+      _steam_api.is_connected = 0;
+   }
+   SteamGameServer_Shutdown();
+#else
+   SteamAPI_Shutdown();
+#endif
+}
+
+vg_steam_api_call *vg_alloc_async_steam_api_call(void)
+{
+   if( _steam_api.api_call_count == VG_ARRAY_LEN(_steam_api.api_calls) )
+      return NULL;
+   return &_steam_api.api_calls[ _steam_api.api_call_count ++ ];
+}
+
+#if defined( VG_ENGINE )
+void vg_steam_set_achievement( const c8 *name, b8 yes )
+{
+   if( _steam_api.disabled || _steam_api.demo_mode )
+      return;
+   
+   if( yes )
+   {
+      if( SteamAPI_ISteamUserStats_SetAchievement( _steam_api.pSteamUserStats, name ) )
+      {
+         vg_logsteam( KGRN "Set achievement '%s'\n", name ); 
+         SteamAPI_ISteamUserStats_StoreStats( _steam_api.pSteamUserStats );
+      }
+   }
+   else
+   {
+      if( SteamAPI_ISteamUserStats_ClearAchievement( _steam_api.pSteamUserStats, name ) )
+      {
+         vg_logsteam( KBLK "Clear achievement '%s'\n", name ); 
+         SteamAPI_ISteamUserStats_StoreStats( _steam_api.pSteamUserStats );
+      }
+   }
+}
+#endif
diff --git a/source/engine/texture.c b/source/engine/texture.c
deleted file mode 100644 (file)
index 7acd81c..0000000
+++ /dev/null
@@ -1,430 +0,0 @@
-#define QOI_OP_INDEX  0x00 /* 00xxxxxx */
-#define QOI_OP_DIFF   0x40 /* 01xxxxxx */
-#define QOI_OP_LUMA   0x80 /* 10xxxxxx */
-#define QOI_OP_RUN    0xc0 /* 11xxxxxx */
-#define QOI_OP_RGB    0xfe /* 11111110 */
-#define QOI_OP_RGBA   0xff /* 11111111 */
-#define QOI_MASK_2    0xc0 /* 11000000 */
-
-#define QOI_COLOR_HASH(C) (C.rgba[0]*3 + C.rgba[1]*5 + C.rgba[2]*7 + C.rgba[3]*11)
-#define QOI_MAGIC \
-   (((u32)'q')       | ((u32)'o') << 8 | \
-    ((u32)'i') << 16 | ((u32)'f') << 24)
-
-static const u8 qoi_padding[8] = {0,0,0,0,0,0,0,1};
-
-#define cpu_to_big32 big32_to_cpu
-VG_TIER_0 static inline u32 big32_to_cpu( u32 x )
-{
-   return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | (x >> 24);
-}
-
-VG_TIER_0 bool vg_qoi_validate( const qoi_desc *desc )
-{
-   if( (desc->width == 0)    || (desc->height == 0) ||
-       (desc->width >= 2048) || (desc->height >= 2048) )
-   {
-      vg_error( "QOI file is invalid; Unpermitted size (%ux%u)\n", desc->width, desc->height );
-      return 0;
-   }
-
-   if( !(desc->channels == 3 || desc->channels == 4) )
-   {
-      vg_error( "QOI file is invalid; Only 3 or 4 channels permitted. File has %u\n", desc->channels );
-      return 0;
-   }
-
-   return 1;
-}
-
-/* Initalize stream context and return the number of bytes required to store the final RGB/A image data. */
-VG_TIER_0 u32 vg_qoi_stream_init( qoi_desc *desc, vg_stream *stream )
-{
-   vg_stream_read( stream, desc, sizeof(qoi_desc) );
-   if( desc->magic != QOI_MAGIC )
-   {
-      vg_error( "QOI file is invalid; Magic Number incorrect.\n" );
-      return 0;
-   }
-
-   desc->width = big32_to_cpu( desc->width );
-   desc->height = big32_to_cpu( desc->height );
-
-   if( vg_qoi_validate( desc ) )
-      return desc->width * desc->height * desc->channels;
-   else return 0;
-}
-
-VG_TIER_0 void vg_qoi_stream_decode( qoi_desc *desc, vg_stream *stream, u8 *pixels, bool v_flip )
-{
-   qoi_rgba_t index[64], px;
-   vg_zero_mem( index, sizeof(qoi_rgba_t)*64 );
-   vg_zero_mem( &px, sizeof(qoi_rgba_t) );
-   px.rgba[3] = 255;
-
-   u32 run=0;
-
-   for( u32 y=0; y<desc->height; y ++ )
-   {
-      for( u32 x=0; x<desc->width; x ++ )
-      {
-         if( run > 0 )
-            run --;
-         else 
-         {
-            u8 b1;
-            vg_stream_read( stream, &b1, 1 );
-
-            if( b1 == QOI_OP_RGB ) 
-               vg_stream_read( stream, px.rgba, 3 );
-            else if( b1 == QOI_OP_RGBA ) 
-               vg_stream_read( stream, px.rgba, 4 );
-            else if( (b1 & QOI_MASK_2) == QOI_OP_INDEX ) 
-               px = index[b1];
-            else if( (b1 & QOI_MASK_2) == QOI_OP_DIFF ) 
-            {
-               px.rgba[0] += (i32)((b1 >> 4) & 0x03) - 2;
-               px.rgba[1] += (i32)((b1 >> 2) & 0x03) - 2;
-               px.rgba[2] += (i32)( b1       & 0x03) - 2;
-            }
-            else if( (b1 & QOI_MASK_2) == QOI_OP_LUMA ) 
-            {
-               u8 b2;
-               vg_stream_read( stream, &b2, 1 );
-               i32 vg = (i32)(b1 & 0x3f) - 32;
-               px.rgba[0] += vg - 8 + (i32)((b2 >> 4) & 0x0f);
-               px.rgba[1] += vg;
-               px.rgba[2] += vg - 8 + (i32)(b2 & 0x0f);
-            }
-            else if( (b1 & QOI_MASK_2) == QOI_OP_RUN ) 
-               run = (b1 & 0x3f);
-            index[ QOI_COLOR_HASH(px) % 64 ] = px;
-         }
-
-         u32 row = v_flip? desc->height-(y+1): y;
-         for( u32 i=0; i < desc->channels; i ++ )
-            pixels[ ((row*desc->width) + x)*desc->channels + i ] = px.rgba[i];
-      }
-   }
-}
-
-VG_TIER_0 u32 vg_query_qoi_max_compressed_size( const qoi_desc *desc )
-{
-   return desc->width * desc->height * (desc->channels + 1) + sizeof(qoi_desc) + sizeof(qoi_padding);
-}
-
-VG_TIER_0 u32 vg_qoi_stream_encode( const qoi_desc *desc, const u8 *pixels, vg_stream *stream, bool v_flip )
-{
-   if( !vg_qoi_validate( desc ) )
-      return 0;
-
-   qoi_desc file_header = *desc;
-   file_header.magic  = QOI_MAGIC;
-   file_header.width  = cpu_to_big32( file_header.width );
-   file_header.height = cpu_to_big32( file_header.height );
-   vg_stream_write( stream, &file_header, sizeof(qoi_desc) );
-   
-   qoi_rgba_t index[64];
-   vg_zero_mem( index, sizeof(qoi_rgba_t)*64 );
-   qoi_rgba_t px_prev = { .rgba = { 0, 0, 0, 255 } };
-   qoi_rgba_t px = px_prev;
-
-   u32 run = 0;
-   for( u32 y=0; y<desc->height; y ++ )
-   {
-      for( u32 x=0; x<desc->width; x ++ )
-      {
-         u32 row = v_flip? desc->height-(y+1): y;
-         for( u32 i=0; i < desc->channels; i ++ )
-            px.rgba[i] = pixels[ ((row*desc->width) + x)*desc->channels + i ];
-
-         if( px.v == px_prev.v )
-         {
-            run ++;
-            if( run == 62 || ((y+1 == desc->height) && (x+1 == desc->width)) )
-            {
-               u8 b1 = QOI_OP_RUN | (run - 1);
-               vg_stream_write( stream, &b1, 1 );
-               run = 0;
-            }
-         }
-         else
-         {
-            if( run > 0 )
-            {
-               u8 b1 = QOI_OP_RUN | (run - 1);
-               vg_stream_write( stream, &b1, 1 );
-               run = 0;
-            }
-
-            u32 index_pos = QOI_COLOR_HASH( px ) % 64;
-            if( index[ index_pos ].v == px.v )
-            {
-               u8 b1 = QOI_OP_INDEX | index_pos;
-               vg_stream_write( stream, &b1, 1 );
-            }
-            else
-            {
-               index[ index_pos ] = px;
-               if( px.rgba[3] == px_prev.rgba[3] )
-               {
-                  i8 vr = px.rgba[0] - px_prev.rgba[0],
-                     vg = px.rgba[1] - px_prev.rgba[1],
-                     vb = px.rgba[2] - px_prev.rgba[2],
-                     vg_r = vr - vg,
-                     vg_b = vb - vg;
-
-                  if( vr > -3 && vr < 2 &&
-                      vg > -3 && vg < 2 &&
-                      vb > -3 && vb < 2 ) 
-                  {
-                     vg_stream_write( stream, (u8[]){ QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2) }, 1 );
-                  }
-                  else if( vg_r >  -9 && vg_r <  8 &&
-                           vg   > -33 && vg   < 32 &&
-                           vg_b >  -9 && vg_b <  8 ) 
-                  {
-                     vg_stream_write( stream, (u8[]){ QOI_OP_LUMA     | (vg   + 32),
-                                                      (vg_r + 8) << 4 | (vg_b +  8) }, 2 );
-                  }
-                  else 
-                  {
-                     vg_stream_write( stream, (u8[]){ QOI_OP_RGB }, 1 );
-                     vg_stream_write( stream, px.rgba, 3 );
-                  }
-               }
-               else
-               {
-                  vg_stream_write( stream, (u8 []){ QOI_OP_RGBA }, 1 );
-                  vg_stream_write( stream, px.rgba, 4 );
-               }
-            }
-         }
-         px_prev = px;
-      }
-   }
-   vg_stream_write( stream, qoi_padding, sizeof(qoi_padding) );
-   return 1;
-}
-
-/* VG_PART 
- * ------------------------------------------------------------------------------------------------------------------ */
-
-struct
-{
-   GLuint error2d, errorcube;
-}
-_vg_tex;
-
-VG_API void _vg_tex_init(void)
-{
-   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) );
-
-   static u8 const_vg_tex2d_err[] =
-   {
-      0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff,
-      0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 
-      0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff,
-      0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 
-   };
-
-   glGenTextures( 1, &_vg_tex.error2d );
-   glBindTexture( GL_TEXTURE_2D, _vg_tex.error2d );
-   glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, const_vg_tex2d_err );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
-
-   glGenTextures( 1, &_vg_tex.errorcube );
-   glBindTexture( GL_TEXTURE_CUBE_MAP, _vg_tex.errorcube );
-   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
-   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
-   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
-   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
-   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
-
-   for( u32 j=0; j<6; j ++ ) 
-   {
-      glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, 0, GL_RGBA, 4, 4, 
-                    0, GL_RGBA, GL_UNSIGNED_BYTE, const_vg_tex2d_err );
-   }
-}
-
-struct tex_upload_task 
-{
-   vg_tex *tex;
-   u32 width, height, channels, flags;
-   u8 image_buffer[];
-};
-
-VG_API_INTERNAL static void _vg_tex_upload( struct tex_upload_task *in_args, vg_async_info *async )
-{
-   VG_ASSERT( _vg_tex.errorcube && _vg_tex.error2d );
-
-   u32 flags = in_args->flags;
-   vg_tex *tex = in_args->tex;
-
-   if( flags & VG_TEX_ERROR )
-   {
-      tex->name  = (flags & VG_TEX_CUBEMAP)? _vg_tex.errorcube: _vg_tex.error2d;
-      tex->flags = (flags & VG_TEX_CUBEMAP) | VG_TEX_ERROR | VG_TEX_COMPLETE;
-   }
-   else
-   {
-      u32 pixel_format = 0;
-      if( in_args->channels == 3 ) pixel_format = GL_RGB;
-      else if( in_args->channels == 4 ) pixel_format = GL_RGBA;
-      else vg_fatal_error( "Can't upload texture with '%u' channels.\n", in_args->channels );
-
-      glGenTextures( 1, &tex->name );
-
-      u32 filter_min = 0,
-          filter_mag = 0;
-      if( flags & VG_TEX_LINEAR )
-      {
-         if( flags & VG_TEX_NOMIP ) filter_min = GL_LINEAR;
-         else                       filter_min = GL_LINEAR_MIPMAP_LINEAR;
-         filter_mag = GL_LINEAR;
-      }
-      else
-      {
-         VG_ASSERT( flags & VG_TEX_NEAREST );
-         filter_min = GL_NEAREST;
-         filter_mag = GL_NEAREST;
-      }
-
-      if( flags & VG_TEX_CUBEMAP )
-      {
-         u32 w = in_args->width,
-             h = in_args->height/6;
-
-         glBindTexture(   GL_TEXTURE_CUBE_MAP, tex->name );
-         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, filter_min );
-         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, filter_mag );
-         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); /* can this be anything else? */
-         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
-         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
-
-         for( u32 j=0; j<6; j ++ ) 
-         {
-            u32 offset = w*h*j*in_args->channels;
-            glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, 0, pixel_format,
-                           w, h,
-                           0, pixel_format, GL_UNSIGNED_BYTE, in_args->image_buffer + offset );
-         }
-
-         if( !(flags & VG_TEX_NOMIP) )
-            glGenerateMipmap( GL_TEXTURE_CUBE_MAP );
-      }
-      else
-      {
-         glBindTexture( GL_TEXTURE_2D, tex->name );
-         glTexImage2D( GL_TEXTURE_2D, 0, pixel_format, in_args->width, in_args->height,
-                        0, pixel_format, GL_UNSIGNED_BYTE, in_args->image_buffer );
-
-         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_min );
-         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_mag );
-
-         u32 wrap_s = 0,
-             wrap_t = 0;
-
-         if( flags & VG_TEX_CLAMP )
-         {
-            wrap_s = GL_CLAMP_TO_EDGE;
-            wrap_t = GL_CLAMP_TO_EDGE;
-         }
-         else
-         {
-            VG_ASSERT( flags & VG_TEX_REPEAT );
-            wrap_s = GL_REPEAT;
-            wrap_t = GL_REPEAT;
-         }
-
-         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s );
-         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t );
-
-         if( !(flags & VG_TEX_NOMIP) )
-            glGenerateMipmap( GL_TEXTURE_2D );
-      }
-      tex->flags = flags | VG_TEX_COMPLETE;
-   }
-}
-
-VG_API bool _vg_tex_load_stream( vg_tex *out_tex, vg_stream *in_stream, u32 flags )
-{
-   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_BACKGROUND ) );
-
-   qoi_desc qoi;
-   u32 size = vg_qoi_stream_init( &qoi, in_stream );
-   if( size )
-   {
-      _vg_async_context_push_groups( VG_ASYNC_GROUP_OPENGL, 0 );
-      struct tex_upload_task *out_args = _vg_async_alloc( VG_THREAD_MAIN_ID, sizeof( struct tex_upload_task ) + size );
-      vg_qoi_stream_decode( &qoi, in_stream, out_args->image_buffer, flags & VG_TEX_FLIP_V? 1: 0 );
-      out_args->tex = out_tex;
-      out_args->width = qoi.width;
-      out_args->height = qoi.height;
-      out_args->channels = qoi.channels;
-      out_args->flags = flags;
-      _vg_async_send( out_args, (vg_async_fn)_vg_tex_upload );
-      _vg_async_context_pop_groups();
-      return 1;
-   }
-   else 
-      return 0;
-}
-
-VG_API void _vg_tex_load( vg_tex *out_tex, const c8 *path, u32 flags )
-{
-   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_BACKGROUND ) );
-
-   bool error = 0;
-   vg_stream file;
-   if( vg_file_stream_open( &file, path, VG_STREAM_READ ) )
-   {
-      if( !_vg_tex_load_stream( out_tex, &file, flags ) )
-         error = 1;
-      vg_file_stream_close( &file );
-   }
-   else 
-      error = 1;
-
-   if( error )
-   {
-      _vg_async_context_push_groups( VG_ASYNC_GROUP_OPENGL, 0 );
-      struct tex_upload_task *out_args = _vg_async_alloc( VG_THREAD_MAIN_ID, sizeof( struct tex_upload_task ) );
-      out_args->tex = out_tex;
-      out_args->width = 0;
-      out_args->height = 0;
-      out_args->channels = 0;
-      out_args->flags = VG_TEX_ERROR;
-      _vg_async_send( out_args, (vg_async_fn)_vg_tex_upload );
-      _vg_async_context_pop_groups();
-   }
-}
-
-VG_API u32  vg_tex_name( GLuint target, vg_tex *tex )
-{
-   if( !tex ) 
-   {
-      return (target == GL_TEXTURE_2D)? _vg_tex.error2d: _vg_tex.errorcube;
-   }
-   if( tex->flags & VG_TEX_COMPLETE ) return tex->name;
-   else                               return (target == GL_TEXTURE_2D)? _vg_tex.error2d: _vg_tex.errorcube;
-}
-
-VG_API void vg_tex_bind( GLuint target, vg_tex *tex, u32 slot )
-{
-   glActiveTexture( GL_TEXTURE0 + slot );
-   glBindTexture( target, vg_tex_name( target, tex ) );
-}
-
-VG_API void vg_tex_delete( vg_tex *tex )
-{
-   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_OPENGL ) );
-   VG_ASSERT( tex->flags & VG_TEX_COMPLETE );
-   if( !(tex->flags & VG_TEX_ERROR) )
-      glDeleteTextures( 1, &tex->name );
-   vg_zero_mem( tex, sizeof(vg_tex) );
-}
diff --git a/source/engine/ui.c b/source/engine/ui.c
deleted file mode 100644 (file)
index fc2d07d..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-#include "common_api.h"
-#include "opengl.h"
-#include "graphics_api.h"
-#include "engine_interface.h"
-#include "engine_backend.h"
-#include "glfw.h"
-
-struct graphics_target _ui_surface = 
-{
-   .mode = k_graphics_mode_software,
-   .size = { 1920, 1080 },
-};
-
-GLuint _ui_surface_texture;
-GLuint quad_vao, quad_vbo;
-
-void _engine_ui_init(void)
-{
-   _ui_surface.buffer = _heap_allocate( 1920*1080*4 );
-
-   glGenTextures( 1, &_ui_surface_texture );
-   glActiveTexture( GL_TEXTURE0 );
-   glBindTexture( GL_TEXTURE_2D, _ui_surface_texture );
-   glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
-   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 );
-
-   f32 quad[] = 
-   { 
-      0.00f,0.00f, 1.00f,1.00f, 0.00f,1.00f,
-      0.00f,0.00f, 1.00f,0.00f, 1.00f,1.00f,    
-   };
-
-   glGenVertexArrays( 1, &quad_vao );
-   glGenBuffers( 1, &quad_vbo );
-   glBindVertexArray( quad_vao );
-   glBindBuffer( GL_ARRAY_BUFFER, quad_vbo );
-   glBufferData( GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW );
-   glBindVertexArray( quad_vao );
-   glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, sizeof(f32)*2, (void*)0 );
-   glEnableVertexAttribArray( 0 );
-}
-
-void _engine_ui_pre_render(void)
-{
-   ASSERT_CRITICAL( _engine.w <= 1920 );
-   ASSERT_CRITICAL( _engine.h <= 1080 );
-   
-   f64 x, y;
-   glfwGetCursorPos( _engine.window_handle, &x, &y );
-   _ui_set_mouse( x, y );
-   _ui_update();
-
-   _graphics_set_target( &_ui_surface );
-   _graphics_viewport( 0, 0, _engine.w, _engine.h );
-   //_graphics_fill_rect( (i16[]){ 0, 0, _engine.w, _engine.h }, (union colour){{0,0,0,255}} );
-}
-
-void _engine_ui_input_character( u32 codepoint )
-{
-   if( (codepoint >= (u32)' ') && (codepoint <= (u32)'~') )
-      _ui_input_text( (const c8[]){ codepoint, 0 } );
-}
-
-void _engine_ui_post_render(void)
-{
-   glEnable( GL_BLEND );
-   glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
-   glBlendEquation( GL_FUNC_ADD );
-
-   glActiveTexture( GL_TEXTURE0 );
-   glBindTexture( GL_TEXTURE_2D, _ui_surface_texture );
-   glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 1920, 1080, GL_BGRA, GL_UNSIGNED_BYTE, _ui_surface.buffer );
-
-   _shader_bind( k_shader_blit );
-   _shader_blit_uTexMain( 0 );
-   _shader_blit_uFlip( 1 );
-   _shader_blit_uInverseRatio( (f32[2]){ (f64)_engine.w/1920.0, (f64)_engine.h/1080.0 } );
-   glBindVertexArray( quad_vao );
-   glDrawArrays( GL_TRIANGLES, 0, 6 );
-
-   glDisable( GL_BLEND );
-}
diff --git a/source/engine/ui.c_UNUSED b/source/engine/ui.c_UNUSED
new file mode 100644 (file)
index 0000000..14ca3cc
--- /dev/null
@@ -0,0 +1,71 @@
+#include "foundation.h"
+#include "vg_engine.h"
+#include "vg_graphics.h"
+#include "vg_render.h"
+#include "vg_opengl.h"
+#include "vg_shader.h"
+
+struct graphics_target _ui_surface = 
+{
+   .mode = k_graphics_mode_software,
+   .size = { 1920, 1080 },
+};
+
+static GLuint _ui_surface_texture;
+
+#include <unistd.h>
+void _engine_ui_init(void)
+{
+   _ui_surface.buffer = _heap_allocate( 1920*1080*4 );
+
+   glGenTextures( 1, &_ui_surface_texture );
+   glActiveTexture( GL_TEXTURE0 );
+   glBindTexture( GL_TEXTURE_2D, _ui_surface_texture );
+   glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL );
+   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+   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 );
+}
+
+void _engine_ui_pre_render(void)
+{
+   ASSERT_CRITICAL( _engine.w <= 1920 );
+   ASSERT_CRITICAL( _engine.h <= 1080 );
+   
+   f64 x = 0, y = 0;
+#if 0
+   glfwGetCursorPos( _engine.window_handle, &x, &y );
+#endif
+   _ui_set_mouse( x, y );
+   _ui_update();
+
+   _graphics_set_target( &_ui_surface );
+   _graphics_viewport( 0, 0, _engine.w, _engine.h );
+   //_graphics_fill_rect( (i16[]){ 0, 0, _engine.w, _engine.h }, (union colour){{0,0,0,255}} );
+}
+
+void _engine_ui_input_character( u32 codepoint )
+{
+   if( (codepoint >= (u32)' ') && (codepoint <= (u32)'~') )
+      _ui_input_text( (const c8[]){ codepoint, 0 } );
+}
+
+void _engine_ui_post_render(void)
+{
+   glEnable( GL_BLEND );
+   glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+   glBlendEquation( GL_FUNC_ADD );
+
+   glActiveTexture( GL_TEXTURE0 );
+   glBindTexture( GL_TEXTURE_2D, _ui_surface_texture );
+   glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 1920, 1080, GL_BGRA, GL_UNSIGNED_BYTE, _ui_surface.buffer );
+
+   _shader_bind( k_shader_blit );
+   _shader_blit_uTexMain( 0 );
+   _shader_blit_uFlip( 1 );
+   _shader_blit_uInverseRatio( (f32[2]){ (f64)_engine.w/1920.0, (f64)_engine.h/1080.0 } );
+
+   _render_fullscreen_quad();
+   glDisable( GL_BLEND );
+}
diff --git a/source/engine/vg_audio.c b/source/engine/vg_audio.c
new file mode 100644 (file)
index 0000000..876624f
--- /dev/null
@@ -0,0 +1,1358 @@
+#include "SDL3/SDL.h"
+#include "SDL3/SDL_audio.h"
+#include "vg_audio.h"
+#include "vg_audio_dsp.h"
+#include "foundation.h"
+#include "vg_audio_vorbis.h"
+#include "common_maths.h"
+#include "vg_async.h"
+
+#include <unistd.h>
+
+SDL_Mutex *_audio_mutex;
+_Thread_local static bool _audio_have_lock = 0;
+static f32 _master_volume = 1.0f;
+
+enum channel_stage
+{
+   k_channel_stage_none = 0,
+   k_channel_stage_allocation,
+   k_channel_stage_active,
+   k_channel_stage_orphan
+};
+
+enum channel_activity
+{
+   k_channel_activity_wake,
+   k_channel_activity_playing,
+   k_channel_activity_paused,
+   k_channel_activity_end,
+   k_channel_activity_error
+};
+
+struct audio_lfo
+{
+   enum channel_stage stage;
+
+   struct audio_lfo_controls
+   {
+      u32 period_in_samples;
+      enum lfo_wave_type
+      {
+         k_lfo_triangle,
+         k_lfo_square,
+         k_lfo_saw,
+         k_lfo_polynomial_bipolar
+      }
+      wave_type;
+
+      f32 polynomial_coefficient, sqrt_polynomial_coefficient;
+      u32 flags;
+   }
+   controls;
+
+   struct audio_lfo_state
+   {
+      u32 time, last_period_in_samples, frame_reference_count, time_at_frame_start;
+      struct audio_lfo_controls *controls;
+   }
+   state;
+};
+
+#define LFO_FLAG_PERIOD_CHANGED 0x1
+
+struct audio_channel
+{
+   enum channel_stage stage;
+
+   c8   ui_name[32];
+   u32  ui_colour;
+   i32  ui_volume, ui_pan;
+   f32  ui_spacial_volume, ui_spacial_pan;
+
+   enum channel_activity ui_activity;
+   u16 group;
+
+   struct audio_clip *clip;
+
+   /* the controls structure is copied into the stack of the mixer function so it can work without locking. */
+   struct audio_channel_controls
+   {
+      u32 flags;
+
+      i32 volume_target, volume_slew_rate_per_sample;
+      i32 pan_target, pan_slew_rate_per_sample;
+      f32 sampling_rate_multiplier;
+
+      audio_channel_id lfo_id;
+      f32 lfo_attenuation_amount; /* multiply volume by (1 + value) */
+
+      f32 spacial_falloff[4]; /* xyz, range */
+      bool pause;
+   }
+   controls;
+
+   /* the channel state can be accessed when channel stage is in allocation, or by the mixer thread post allocation. */
+   struct audio_channel_state
+   {
+      enum channel_activity activity;
+
+      u32 cursor;
+      i32 volume, pan, 
+          spacial_volume, spacial_pan;
+      bool spacial_warm;
+
+      union
+      {
+#if 0
+         struct synth_bird *bird;
+#endif
+         stb_vorbis *vorbis;
+      }
+      decoder_handle;
+
+      u32 loaded_clip_length;
+   }
+   state;
+};
+
+struct
+{
+   void *decoding_buffer;
+   u32 samples_written_last_audio_frame;
+
+   struct audio_lfo lfos[ AUDIO_LFOS ];
+   struct audio_channel channels[ AUDIO_CHANNELS ];
+   stb_vorbis_alloc vorbis_decoders[ AUDIO_CHANNELS ];
+
+   bool inspector_open;
+
+   struct audio_master_controls
+   {
+      f32 listener_position[3],
+          listener_right_ear_direction[3],
+          listener_velocity[3];
+      bool dsp_enabled;
+      i32 volume_target;
+   }
+   controls;
+
+   struct audio_master_state
+   {
+      i32 volume, volume_at_frame_start;
+   }
+   state;
+
+   f32 master_volume_ui;
+   i32 dsp_enabled_ui;
+
+   bool working;
+
+   i32 sdl_output_device;
+}
+static _audio = 
+{
+   .master_volume_ui = 1,
+   .dsp_enabled_ui = 1,
+};
+
+void _audio_lock(void)
+{
+   SDL_LockMutex( _audio_mutex );
+   _audio_have_lock = 1;
+}
+
+void _audio_unlock(void)
+{
+   _audio_have_lock = 0;
+   SDL_UnlockMutex( _audio_mutex );
+}
+
+static void _audio_assert_lock(void)
+{
+   if( _audio_have_lock == 0 )
+   {
+      $log( $fatal, {"AUDIO LOCK WAS NOT HELD"} );
+      _fatal_exit();
+   }
+}
+
+/* clip loading from disk
+ * -------------------------------------------------------------------------------
+ */
+bool audio_clip_load( struct audio_clip *clip, struct stack_allocator *stack )
+{
+   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
+
+   /* load in directly */
+   u32 format = clip->flags & AUDIO_FLAG_FORMAT;
+   if( format == k_audio_format_vorbis )
+   {
+      if( clip->path )
+      {
+         struct stream file;
+         if( stream_open_file( &file, clip->path, k_stream_read ) )
+         {
+            void *buffer = stream_read_all( &file, stack, 8, &clip->size );
+            stream_close( &file );
+
+            f32 mb = (f32)(clip->size) / (1024.0f*1024.0f);
+            clip->any_data = buffer;
+            $log( $ok, {"Loaded audio clip '"}, {clip->path}, {"' ("}, $float( mb, .decimals=2 ), {" MB"} );
+            return 1;
+         }
+         else
+         {
+            $log( $error, {"Audio failed to load '"},{clip->path},{"'"} );
+            clip->any_data = NULL;
+            clip->size = 0;
+            return 0;
+         }
+      }
+      else
+      {
+         $log( $error, {"No path specified, embeded vorbis unsupported"} );
+         return 0;
+      }
+   }
+   else if( format == k_audio_format_stereo )
+   {
+      $log( $error, {"Unsupported format (Stereo uncompressed)"} );
+      return 0;
+   }
+   else if( format == k_audio_format_bird )
+   {
+      ASSERT_CRITICAL( 0 );
+#if 0
+      if( !clip->any_data )
+      {
+         $log( $error, {"No data, external birdsynth unsupported"} );
+         return 0;
+      }
+
+      struct synth_bird *bird = stack_allocate( stack, sizeof(struct synth_bird), 8, NULL );
+      bird->settings = clip->any_data;
+      clip->any_data = bird;
+      _info( "Loaded bird synthesis pattern (%u bytes)\n", clip->size );
+      return 1;
+#endif
+      return 0;
+   }
+   else
+   {
+      if( !clip->path )
+      {
+         $log( $error, {"No path specified, embeded mono unsupported"} );
+         return 0;
+      }
+
+      u32 temp_frame = _start_temporary_frame();
+      stb_vorbis_alloc alloc = 
+      {
+         .alloc_buffer = stack_allocate( _temporary_stack_allocator(), AUDIO_DECODE_SIZE, 8, "Vorbis alloc buffer (temp)" ),
+         .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE
+      };
+
+      struct stream file;
+      if( stream_open_file( &file, clip->path, k_stream_read ) )
+      {
+         u32 fsize;
+         void *filedata = stream_read_all( &file, stack, 8, &fsize );
+         stream_close( &file );
+
+         int err;
+         stb_vorbis *decoder = stb_vorbis_open_memory( filedata, fsize, &err, &alloc );
+         if( !decoder )
+         {
+            _end_temporary_frame( temp_frame );
+            $log( $error, {"Audio failed to load '"},{clip->path},{"', error code: "}, $signed(err) );
+            clip->any_data = NULL;
+            clip->size = 0;
+            return 0;
+         }
+
+         /* only mono is supported in uncompressed */
+         u32 length_samples = stb_vorbis_stream_length_in_samples( decoder ),
+             data_size      = length_samples * sizeof(i16);
+
+         clip->any_data = stack_allocate( stack, data_size, 8, NULL );
+         clip->size = length_samples;
+         int read_samples = stb_vorbis_get_samples_i16_downmixed( decoder, clip->any_data, length_samples );
+         _end_temporary_frame( temp_frame );
+
+         if( read_samples == length_samples )
+         {
+            f32 mb = (f32)(clip->size) / (1024.0f*1024.0f);
+            $log( $ok, {"Loaded audio clip '"}, {clip->path}, {"' ("}, $float( mb, .decimals=2 ), {" MB"} );
+            return 1;
+         }
+         else
+         {
+            $log( $error, {"Audio failed to load '"},{clip->path},{"'.. Decode error (samples read did not match)"} );
+            clip->any_data = NULL;
+            clip->size = 0;
+            return 0;
+         }
+      }
+      else
+      {
+         _end_temporary_frame( temp_frame );
+         $log( $error, {"Audio failed to load '"},{clip->path},{"'"} );
+         clip->any_data = NULL;
+         clip->size = 0;
+         return 0;
+      }
+   }
+}
+
+u32 audio_clip_loadn( struct audio_clip *arr, u32 count, struct stack_allocator *stack )
+{
+   u32 succeeded = 0;
+   for( u32 i=0; i<count; i++ )
+      succeeded += (u32)audio_clip_load( &arr[i], stack );
+   return succeeded;
+}
+
+/* 
+ * -------------------------------------------------------------------------------
+ */
+
+static struct audio_channel *get_audio_channel( audio_channel_id id )
+{
+   ASSERT_CRITICAL( (id > 0) && (id <= AUDIO_CHANNELS) );
+   return &_audio.channels[ id-1 ];
+}
+
+static struct audio_channel_controls *get_audio_channel_controls( audio_channel_id id )
+{
+   _audio_assert_lock();
+   ASSERT_CRITICAL( (id > 0) && (id <= AUDIO_CHANNELS) );
+   return &_audio.channels[ id-1 ].controls;
+}
+
+static struct audio_channel_state *get_audio_channel_state( audio_channel_id id )
+{
+   ASSERT_CRITICAL( (id > 0) && (id <= AUDIO_CHANNELS) );
+   return &_audio.channels[ id-1 ].state;
+}
+
+static struct audio_lfo *get_audio_lfo( audio_channel_id lfo_id )
+{
+   ASSERT_CRITICAL( (lfo_id > 0) && (lfo_id <= AUDIO_LFOS) );
+   return &_audio.lfos[ lfo_id-1 ];
+}
+
+static struct audio_lfo_controls *get_audio_lfo_controls( audio_channel_id lfo_id )
+{
+   _audio_assert_lock();
+   ASSERT_CRITICAL( (lfo_id > 0) && (lfo_id <= AUDIO_LFOS) );
+   return &_audio.lfos[ lfo_id-1 ].controls;
+}
+
+static struct audio_lfo_state *get_audio_lfo_state( audio_channel_id lfo_id )
+{
+   ASSERT_CRITICAL( (lfo_id > 0) && (lfo_id <= AUDIO_LFOS) );
+   return &_audio.lfos[ lfo_id-1 ].state;
+}
+
+static void audio_decode_uncompressed_mono( i16 *src, u32 count, f32 *dst )
+{
+   for( u32 i=0; i<count; i++ )
+   {
+      dst[ i*2 + 0 ] = ((f32)src[i]) * (1.0f/32767.0f);
+      dst[ i*2 + 1 ] = ((f32)src[i]) * (1.0f/32767.0f);
+   }
+}
+
+/* main channels
+ * ---------------------------------------------------------------------------------------- */
+
+audio_channel_id _audio_get_first_idle_channel(void)
+{
+   _audio_assert_lock();
+   for( int id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      struct audio_channel *channel = get_audio_channel( id );
+
+      if( channel->stage == k_channel_stage_none )
+      {
+         channel->stage = k_channel_stage_allocation;
+         channel->ui_name[0] = 0;
+         channel->ui_colour = 0x00333333;
+         channel->group = 0;
+         channel->clip = NULL;
+         channel->ui_volume = 0;
+         channel->ui_pan = 0;
+         channel->ui_spacial_volume = 0;
+         channel->ui_spacial_pan = 0;
+         channel->ui_activity = k_channel_activity_wake;
+
+         struct audio_channel_controls *controls = get_audio_channel_controls( id );
+         controls->flags = 0x00;
+         controls->volume_target = AUDIO_VOLUME_100;
+         controls->volume_slew_rate_per_sample = (f64)AUDIO_VOLUME_100 / (0.1*44100.0);
+         controls->pan_target = 0;
+         controls->pan_slew_rate_per_sample = (f64)AUDIO_PAN_RIGHT_100 / (0.1*44100.0);
+         controls->sampling_rate_multiplier = 1.0f;
+         controls->lfo_id = 0;
+         controls->lfo_attenuation_amount = 0.0f;
+         controls->pause = 0;
+         v4_copy( (f32[4]){0,0,0,1}, controls->spacial_falloff );
+
+         struct audio_channel_state *state = get_audio_channel_state( id );
+         state->activity = k_channel_activity_wake;
+         state->loaded_clip_length = 0;
+#if 0
+         state->decoder_handle.bird = NULL;
+#endif
+         state->decoder_handle.vorbis = NULL;
+         state->cursor = 0;
+         state->volume = AUDIO_VOLUME_100;
+         state->pan = 0;
+         state->spacial_volume = 0;
+         state->spacial_pan = 0;
+         state->spacial_warm = 0;
+         return id;
+      }
+   }
+
+   return 0;
+}
+
+void _audio_set_channel_clip( audio_channel_id id, struct audio_clip *clip )
+{
+   _audio_assert_lock();
+
+   struct audio_channel *channel = get_audio_channel( id );
+   ASSERT_CRITICAL( channel->stage == k_channel_stage_allocation );
+   ASSERT_CRITICAL( channel->clip == NULL );
+   channel->clip = clip;
+
+   u32 audio_format = channel->clip->flags & AUDIO_FLAG_FORMAT;
+   if( audio_format == k_audio_format_bird )
+      buffer_copy( "[array]", 0, channel->ui_name, sizeof(channel->ui_name) );
+   else if( audio_format == k_audio_format_gen )
+      buffer_copy( "[program]", 0, channel->ui_name, sizeof(channel->ui_name) );
+   else
+      buffer_copy( clip->path, 0, channel->ui_name, sizeof(channel->ui_name) );
+}
+
+void _audio_set_channel_group( audio_channel_id id, u16 group )
+{
+   _audio_assert_lock();
+
+   struct audio_channel *channel = get_audio_channel( id );
+   ASSERT_CRITICAL( channel->stage == k_channel_stage_allocation );
+   ASSERT_CRITICAL( channel->group == 0 );
+   channel->group = group;
+   if( group )
+      channel->ui_colour = (((u32)group * 29986577) & 0x00ffffff) | 0xff000000;
+}
+
+u32 _audio_count_channels_in_group( u16 group )
+{
+   _audio_assert_lock();
+
+   u32 count = 0;
+   for( int id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      struct audio_channel *channel = get_audio_channel( id );
+      if( channel->stage != k_channel_stage_none )
+      {
+         if( channel->group == group )
+            count ++;
+      }
+   }
+   
+   return count;
+}
+
+audio_channel_id _audio_get_first_active_channel_in_group( u16 group )
+{
+   _audio_assert_lock();
+   for( int id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      struct audio_channel *channel = get_audio_channel( id );
+      if( (channel->stage != k_channel_stage_none) && (channel->group == group) )
+         return id;
+   }
+   return 0;
+}
+
+void _audio_sidechain_lfo_to_channel( audio_channel_id id, audio_channel_id lfo_id, f32 amount )
+{
+   _audio_assert_lock();
+   
+   struct audio_lfo *lfo = get_audio_lfo( lfo_id );
+   ASSERT_CRITICAL( lfo->stage == k_channel_stage_active );
+
+   struct audio_channel_controls *controls = get_audio_channel_controls( id );
+   controls->lfo_id = lfo_id;
+   controls->lfo_attenuation_amount = amount;
+}
+
+void _audio_add_channel_flags( audio_channel_id id, u32 flags )
+{
+   _audio_assert_lock();
+   struct audio_channel_controls *controls = get_audio_channel_controls( id );
+   controls->flags |= flags;
+}
+
+void _audio_set_channel_spacial_falloff( audio_channel_id id, f32 co[3], f32 range )
+{
+   _audio_assert_lock();
+
+   struct audio_channel_controls *controls = get_audio_channel_controls( id );
+   v3_copy( co, controls->spacial_falloff );
+   controls->spacial_falloff[3] = range == 0.0f? 1.0f: 1.0f/range;
+
+   _audio_add_channel_flags( id, AUDIO_FLAG_SPACIAL_3D );
+}
+
+void _audio_sync_ui_master_controls(void)
+{
+   _audio_assert_lock();
+   _audio.controls.volume_target = ((f64)AUDIO_VOLUME_100) * _audio.master_volume_ui;
+   _audio.controls.dsp_enabled = _audio.dsp_enabled_ui;
+}
+
+void _audio_set_channel_volume( audio_channel_id id, f64 volume, bool instant )
+{
+   _audio_assert_lock();
+
+   struct audio_channel_controls *controls = get_audio_channel_controls( id );
+   controls->volume_target = ((f64)AUDIO_VOLUME_100) * volume;
+
+   if( instant )
+   {
+      struct audio_channel *channel = get_audio_channel( id );
+      ASSERT_CRITICAL( channel->stage == k_channel_stage_allocation );
+
+      struct audio_channel_state *state = get_audio_channel_state( id );
+      state->volume = controls->volume_target;
+   }
+}
+
+void _audio_set_channel_volume_slew_duration( audio_channel_id id, f64 length_seconds )
+{
+   _audio_assert_lock();
+   
+   struct audio_channel_controls *controls = get_audio_channel_controls( id );
+   controls->volume_slew_rate_per_sample = (f64)AUDIO_VOLUME_100 / (length_seconds * 44100.0);
+}
+
+void _audio_set_channel_pan( audio_channel_id id, f64 pan, bool instant )
+{
+   _audio_assert_lock();
+
+   struct audio_channel_controls *controls = get_audio_channel_controls( id );
+   controls->pan_target = ((f64)AUDIO_PAN_RIGHT_100) * pan;
+
+   if( instant )
+   {
+      struct audio_channel *channel = get_audio_channel( id );
+      ASSERT_CRITICAL( channel->stage == k_channel_stage_allocation );
+
+      struct audio_channel_state *state = get_audio_channel_state( id );
+      state->pan = controls->pan_target;
+   }
+}
+
+void _audio_set_channel_pan_slew_duration( audio_channel_id id, f64 length_seconds )
+{
+   _audio_assert_lock();
+
+   struct audio_channel_controls *controls = get_audio_channel_controls( id );
+   controls->pan_slew_rate_per_sample = (f64)AUDIO_PAN_RIGHT_100 / (length_seconds * 44100.0);
+}
+
+void _audio_set_channel_sampling_rate( audio_channel_id id, f32 rate )
+{
+   _audio_assert_lock();
+   struct audio_channel_controls *controls = get_audio_channel_controls( id );
+   controls->sampling_rate_multiplier = rate;
+}
+
+void _audio_start_channel( audio_channel_id id )
+{
+   _audio_assert_lock();
+
+   struct audio_channel *channel = get_audio_channel( id );
+   ASSERT_CRITICAL( channel->stage == k_channel_stage_allocation );
+   ASSERT_CRITICAL( channel->clip );
+   channel->stage = k_channel_stage_active;
+}
+
+audio_channel_id _audio_crossfade( audio_channel_id id, struct audio_clip *new_clip, f32 transition_seconds )
+{
+   _audio_assert_lock();
+
+   struct audio_channel *channel = get_audio_channel( id );
+   audio_channel_id new_id = 0;
+   if( new_clip )
+   {
+      new_id = _audio_get_first_idle_channel();
+      if( new_id )
+      {
+         _audio_set_channel_clip( new_id, new_clip );
+         _audio_set_channel_volume_slew_duration( new_id, transition_seconds );
+         _audio_set_channel_volume( new_id, 1.0, 0 );
+         _audio_set_channel_group( new_id, channel->group );
+
+         struct audio_channel_controls *existing_controls = get_audio_channel_controls( id ),
+                                       *new_controls      = get_audio_channel_controls( new_id );
+
+         buffer_copy( new_controls, sizeof( struct audio_channel_controls ), 
+                      existing_controls, sizeof( struct audio_channel_controls ) );
+         _audio_start_channel( new_id );
+      }
+   }
+
+   _audio_set_channel_volume_slew_duration( id, transition_seconds );
+   _audio_set_channel_volume( id, 0.0, 0 );
+   _audio_add_channel_flags( id, AUDIO_FLAG_RELINQUISHED );
+
+   return new_id;
+}
+
+bool _audio_is_channel_using_clip( audio_channel_id id, struct audio_clip *clip )
+{
+   _audio_assert_lock();
+   struct audio_channel *channel = get_audio_channel( id );
+
+   if( channel->clip == clip ) return 1;
+   else return 0;
+}
+
+void _audio_fadeout_flagged_audio( u32 flag, f32 length )
+{
+   _audio_assert_lock();
+   for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      struct audio_channel *channel = get_audio_channel( id );
+      if( channel->stage != k_channel_stage_none )
+      {
+         struct audio_channel_controls *controls = get_audio_channel_controls( id );
+         if( controls->flags & flag )
+         {
+            if( _audio.working )
+               _audio_crossfade( id, NULL, 1.0f );
+            else
+               channel->stage = k_channel_stage_none;
+         }
+      }
+   }
+}
+
+bool _audio_flagged_stopped( u32 flag )
+{
+   _audio_lock();
+   for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      struct audio_channel *channel = get_audio_channel( id );
+      if( channel->stage != k_channel_stage_none )
+      {
+         struct audio_channel_controls *controls = get_audio_channel_controls( id );
+         if( controls->flags & flag )
+         {
+            _audio_unlock();
+            return 0;
+         }
+      }
+   }
+   _audio_unlock();
+   return 1;
+}
+
+void _audio_set_channel_pause( audio_channel_id id, bool pause )
+{
+   _audio_assert_lock();
+   struct audio_channel_controls *controls = get_audio_channel_controls( id );
+   controls->pause = pause;
+}
+
+void _audio_set_flagged_pause( u32 flag, bool pause )
+{
+   _audio_assert_lock();
+   for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      struct audio_channel *channel = get_audio_channel( id );
+      if( channel->stage != k_channel_stage_none )
+      {
+         struct audio_channel_controls *controls = get_audio_channel_controls( id );
+         if( controls->flags & flag )
+            _audio_set_channel_pause( id, pause );
+      }
+   }
+}
+
+void _audio_oneshot_3d( struct audio_clip *clip, f32 co[3], f32 range, f32 volume, u16 group, u32 flags )
+{
+   _audio_assert_lock();
+   audio_channel_id id = _audio_get_first_idle_channel();
+
+   if( id )
+   {
+      _audio_set_channel_clip( id, clip );
+      _audio_set_channel_spacial_falloff( id, co, range );
+      _audio_set_channel_group( id, group );
+      _audio_set_channel_volume( id, volume, 1 );
+      _audio_add_channel_flags( id, AUDIO_FLAG_RELINQUISHED | flags );
+      _audio_start_channel( id );
+   }
+}
+
+void _audio_oneshot( struct audio_clip *clip, f32 volume, f32 pan, u16 group, u32 flags )
+{
+   _audio_assert_lock();
+   audio_channel_id id = _audio_get_first_idle_channel();
+
+   if( id )
+   {
+      _audio_set_channel_clip( id, clip );
+      _audio_set_channel_group( id, group );
+      _audio_set_channel_volume( id, volume, 1 );
+      _audio_set_channel_pan( id, pan, 1 );
+      _audio_add_channel_flags( id, AUDIO_FLAG_RELINQUISHED | flags );
+      _audio_start_channel( id );
+   }
+}
+
+
+
+/* lfos
+ * ---------------------------------------------------------------------------------------- */
+
+audio_channel_id _audio_get_first_idle_lfo(void)
+{
+   _audio_assert_lock();
+
+   for( int id=1; id<=AUDIO_LFOS; id ++ )
+   {
+      struct audio_lfo *lfo = get_audio_lfo( id );
+
+      if( lfo->stage == k_channel_stage_none )
+      {
+         lfo->stage = k_channel_stage_allocation;
+
+         const u32 default_lfo_period = 44100;
+
+         struct audio_lfo_controls *controls = get_audio_lfo_controls( id );
+         controls->period_in_samples = default_lfo_period;
+         controls->wave_type = k_lfo_triangle;
+         controls->polynomial_coefficient = 0.0f;
+         controls->flags = 0x00;
+
+         struct audio_lfo_state *state = get_audio_lfo_state( id );
+         state->time = 0;
+         state->last_period_in_samples = default_lfo_period;
+         state->frame_reference_count = 0;
+         state->time_at_frame_start = 0;
+         return id;
+      }
+   }
+
+   return 0;
+}
+
+void _audio_set_lfo_polynomial_bipolar( audio_channel_id lfo_id, f32 coefficient )
+{
+   _audio_assert_lock();
+
+   struct audio_lfo_controls *controls = get_audio_lfo_controls( lfo_id );
+   controls->polynomial_coefficient = coefficient;
+   controls->sqrt_polynomial_coefficient = sqrtf(coefficient);
+   controls->wave_type = k_lfo_polynomial_bipolar;
+}
+
+void _audio_set_lfo_frequency( audio_channel_id lfo_id, f32 freq )
+{
+   _audio_assert_lock();
+
+   struct audio_lfo_controls *controls = get_audio_lfo_controls( lfo_id );
+   u32 length = 44100.0f / freq;
+   controls->period_in_samples = length;
+
+   struct audio_lfo *lfo = get_audio_lfo( lfo_id );
+   if( lfo->stage == k_channel_stage_allocation )
+   {
+      struct audio_lfo_state *state = get_audio_lfo_state( lfo_id );
+      state->last_period_in_samples = length;
+   }
+}
+
+void _audio_start_lfo( audio_channel_id lfo_id )
+{
+   _audio_assert_lock();
+   struct audio_lfo *lfo = get_audio_lfo( lfo_id );
+   lfo->stage = k_channel_stage_active;
+}
+
+static void audio_channel_get_samples( audio_channel_id id, struct audio_channel_controls *controls, 
+                                       u32 count, f32 *out_stereo )
+{
+   u32 remaining = count;
+   u32 buffer_pos = 0;
+
+   struct audio_channel *channel = get_audio_channel( id );
+   struct audio_channel_state *state = get_audio_channel_state( id );
+   u32 format = channel->clip->flags & AUDIO_FLAG_FORMAT;
+
+   while( remaining )
+   {
+      u32 samples_this_run = u32_min( remaining, state->loaded_clip_length - state->cursor );
+      remaining -= samples_this_run;
+
+      f32 *dst = &out_stereo[ buffer_pos * 2 ]; 
+      
+      if( format == k_audio_format_stereo )
+      {
+         for( u32 i=0; i<samples_this_run; i++ )
+         {
+            /* FIXME: ??????? */
+            dst[i*2+0] = 0.0f;
+            dst[i*2+1] = 0.0f;
+            _fatal_exit();
+         }
+      }
+      else if( format == k_audio_format_vorbis )
+      {
+         int read_samples = stb_vorbis_get_samples_float_interleaved_stereo( state->decoder_handle.vorbis,
+                                                                             dst, samples_this_run );
+         if( read_samples != samples_this_run )
+         {
+            $log( $warning, {"Invalid samples read ("},{channel->clip->path},{")"} );
+            for( u32 i=0; i<samples_this_run; i++ )
+            {
+               dst[i*2+0] = 0.0f;
+               dst[i*2+1] = 0.0f;
+            }
+         }
+      }
+      else if( format == k_audio_format_bird )
+      {
+         //synth_bird_generate_samples( state->decoder_handle.bird, dst, samples_this_run );
+         _fatal_exit();
+      }
+      else if( format == k_audio_format_gen )
+      {
+         void (*fn)( void *data, f32 *buf, u32 count ) = channel->clip->generative_function;
+         fn( channel->clip->any_data, dst, samples_this_run );
+      }
+      else
+      {
+         i16 *src_buffer = channel->clip->any_data,
+             *src        = &src_buffer[ state->cursor ];
+         audio_decode_uncompressed_mono( src, samples_this_run, dst );
+      }
+
+      state->cursor += samples_this_run;
+      buffer_pos += samples_this_run;
+      
+      if( (controls->flags & AUDIO_FLAG_LOOP) && remaining )
+      {
+         if( format == k_audio_format_vorbis )
+            stb_vorbis_seek_start( state->decoder_handle.vorbis );
+#if 0
+         else if( format == k_audio_format_bird )
+            synth_bird_reset( state->decoder_handle.bird );
+#endif
+
+         state->cursor = 0;
+         continue;
+      }
+      else
+         break;
+   }
+
+   while( remaining )
+   {
+      out_stereo[ buffer_pos*2 + 0 ] = 0.0f;
+      out_stereo[ buffer_pos*2 + 1 ] = 0.0f;
+      buffer_pos ++;
+      remaining --;
+   }
+}
+
+static f32 audio_lfo_get_sample( audio_channel_id lfo_id, struct audio_lfo_controls *controls )
+{
+   struct audio_lfo_state *state = get_audio_lfo_state( lfo_id );
+   state->time ++;
+
+   if( state->time >= controls->period_in_samples )
+      state->time = 0;
+
+   f32 t  = state->time;
+       t /= (f32)controls->period_in_samples;
+
+   if( controls->wave_type == k_lfo_polynomial_bipolar )
+   {
+      /*
+       *           #
+       *          # #
+       *          # #
+       *          #  #
+       * ###     #    ###
+       *    ##   #
+       *      #  #
+       *       # #
+       *       ##
+       */           
+
+      t *= 2.0f;
+      t -= 1.0f;
+
+      return (( 2.0f * controls->sqrt_polynomial_coefficient * t ) /
+              /* --------------------------------------- */
+               ( 1.0f + controls->polynomial_coefficient * t*t )) * (1.0f-fabsf(t));
+   }
+   else
+      return 0.0f;
+}
+
+static void audio_slew_i32( i32 *value, i32 target, i32 rate )
+{
+   i32 sign = target - *value;
+   if( sign == 0 ) 
+      return;
+
+   sign = sign>0? 1: -1;
+   i32 c = *value + sign*rate;
+
+   if( target*sign < c*sign ) *value = target;
+   else                       *value = c;
+}
+
+static void audio_channel_mix( audio_channel_id id, 
+                               struct audio_channel_controls *controls, 
+                               struct audio_master_controls *master_controls, f32 *inout_buffer )
+{
+   struct audio_channel_state *state = get_audio_channel_state( id );
+
+   bool is_3d = controls->flags & AUDIO_FLAG_SPACIAL_3D? 1: 0;
+   bool use_doppler = controls->flags & AUDIO_FLAG_NO_DOPPLER? 0: 1;
+
+   f32 frame_sample_rate = controls->sampling_rate_multiplier;
+
+   i32 spacial_volume_target = 0,
+       spacial_pan_target = 0;
+
+   if( is_3d )
+   {
+      f32 delta[3];
+      v3_sub( controls->spacial_falloff, master_controls->listener_position, delta );
+
+      f32 dist = v3_length( delta );
+      if( dist <= 0.01f )
+      {
+         spacial_pan_target = 0;
+         spacial_volume_target = AUDIO_VOLUME_100;
+      }
+      else if( dist > 20000.0f || !f32_is_number( dist ) )
+      {
+         spacial_pan_target = 0;
+         spacial_volume_target = 0;
+      }
+      else
+      {
+         f32 vol = f32_max( 0.0f, 1.0f - controls->spacial_falloff[3]*dist );
+         vol = powf( vol, 5.0f );
+         spacial_volume_target = (f64)f32_clamp( vol, 0.0f, 1.0f ) * (f64)AUDIO_VOLUME_100 * 0.5;
+
+         v3_muls( delta, 1.0f/dist, delta );
+         f32 pan = v3_dot( master_controls->listener_right_ear_direction, delta );
+         spacial_pan_target = (f64)f32_clamp( pan, -1.0f, 1.0f ) * (f64)AUDIO_PAN_RIGHT_100;
+
+         if( use_doppler )
+         {
+            const f32 vs = 323.0f;
+            f32 dv = v3_dot( delta, master_controls->listener_velocity );
+            f32 doppler = (vs+dv)/vs;
+                doppler = f32_clamp( doppler, 0.6f, 1.4f );
+                  
+            if( fabsf(doppler-1.0f) > 0.01f )
+               frame_sample_rate *= doppler;
+         }
+      }
+
+      if( !state->spacial_warm )
+      {
+         state->spacial_volume = spacial_volume_target;
+         state->spacial_pan = spacial_pan_target;
+         state->spacial_warm = 1;
+      }
+   }
+
+   u32 buffer_length = AUDIO_MIX_FRAME_SIZE;
+   if( frame_sample_rate != 1.0f )
+   {
+      float l = ceilf( (float)(AUDIO_MIX_FRAME_SIZE) * frame_sample_rate );
+      buffer_length = l+1;
+   }
+
+   f32 samples[ AUDIO_MIX_FRAME_SIZE*2 * 2 ];
+   audio_channel_get_samples( id, controls, buffer_length, samples );
+
+   /* TODO: Slew this */
+   f64 master_volume = (f64)_audio.state.volume / (f64)AUDIO_VOLUME_100;
+   master_volume = 1.0;
+
+   for( u32 j=0; j<AUDIO_MIX_FRAME_SIZE; j++ )
+   {
+      audio_slew_i32( &state->volume, controls->volume_target, controls->volume_slew_rate_per_sample );
+      audio_slew_i32( &state->pan,    controls->pan_target,    controls->pan_slew_rate_per_sample );
+
+      f64 v_c = ((f64)state->volume / (f64)AUDIO_VOLUME_100) * master_volume;
+
+      if( controls->lfo_id )
+      {
+         struct audio_lfo_state *state = get_audio_lfo_state( controls->lfo_id );
+         f32 lfo_value = audio_lfo_get_sample( controls->lfo_id, state->controls );
+         v_c *= 1.0 + lfo_value * controls->lfo_attenuation_amount;
+      }
+
+      f64 v_l = v_c*v_c,
+          v_r = v_c*v_c;
+
+      if( is_3d )
+      {
+         const i32 vol_rate = (f64)AUDIO_VOLUME_100 / (0.05 * 44100.0),
+                   pan_rate = (f64)AUDIO_PAN_RIGHT_100 / (0.05 * 44100.0);
+
+         audio_slew_i32( &state->spacial_volume, spacial_volume_target, vol_rate );
+         audio_slew_i32( &state->spacial_pan,    spacial_pan_target,    pan_rate );
+
+         f64 v_s = (f64)state->spacial_volume / (f64)AUDIO_VOLUME_100,
+             v_p = (f64)state->spacial_pan / (f64)AUDIO_PAN_RIGHT_100;
+
+         v_l *= v_s * (1.0-v_p);
+         v_r *= v_s * (1.0+v_p);
+      }
+      
+      f32 s_l, s_r;
+      if( frame_sample_rate != 1.0f )
+      {
+         /* absolutely garbage resampling, but it will do
+          */
+         f32 sample_index = frame_sample_rate * (f32)j;
+         f32 t = f32_fractional_part( sample_index );
+
+         u32 i0 = floorf( sample_index ),
+             i1 = i0+1;
+
+         s_l = samples[ i0*2+0 ]*(1.0f-t) + samples[ i1*2+0 ]*t;
+         s_r = samples[ i0*2+1 ]*(1.0f-t) + samples[ i1*2+1 ]*t;
+      }
+      else
+      {
+         s_l = samples[ j*2+0 ];
+         s_r = samples[ j*2+1 ];
+      }
+
+      inout_buffer[ j*2+0 ] += s_l * v_l;
+      inout_buffer[ j*2+1 ] += s_r * v_r;
+   }
+}
+
+#if 1
+static void _audio_mixer( void *user, f32 *output_stereo, u32 sample_count )
+#else
+static void _audio_mixer( ma_device *device, void *stream, const void *input__, ma_uint32 sample_count )
+#endif
+{
+   for( i32 i=0; i<sample_count; i ++ )
+   {
+      output_stereo[i*2+0] = 0.0f;
+      output_stereo[i*2+1] = 0.0f;
+   }
+
+   struct audio_master_controls master_controls;
+
+   audio_channel_id active_channel_list[ AUDIO_CHANNELS ];
+   struct audio_channel_controls channel_controls[ AUDIO_CHANNELS ];
+   u32 active_channel_count = 0;
+
+   audio_channel_id active_lfo_list[ AUDIO_LFOS ];
+   struct audio_lfo_controls lfo_controls[ AUDIO_LFOS ];
+   u32 active_lfo_count = 0;
+
+   struct audio_master_state *master_state = &_audio.state;
+
+   _audio_lock();
+   COPY( &_audio.controls, &master_controls,sizeof(struct audio_master_controls) );
+
+   for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      struct audio_channel *channel = get_audio_channel( id );
+      if( channel->stage == k_channel_stage_active )
+      {
+         struct audio_channel_controls *controls = get_audio_channel_controls(id);
+         if( controls->pause ) 
+         {
+            channel->ui_activity = k_channel_activity_paused;
+            continue;
+         }
+
+         active_channel_list[ active_channel_count ] = id;
+         COPY( controls, &channel_controls[ active_channel_count ], sizeof( struct audio_channel_controls ) );
+         active_channel_count ++;
+      }
+   }
+   for( u32 id=1; id<=AUDIO_LFOS; id ++ )
+   {
+      struct audio_lfo *lfo = get_audio_lfo( id );
+      if( lfo->stage == k_channel_stage_active )
+      {
+         struct audio_lfo_controls *local_controls = &lfo_controls[ active_lfo_count ];
+         active_lfo_list[ active_lfo_count ] = id;
+         COPY( get_audio_lfo_controls(id), local_controls, sizeof(struct audio_lfo_controls) );
+         active_lfo_count ++;
+
+         struct audio_lfo_state *state = get_audio_lfo_state(id);
+         state->controls = local_controls;
+      }
+   }
+   //_dsp_update_tunings();
+   _audio_unlock();
+
+   /* init step */
+   master_state->volume = master_controls.volume_target;
+   master_state->volume_at_frame_start = master_controls.volume_target;
+
+   for( u32 i=0; i<active_channel_count; i ++ )
+   {
+      audio_channel_id id = active_channel_list[i];
+      struct audio_channel_state *channel_state = get_audio_channel_state( id );
+      
+      /* Wake up! */
+      if( channel_state->activity == k_channel_activity_wake )
+      {
+         struct audio_channel *channel = get_audio_channel( id );
+
+         u32 format = channel->clip->flags & AUDIO_FLAG_FORMAT;
+         if( format == k_audio_format_vorbis )
+         {
+            /* Setup vorbis decoder */
+            u8 *buf = (u8*)_audio.decoding_buffer,
+               *loc = &buf[AUDIO_DECODE_SIZE*id];
+
+            stb_vorbis_alloc alloc = 
+            {
+               .alloc_buffer = (c8 *)loc,
+               .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE
+            };
+
+            int err;
+            stb_vorbis *decoder = stb_vorbis_open_memory( channel->clip->any_data, channel->clip->size, &err, &alloc );
+
+            if( !decoder )
+            {
+               $log( $error, {"stb_vorbis_open_memory failed on '"},{channel->clip->path},{"' code: "}, $signed(err) );
+               channel_state->activity = k_channel_activity_error;
+            }
+            else
+            {
+               channel_state->loaded_clip_length = stb_vorbis_stream_length_in_samples( decoder );
+               channel_state->decoder_handle.vorbis = decoder;
+               channel_state->activity = k_channel_activity_playing;
+            }
+         }
+         else if( format == k_audio_format_bird )
+         {
+#if 0
+            u8 *buf = (u8*)_audio.decoding_buffer;
+            struct synth_bird *loc = (void *)&buf[AUDIO_DECODE_SIZE*id];
+            COPY( channel->clip->any_data, loc, channel->clip->size );
+            synth_bird_reset( loc );
+
+            channel_state->decoder_handle.bird = loc;
+            channel_state->loaded_clip_length = synth_bird_get_length_in_samples( loc );
+            channel_state->activity = k_channel_activity_playing;
+#endif
+         }
+         else if( format == k_audio_format_stereo )
+         {
+            channel_state->loaded_clip_length = channel->clip->size / 2;
+            channel_state->activity = k_channel_activity_playing;
+         }
+         else if( format == k_audio_format_gen )
+         {
+            channel_state->loaded_clip_length = 0xffffffff;
+            channel_state->activity = k_channel_activity_playing;
+         }
+         else
+         {
+            channel_state->loaded_clip_length = channel->clip->size;
+            channel_state->activity = k_channel_activity_playing;
+         }
+      }
+   }
+
+   for( u32 i=0; i<active_lfo_count; i ++ )
+   {
+      audio_channel_id lfo_id = active_lfo_list[i];
+      struct audio_lfo_state *state = get_audio_lfo_state( lfo_id );
+      struct audio_lfo_controls *controls = &lfo_controls[i];
+
+      /* if the period changes we need to remap the time value to prevent hitching */
+      if( controls->period_in_samples != state->last_period_in_samples )
+      {
+         state->last_period_in_samples = controls->period_in_samples;
+         f64 t = state->time;
+             t/= (f64)controls->period_in_samples;
+         state->time = (f64)controls->period_in_samples * t;
+      }
+
+      state->time_at_frame_start = state->time;
+      state->frame_reference_count = 0;
+   }
+
+   /* mix step */
+   for( u32 dry_layer=0; dry_layer<=1; dry_layer ++ )
+   {
+      for( u32 i=0; i<active_channel_count; i ++ )
+      {
+         audio_channel_id id = active_channel_list[i];
+         struct audio_channel_state *state = get_audio_channel_state( id );
+         struct audio_channel_controls *controls = &channel_controls[i];
+
+         if( state->activity == k_channel_activity_playing )
+         {
+            if( master_controls.dsp_enabled )
+            {
+               if( controls->flags & AUDIO_FLAG_NO_DSP )
+               {
+                  if( !dry_layer )
+                     continue;
+               }
+               else
+               {
+                  if( dry_layer )
+                     continue;
+               }
+            }
+
+            if( controls->lfo_id )
+            {
+               struct audio_lfo_state *lfo_state = get_audio_lfo_state( controls->lfo_id );
+               lfo_state->time = lfo_state->time_at_frame_start;
+               lfo_state->frame_reference_count ++;
+            }
+
+            u32 remaining = sample_count,
+                subpos    = 0;
+
+            while( remaining )
+            {
+               audio_channel_mix( id, controls,  &master_controls, output_stereo+subpos );
+               remaining -= AUDIO_MIX_FRAME_SIZE;
+               subpos += AUDIO_MIX_FRAME_SIZE*2;
+            }
+
+            if( (state->cursor >= state->loaded_clip_length) && !(controls->flags & AUDIO_FLAG_LOOP) )
+               state->activity = k_channel_activity_end;
+         }
+      }
+
+      if( master_controls.dsp_enabled )
+      {
+         if( !dry_layer )
+         {
+            for( i32 i=0; i<sample_count; i++ )
+               _dsp_process( output_stereo + i*2, output_stereo + i*2 );
+         }
+      }
+      else break;
+   }
+
+   _audio_lock();
+   for( u32 i=0; i<active_channel_count; i ++ )
+   {
+      audio_channel_id id = active_channel_list[i];
+      struct audio_channel *channel = get_audio_channel(id);
+      struct audio_channel_state *state = get_audio_channel_state( id );
+      struct audio_channel_controls *controls = &channel_controls[i];
+
+      channel->ui_activity = state->activity;
+      channel->ui_volume = state->volume;
+      channel->ui_pan = state->pan;
+      channel->ui_spacial_volume = state->spacial_volume;
+      channel->ui_spacial_pan = state->spacial_pan;
+
+      if( controls->flags & AUDIO_FLAG_RELINQUISHED )
+      {
+         bool die = 0;
+         if( state->activity == k_channel_activity_end ) die = 1;
+         if( state->activity == k_channel_activity_error ) die = 1;
+         if( state->volume == 0 ) die = 1;
+
+         if( die )
+         {
+            channel->stage = k_channel_stage_none;
+         }
+      }
+   }
+
+   _audio.samples_written_last_audio_frame = sample_count;
+   _audio_unlock();
+}
+
+void _new_callback( void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount )
+{
+   i32 written_amount = 0;
+
+   while( written_amount < additional_amount )
+   {
+      f32 buffer[ AUDIO_FRAME_SIZE * 2 ];
+      _audio_mixer( NULL, buffer, AUDIO_FRAME_SIZE );
+      SDL_PutAudioStreamData( stream, buffer, sizeof(buffer) );
+      written_amount += sizeof(buffer);
+   }
+}
+
+static void _audio_device_init(void)
+{
+   SDL_AudioSpec spec;
+   spec.format = SDL_AUDIO_F32;
+   spec.channels = 2;
+   spec.freq = 44100;
+
+   SDL_AudioStream *stream = SDL_OpenAudioDeviceStream( SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, _new_callback, NULL );
+   SDL_ResumeAudioDevice( SDL_GetAudioStreamDevice( stream ) );
+   _audio.working = 1;
+}
+
+void _audio_init(void)
+{
+   $log( $info, {"[INIT] _audio_init"} );
+   
+   _audio_mutex = SDL_CreateMutex();
+   ASSERT_CRITICAL( _audio_mutex );
+
+   /* fixed */
+   u32 decode_size = AUDIO_DECODE_SIZE * AUDIO_CHANNELS;
+   _audio.decoding_buffer = stack_allocate( NULL, decode_size, 8, "Decoding buffers" );
+
+   struct audio_master_controls *master_controls = &_audio.controls;
+   master_controls->dsp_enabled = _audio.dsp_enabled_ui;
+   master_controls->volume_target = (f64)_audio.master_volume_ui * (f64)AUDIO_VOLUME_100;
+   v3_copy( (f32[]){1,0,0}, master_controls->listener_right_ear_direction );
+   v3_fill( master_controls->listener_velocity, 0 );
+   v3_fill( master_controls->listener_position, 0 );
+
+   _audio_device_init();
+   //_vg_add_exit_function( vg_audio_free );
+}
+
+void vg_audio_preupdate(void)
+{
+#if 0
+   bool before_working = _audio.working;
+   _audio.working = 1;
+   if( _audio.sdl_output_device == 0 )
+      _audio.working = 0;
+   else
+      if( SDL_GetAudioDeviceStatus( _audio.sdl_output_device ) == SDL_AUDIO_STOPPED )
+         _audio.working = 0;
+#endif
+}
diff --git a/source/engine/vg_audio.h b/source/engine/vg_audio.h
new file mode 100644 (file)
index 0000000..18d025f
--- /dev/null
@@ -0,0 +1,108 @@
+#pragma once
+#include "foundation.h"
+
+#define AUDIO_FRAME_SIZE 512
+#define AUDIO_MIX_FRAME_SIZE 256
+
+#define AUDIO_CHANNELS        32
+#define AUDIO_LFOS            8
+#define AUDIO_FILTERS         16
+#define AUDIO_FLAG_LOOP       0x1
+#define AUDIO_FLAG_NO_DOPPLER 0x2
+#define AUDIO_FLAG_SPACIAL_3D 0x4
+#define AUDIO_FLAG_AUTO_START 0x8
+#define AUDIO_FLAG_NO_DSP     0x10
+#define AUDIO_FLAG_WORLD      0x20
+#define AUDIO_FLAG_FORMAT     0x1E00
+#define AUDIO_FLAG_RELINQUISHED 0x2000
+#define AUDIO_FLAG_CUTSCENE     0x4000
+
+enum audio_format
+{
+   k_audio_format_mono   = 0x000u,
+   k_audio_format_stereo = 0x200u,
+   k_audio_format_vorbis = 0x400u,
+   k_audio_format_none0  = 0x600u,
+   k_audio_format_none1  = 0x800u,
+   k_audio_format_none2  = 0xA00u,
+   k_audio_format_none3  = 0xC00u,
+   k_audio_format_none4  = 0xE00u,
+
+   k_audio_format_bird   = 0x1000u,
+   k_audio_format_gen    = 0x1200u,
+   k_audio_format_none6  = 0x1400u,
+   k_audio_format_none7  = 0x1600u,
+   k_audio_format_none8  = 0x1800u,
+   k_audio_format_none9  = 0x1A00u,
+   k_audio_format_none10 = 0x1C00u,
+   k_audio_format_none11 = 0x1E00u,
+};
+
+#define AUDIO_DECODE_SIZE     (1024*256)  /* 256 kb decoding buffers */
+#define AUDIO_VOLUME_100      500000000
+#define AUDIO_PAN_RIGHT_100   500000000
+#define AUDIO_PAN_LEFT_100   -500000000
+
+typedef u16 audio_channel_id;
+typedef u16 audio_channel_group; /* TODO: Create a generation system for this */
+
+struct audio_clip
+{
+   union 
+   {
+      const char *path;
+      void *generative_function;
+   };
+
+   u32 flags;
+   u32 size;
+
+   union
+   { 
+      void *any_data;
+   };
+};
+
+void vg_audio_device_init(void);
+void vg_audio_begin(void);
+
+bool audio_clip_load( struct audio_clip *clip, struct stack_allocator *stack );
+u32 audio_clip_loadn( struct audio_clip *arr, u32 count, struct stack_allocator *stack );
+
+void _audio_lock(void);
+void _audio_unlock(void);
+void _audio_preupdate(void);
+
+/* channel API */
+audio_channel_id _audio_get_first_idle_channel(void);
+void _audio_set_channel_clip( audio_channel_id id, struct audio_clip *clip );
+void _audio_set_channel_group( audio_channel_id id, u16 group );
+u32 _audio_count_channels_in_group( u16 group );
+audio_channel_id _audio_get_first_active_channel_in_group( u16 group );
+void _audio_sidechain_lfo_to_channel( audio_channel_id id, audio_channel_id lfo_id, f32 amount );
+void _audio_set_channel_spacial_falloff( audio_channel_id id, f32 co[3], f32 range );
+void _audio_set_channel_volume( audio_channel_id id, f64 volume, bool instant );
+void _audio_set_channel_volume_slew_duration( audio_channel_id id, f64 length_seconds );
+void _audio_set_channel_pan( audio_channel_id id, f64 pan, bool instant );
+void _audio_set_channel_pan_slew_duration( audio_channel_id id, f64 length_seconds );
+void _audio_set_channel_sampling_rate( audio_channel_id id, f32 rate );
+void _audio_start_channel( audio_channel_id id );
+void _audio_add_channel_flags( audio_channel_id id, u32 flags );
+
+audio_channel_id _audio_get_first_idle_lfo(void);
+void _audio_set_lfo_polynomial_bipolar( audio_channel_id lfo_id, f32 coefficient );
+void _audio_set_lfo_frequency( audio_channel_id lfo_id, f32 freq );
+void _audio_start_lfo( audio_channel_id lfo_id );
+
+/* high level functions */
+void _audio_sync_ui_master_controls(void);
+audio_channel_id _audio_crossfade( audio_channel_id id, struct audio_clip *new_clip, f32 transition_seconds );
+void _audio_oneshot_3d( struct audio_clip *clip, f32 co[3], f32 range, f32 volume, u16 group, u32 flags );
+void _audio_oneshot( struct audio_clip *clip, f32 volume, f32 pan, u16 group, u32 flags );
+void _audio_set_channel_pause( audio_channel_id id, bool pause );
+
+/* half measures... Don't expect these functions to stay. */
+void _audio_fadeout_flagged_audio( u32 flag, f32 length );
+bool _audio_flagged_stopped( u32 flag );
+bool _audio_is_channel_using_clip( audio_channel_id id, struct audio_clip *clip );
+void _audio_set_flagged_pause( u32 flag, bool pause );
diff --git a/source/engine/vg_audio_dsp.c b/source/engine/vg_audio_dsp.c
new file mode 100644 (file)
index 0000000..b5c5064
--- /dev/null
@@ -0,0 +1,278 @@
+#include "foundation.h"
+#include "common_maths.h"
+#include "random.h"
+
+struct 
+{
+   struct stack_allocator allocator;
+   f32 *buffer;
+   f32  echo_tunings[8],
+        reverb_wet_mix,
+        reverb_dry_mix;
+
+   struct mt_random rand;
+}
+static _dsp;
+
+struct dsp_delay
+{
+   u32 length, cur;
+   f32 *buffer;
+};
+
+struct dsp_lpf
+{
+   f32 exponent;
+   f32 *buffer;
+};
+
+struct dsp_schroeder
+{
+   struct dsp_delay M;
+   f32 gain;
+};
+
+struct dsp_biquad 
+{
+   f32 a0, a1, a2, b1, b2, c0, d0,
+       xnz1, xnz2, ynz1, ynz2, offset;
+};
+
+static f32 *_dsp_allocate( u32 samples )
+{
+   return stack_allocate( &_dsp.allocator, samples * sizeof(f32), sizeof(f32), "DSP Buffer" );
+}
+
+f32 dsp_biquad_process( struct dsp_biquad *bq, f32 xn )
+{
+   f32 yn = + bq->a0*xn + bq->a1*bq->xnz1 + bq->a2*bq->xnz2 
+            - bq->b1*bq->ynz1 - bq->b2*bq->ynz2;
+   bq->xnz2 = bq->xnz1;
+   bq->xnz1 = xn;
+   bq->ynz2 = bq->ynz1;
+   bq->ynz1 = yn;
+   return yn + bq->offset;
+}
+
+void dsp_init_biquad_butterworth_lpf( struct dsp_biquad *bq, f32 fc )
+{
+   f32 c = 1.0f/tanf(VG_PIf*fc / 44100.0f);
+   bq->a0 = 1.0f / (1.0f + sqrtf(2.0f)*c + powf(c, 2.0f) );
+   bq->a1 = 2.0f * bq->a0;
+   bq->a2 = bq->a0;
+   bq->b1 = 2.0f * bq->a0*(1.0f - powf(c, 2.0f));
+   bq->b2 = bq->a0 * (1.0f - sqrtf(2.0f)*c + powf(c, 2.0f) );
+}
+
+void dsp_read_delay( struct dsp_delay *delay, f32 *s, u32 t )
+{
+   u32 index = delay->cur+t;
+   if( index >= delay->length )
+      index -= delay->length;
+   *s = delay->buffer[ index ];
+}
+
+void dsp_write_delay( struct dsp_delay *delay, f32 *s )
+{
+   u32 index = delay->cur;
+   delay->buffer[ index ] = *s;
+   delay->cur ++;
+   if( delay->cur >= delay->length )
+      delay->cur = 0;
+}
+
+void dsp_init_delay( struct dsp_delay *delay, f32 length )
+{
+   delay->length = 44100.0f * length;
+   delay->cur = 0;
+   delay->buffer = _dsp_allocate( delay->length );
+   for( u32 i=0; i<delay->length; i++ )
+      delay->buffer[i] = 0.0f;
+}
+
+void dsp_update_lpf( struct dsp_lpf *lpf, f32 freq )
+{
+   lpf->exponent = 1.0f-expf( -(1.0f/44100.0f) * 2.0f * VG_PIf * freq );
+}
+
+void dsp_init_lpf( struct dsp_lpf *lpf, f32 freq )
+{
+   lpf->buffer = _dsp_allocate( 4 );
+   lpf->buffer[0] = 0.0f;
+   dsp_update_lpf( lpf, freq );
+}
+
+void dsp_write_lpf( struct dsp_lpf *lpf, f32 *s )
+{
+   f32 diff = *s - lpf->buffer[0];
+   lpf->buffer[0] += diff * lpf->exponent;
+}
+
+void dsp_read_lpf( struct dsp_lpf *lpf, f32 *s )
+{
+   *s = lpf->buffer[0];
+}
+
+void dsp_init_schroeder( struct dsp_schroeder *sch, f32 length, f32 gain )
+{
+   dsp_init_delay( &sch->M, length );
+   sch->gain = gain;
+}
+
+void dsp_process_schroeder( struct dsp_schroeder *sch, f32 *input, f32 *output )
+{
+   f32 dry = *input;
+   f32 delay_output;
+   dsp_read_delay( &sch->M, &delay_output, 1 );
+
+   f32 feedback_attenuated = delay_output * sch->gain,
+       input_feedback_sum  = dry + feedback_attenuated;
+
+   dsp_write_delay( &sch->M, &input_feedback_sum );
+   *output = delay_output - input_feedback_sum*sch->gain;
+}
+
+/* GLOBAL DSP DESIGN */
+static struct dsp_lpf __lpf_mud_free;
+static struct dsp_delay __echos[8];
+
+#ifdef VG_ECHO_LPF_BUTTERWORTH
+static struct dsp_biquad __echos_lpf[8];
+#else
+static struct dsp_lpf   __echos_lpf[8];
+#endif
+
+static struct dsp_schroeder __diffusion_chain[8];
+
+void _dsp_process( f32 *stereo_in, f32 *stereo_out )
+{
+   f32 in_total = (stereo_in[0]+stereo_in[1])*0.5f;
+   f32 recieved = 0.0f;
+
+   for( i32 i=0; i<8; i++ )
+   {
+      f32 echo;
+      dsp_read_delay(  __echos+i, &echo, 1 );
+
+#ifdef VG_ECHO_LPF_BUTTERWORTH
+      echo = dsp_biquad_process( __echos_lpf+i, echo );
+#else
+      dsp_write_lpf( __echos_lpf+i, &echo );
+      dsp_read_lpf(  __echos_lpf+i, &echo );
+#endif
+      recieved += echo * _dsp.echo_tunings[i]*0.98;
+   }
+
+   f32 diffused = recieved;
+   for( i32 i=0; i<8; i++ )
+      dsp_process_schroeder( __diffusion_chain+i, &diffused, &diffused );
+
+   f32 diffuse_mix = _dsp.reverb_wet_mix;
+       diffuse_mix = f32_lerp( recieved, diffused, diffuse_mix );
+   f32 total = in_total + diffuse_mix;
+
+   f32 low_mud;
+   dsp_write_lpf( &__lpf_mud_free, &total );
+   dsp_read_lpf(  &__lpf_mud_free, &low_mud );
+
+   total -= low_mud;
+   for( i32 i=0; i<8; i++ )
+      dsp_write_delay( __echos+i, &total );
+
+   stereo_out[0]  = stereo_in[0]*_dsp.reverb_dry_mix;
+   stereo_out[1]  = stereo_in[1]*_dsp.reverb_dry_mix;
+   stereo_out[0] += diffuse_mix*2.0f*_dsp.reverb_wet_mix;
+   stereo_out[1] += diffuse_mix*2.0f*_dsp.reverb_wet_mix;
+}
+
+void _dsp_update_tunings( f32 echo_distances[14] )
+{
+   f32 sizes[] = { 2.0f, 4.0f, 8.0f, 16.0f,   32.0f, 64.0f, 128.0f, 256.0f };
+   f32 volumes[] = { 0.2f, 0.3f, 0.5f, 0.7f,   0.8f, 0.9f, 1.0f, 1.0f };
+   f32 avg_distance = 0.0f;
+
+   for( i32 i=0; i<8; i++ )
+      _dsp.echo_tunings[i] = 0.5f;
+
+   for( i32 j=0; j<14; j++ )
+   {
+      f32 d = echo_distances[j];
+      for( i32 i=0; i<7; i++ )
+      {
+         if( d < sizes[i+1] )
+         {
+            f32 range = sizes[i+1]-sizes[i];
+            f32 t = f32_clamp( (d - sizes[i])/range, 0.0f, 1.0f );
+            _dsp.echo_tunings[i  ] += 1.0f-t;
+            _dsp.echo_tunings[i+1] += t;
+            break;
+         }
+      }
+
+      avg_distance += d;
+   }
+   avg_distance /= 14.0f;
+   
+   _dsp.reverb_wet_mix =1.0f-f32_clamp((avg_distance-30.0f)/200.0f,0.0f,1.0f);
+   _dsp.reverb_dry_mix =1.0f-_dsp.reverb_wet_mix*0.4f;
+
+   f32 total = 0.0f;
+   for( i32 i=0; i<8; i++ )
+      total += _dsp.echo_tunings[i];
+
+   if( total > 0.0f )
+   {
+      f32 inverse = 1.0f/total;
+      for( i32 i=0;i<8; i++ )
+         _dsp.echo_tunings[i] *= inverse;
+   }
+
+   for( i32 i=0; i<8; i++ )
+   {
+      f32 freq = f32_lerp( 200.0f, 500.0f, _dsp.echo_tunings[i] );
+
+#ifdef VG_ECHO_LPF_BUTTERWORTH
+      dsp_init_biquad_butterworth_lpf( &__echos_lpf[i], freq );
+#else
+      dsp_update_lpf( &__echos_lpf[i], freq );
+#endif
+   }
+
+   for( i32 i=0;i<8; i++ )
+      _dsp.echo_tunings[i] *= volumes[i];
+}
+
+void _dsp_init(void)
+{
+   $log( $info, {"[INIT] _dsp_init"} );
+   _dsp_update_tunings( (f32[14]){ 12.0f,8.5465f, 18.122f,60.0f, 100.0f,120.2f, 5.0f,7.8f, 23.0f,82.2f, 30.8f,42.3f, 29.2f,15.6f } );
+
+   mt_random_seed( &_dsp.rand, 461 );
+   stack_init( &_dsp.allocator, NULL, BYTES_MB(1), "DSP ALLOCATOR" );
+
+   /* GLOBAL design */
+   dsp_init_lpf( &__lpf_mud_free, 125.0f );
+
+   f32 sizes[] = { 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f, 128.0f, 256.0f };
+   f32 variance = 0.1f;
+
+   for( int i=0; i<8; i++ )
+   {
+      f32 reflection_time = ((sizes[i])/343.0f) * 1000.0f;
+      f32 var   = 1.0f + (mt_random_f64(&_dsp.rand)*2.0f - 1.0f) * variance,
+          total = reflection_time * var;
+
+      dsp_init_delay( &__echos[i], total / 1000.0f );
+      f32 freq = f32_lerp( 800.0f, 350.0f, sizes[i] / 256.0f );
+
+#ifdef VG_ECHO_LPF_BUTTERWORTH
+      dsp_init_biquad_butterworth_lpf( &__echos_lpf[i], freq );
+#else
+      dsp_init_lpf( &__echos_lpf[i], freq );
+#endif
+   }
+
+   f32 diffusions[] = { 187.0f, 159.0f, 143.0f, 121.0f, 79.0f,  57.0f, 27.0f,  11.0f };
+   for( i32 i=0; i<8; i++ )
+      dsp_init_schroeder( __diffusion_chain+i, diffusions[i]/1000.0f, 0.7f );
+}
diff --git a/source/engine/vg_audio_dsp.h b/source/engine/vg_audio_dsp.h
new file mode 100644 (file)
index 0000000..c9feb99
--- /dev/null
@@ -0,0 +1,5 @@
+//#define VG_ECHO_LPF_BUTTERWORTH
+#pragma once
+
+void _dsp_update_tunings( f32 echo_distances[14] );
+void _dsp_process( f32 *stereo_in, f32 *stereo_out );
diff --git a/source/engine/vg_audio_vorbis.c b/source/engine/vg_audio_vorbis.c
new file mode 100644 (file)
index 0000000..b9ffa60
--- /dev/null
@@ -0,0 +1,74 @@
+#include "vg_audio_vorbis.h"
+
+#undef STB_VORBIS_HEADER_ONLY
+#include "../../submodules/stb/stb_vorbis.c"
+
+/*
+ * adapted from stb_vorbis.h, since the original does not handle mono->stereo
+ */
+int stb_vorbis_get_samples_float_interleaved_stereo( stb_vorbis *f, float *buffer, int len )
+{
+   int n = 0, c = 1;
+   if( f->channels < 2 ) c = 0;
+
+   while( n < len ) {
+      int k = f->channel_buffer_end - f->channel_buffer_start;
+
+      if( n+k >= len )
+         k = len - n;
+
+      for( int j=0; j < k; ++j ) {
+         *buffer++ = f->channel_buffers[ 0 ][f->channel_buffer_start+j];
+         *buffer++ = f->channel_buffers[ c ][f->channel_buffer_start+j];
+      }
+
+      n += k;
+      f->channel_buffer_start += k;
+
+      if( n == len )
+         break;
+
+      if( !stb_vorbis_get_frame_float( f, NULL, NULL ))
+         break;
+   }
+
+   return n;
+}
+
+/*
+ * ........ more wrecked code sorry!
+ */
+int stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, signed short *buffer, int len )
+{
+   int n = 0, c = 1;
+   if( f->channels < 2 ) c = 0;
+
+   while( n < len ) {
+      int k = f->channel_buffer_end - f->channel_buffer_start;
+
+      if( n+k >= len )
+         k = len - n;
+
+      for( int j=0; j < k; ++j ) {
+         float sl = f->channel_buffers[ 0 ][f->channel_buffer_start+j],
+               sr = f->channel_buffers[ c ][f->channel_buffer_start+j],
+               s  = 0.5f*(sl+sr);
+
+         if( s < -1.0f ) s = -1.0f;
+         if( s >  1.0f ) s =  1.0f;
+
+         *buffer++ = s * 32767.0f;
+      }
+
+      n += k;
+      f->channel_buffer_start += k;
+
+      if( n == len )
+         break;
+
+      if( !stb_vorbis_get_frame_float( f, NULL, NULL ))
+         break;
+   }
+
+   return n;
+}
diff --git a/source/engine/vg_audio_vorbis.h b/source/engine/vg_audio_vorbis.h
new file mode 100644 (file)
index 0000000..c813e85
--- /dev/null
@@ -0,0 +1,11 @@
+#pragma once
+
+#define STB_VORBIS_HEADER_ONLY
+#define STB_VORBIS_MAX_CHANNELS 2
+
+#include "../../submodules/stb/stb_vorbis.c"
+
+/* This is just an extension to submodules/stb/stb_vorbis.c, so that we don't have to patch the file directly. */
+
+int stb_vorbis_get_samples_float_interleaved_stereo( stb_vorbis *f, float *buffer, int len );
+int stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, signed short *buffer, int len );
diff --git a/source/engine/vg_camera.c b/source/engine/vg_camera.c
new file mode 100644 (file)
index 0000000..e807126
--- /dev/null
@@ -0,0 +1,80 @@
+#include "foundation.h"
+#include "common_maths.h"
+#include "vg_camera.h"
+
+void vg_camera_lerp_angles( f32 a[3], f32 b[3], f32 t, f32 d[3] )
+{
+   d[0] = f32_radian_lerp( a[0], b[0], t );
+   d[1] = f32_lerp(  a[1], b[1], t );
+   d[2] = f32_lerp(  a[2], b[2], t );
+}
+
+/* lerp position, fov, and angles */
+void vg_camera_lerp( struct vg_camera *a, struct vg_camera *b, f32 t, struct vg_camera *d )
+{
+   v3_lerp( a->pos, b->pos, t, d->pos );
+   vg_camera_lerp_angles( a->angles, b->angles, t, d->angles );
+   d->fov = f32_lerp( a->fov, b->fov, t );
+}
+
+void vg_camera_copy( struct vg_camera *a, struct vg_camera *d )
+{
+   v3_copy( a->pos, d->pos );
+   v3_copy( a->angles, d->angles );
+   d->fov = a->fov;
+}
+
+void vg_m4x3_transform_camera( f32 m[4][3], struct vg_camera *cam )
+{
+   m4x3_mulv( m, cam->pos, cam->pos );
+
+   f32 v0[3];
+   v3_angles_to_vector( cam->angles, v0 );
+   m3x3_mulv( m, v0, v0 );
+   v3_normalize( v0 );
+   v3_vector_to_angles( v0, cam->angles );
+}
+
+/*
+ * 1) [angles, pos] -> transform 
+ */
+void vg_camera_update_transform( struct vg_camera *cam )
+{
+   f32 qyaw[4], qpitch[4], qroll[4], qcam[4];
+   q_axis_angle( qyaw,   (f32[]){ 0.0f, 1.0f, 0.0f }, -cam->angles[0] );
+   q_axis_angle( qpitch, (f32[]){ 1.0f, 0.0f, 0.0f }, -cam->angles[1] );
+   q_axis_angle( qroll,  (f32[]){ 0.0f, 0.0f, 1.0f }, -cam->angles[2] );
+
+   q_mul( qyaw, qpitch, qcam );
+   q_mul( qcam, qroll, qcam );
+   q_m3x3( qcam, cam->transform );
+   v3_copy( cam->pos, cam->transform[3] );
+}
+
+/*
+ * 2) [transform] -> transform_inverse, view matrix
+ */
+void vg_camera_update_view( struct vg_camera *cam )
+{
+   m4x4_copy( cam->mtx.v,  cam->mtx_prev.v );
+   m4x3_invert_affine( cam->transform, cam->transform_inverse );
+   m4x3_expand( cam->transform_inverse, cam->mtx.v );
+}
+
+/*
+ * 3) [fov,nearz,farz] -> projection matrix
+ */
+void vg_camera_update_projection( struct vg_camera *cam, f32 vw, f32 vh )
+{
+   m4x4_copy( cam->mtx.p,  cam->mtx_prev.p );
+   m4x4_projection( cam->mtx.p, cam->fov, (f32)vw / (f32)vh, cam->nearz, cam->farz );
+}
+
+/*
+ * 4) [projection matrix, view matrix] -> previous pv, new pv 
+ */
+void vg_camera_finalize( struct vg_camera *cam )
+{
+   m4x4_copy( cam->mtx.pv, cam->mtx_prev.pv );
+   m4x4_mul( cam->mtx.p, cam->mtx.v, cam->mtx.pv );
+}
diff --git a/source/engine/vg_camera.h b/source/engine/vg_camera.h
new file mode 100644 (file)
index 0000000..b8ab30f
--- /dev/null
@@ -0,0 +1,49 @@
+#pragma once
+
+struct vg_camera
+{
+   /* Input */
+   f32 angles[3];
+   f32 pos[3];
+   f32 fov, nearz, farz;
+
+   /* Output */
+   f32 transform[4][3],
+       transform_inverse[4][3];
+
+   struct vg_camera_mtx
+   {
+      f32 p[4][4],
+          v[4][4],
+         pv[4][4];
+   }
+   mtx,
+   mtx_prev;
+};
+
+void vg_camera_lerp_angles( f32 a[3], f32 b[3], f32 t, f32 d[3] );
+
+/* lerp position, fov, and angles */
+void vg_camera_lerp( struct vg_camera *a, struct vg_camera *b, f32 t, struct vg_camera *d );
+void vg_camera_copy( struct vg_camera *a, struct vg_camera *d );
+void vg_m4x3_transform_camera( f32 m[4][3], struct vg_camera *cam );
+
+/*
+ * 1) [angles, pos] -> transform 
+ */
+void vg_camera_update_transform( struct vg_camera *cam );
+
+/*
+ * 2) [transform] -> transform_inverse, view matrix
+ */
+void vg_camera_update_view( struct vg_camera *cam );
+
+/*
+ * 3) [fov,nearz,farz] -> projection matrix
+ */
+void vg_camera_update_projection( struct vg_camera *cam, f32 vw, f32 vh );
+
+/*
+ * 4) [projection matrix, view matrix] -> previous pv, new pv 
+ */
+void vg_camera_finalize( struct vg_camera *cam );
diff --git a/source/engine/vg_engine.c b/source/engine/vg_engine.c
new file mode 100644 (file)
index 0000000..925c293
--- /dev/null
@@ -0,0 +1,301 @@
+#include "foundation.h"
+#include "vg_async.h"
+#include <time.h>
+#include <math.h>
+#include <time.h>
+#include "vg_input.h"
+#include "vg_opengl.h"
+#include "vg_engine.h"
+#include "vg_ui.h"
+
+// TODO: temp
+#include "console_system.h"
+#include "generated/hooks.h"
+
+struct _engine _engine;
+
+struct
+{
+   b8 console_open;
+}
+_engine_backend;
+
+static void _engine_set_framerate( f64 hz, b8 vsync )
+{
+   SDL_GL_SetSwapInterval( vsync );
+   _engine.vsync = vsync;
+
+   if( hz < 24.0 )
+      hz = 24.0;
+   if( hz > 300.0 )
+      hz = 300.0;
+
+   if( vsync )
+      hz += 5.0; // Timing allowance
+
+   _engine.framerate_limit = hz;
+}
+
+#if 0
+void _character_callback( GLFWwindow *window, u32 codepoint )
+{
+   if( _input_layer_filter( 1<<k_input_layer_ui ) )
+      _engine_ui_input_character( codepoint );
+}
+#endif
+
+void _engine_options(void)
+{
+   const c8 *arg;
+   if( _option_long( "high-performance", "Turn graphics to lowest quality" ) )
+      _engine.quality = k_quality_profile_low;
+
+   //if( vg_long_opt( "no-steam", "Disable Steam integration (Good idea for pirating)" ) )
+   //   _steam_api.disabled = 1;
+}
+
+
+i32 async_thread( void *_ )
+{
+   _set_thread_id( k_thread_async );
+   while( _task_queue_process( 1 ) ) {}
+   $log( $warning, {"Async thread exits"} );
+   return 0;
+}
+
+APIENTRY void _opengl_debug( GLenum source,
+            GLenum type,
+            GLuint id,
+            GLenum severity,
+            GLsizei length,
+            const GLchar *message,
+            const void *userParam)
+{
+   if( type == GL_DEBUG_TYPE_ERROR || type == GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR )
+      $log( $error, {message} );
+
+   if( type == GL_DEBUG_TYPE_ERROR )
+      _fatal_exit();
+}
+
+#include <unistd.h>
+
+i32 main( i32 argc, const c8 *argv[] )
+{
+   VG_PRE_MAIN;
+
+   $log( $info, {"SDL_INIT_VIDEO"} );
+   if( SDL_Init( SDL_INIT_VIDEO ) == 0 )
+   {
+      $log( $fatal, {"SDL_Init failed: "}, {SDL_GetError()} );
+      _fatal_exit();
+   }
+
+   $log( $info, {"SDL_INIT_AUDIO"} );
+   SDL_InitSubSystem( SDL_INIT_AUDIO );
+
+#if 0
+   $log( $info, {"SDL_INIT_GAMECONTROLLER"} );
+   SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER );
+#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
+   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 );
+   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 );
+
+   /*
+    * Get monitor information
+    */
+
+   const SDL_DisplayMode *video_mode = SDL_GetDesktopDisplayMode( SDL_GetPrimaryDisplay() );
+   if( video_mode == NULL )
+   {
+      $log( $error, {"SDL_GetDesktopDisplayMode failed: "}, {SDL_GetError()} );
+      SDL_Quit();
+      _fatal_exit();
+   }
+
+   _engine.w = video_mode->w;
+   _engine.h = video_mode->h;
+
+   //FIXME
+   //if( _vg_window.display_mode == k_vg_window_windowed )
+   if( 1 )
+   {
+      _engine.w = 1280;
+      _engine.h = 720;
+   }
+
+#ifndef _WIN32
+       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;
+
+#if 0
+   if( _vg_window.display_mode == k_vg_window_fullscreen )
+      flags |= SDL_WINDOW_FULLSCREEN;
+   else if( _vg_window.display_mode == k_vg_window_fullscreen_desktop )
+      flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
+#endif
+
+   $log( $info, {"SDL_CreateWindow()"} );
+   if(( _engine.window_handle = SDL_CreateWindow( "Voyager Game Engine", _engine.w, _engine.h, flags )))
+   {
+#if 0
+      if( _vg_window.display_mode == k_vg_window_windowed )
+#endif
+         SDL_SetWindowPosition( _engine.window_handle, video_mode->w-_engine.w, 0 );
+   }
+   else
+   {
+      $log( $fatal, {"SDL_CreateWindow failed: "}, {SDL_GetError()} );
+      _fatal_exit();
+   }
+
+   SDL_RaiseWindow( _engine.window_handle );
+   SDL_SetWindowMinimumSize( _engine.window_handle, 1280, 720 );
+   SDL_SetWindowMaximumSize( _engine.window_handle, 1920, 1080 );
+
+   /*
+    * OpenGL loading
+    */
+   if( ( _engine.opengl_handle = SDL_GL_CreateContext( _engine.window_handle ) ))
+   {
+      SDL_GetWindowSizeInPixels( _engine.window_handle, &_engine.w, &_engine.h );
+      $log( $ok, {"Window created "}, $signed( _engine.w ), {"x"}, $signed( _engine.h ) );
+   }
+   else
+   {
+      $log( $fatal, {"SDL_GL_CreateContext failed: "}, {SDL_GetError()} );
+      SDL_Quit();
+      _fatal_exit();
+   }
+
+   if( !gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress) )
+   {
+      $log( $fatal, {"Glad Failed to initialize"} );
+      SDL_GL_DestroyContext( _engine.opengl_handle );
+      SDL_Quit();
+      _fatal_exit();
+   }
+
+   const c8* glver = glGetString( GL_VERSION );
+   $log( $ok, {"Load setup complete, OpenGL version: "}, {glver} );
+
+   glEnable              ( GL_DEBUG_OUTPUT );
+   glDebugMessageCallback( _opengl_debug, 0 );
+
+   SDL_GL_SetSwapInterval(0); /* disable vsync while loading */
+
+   i32 rate = video_mode->refresh_rate;
+   if( rate < 25 || rate > 300 )
+      rate = 60;
+   $log( $info, {"Display refresh rate: "}, $signed( video_mode->refresh_rate ), {", we chose: "}, $signed(rate) );
+   _engine_set_framerate( rate, 1 );
+   //SDL_SetRelativeMouseMode(1);
+
+   f64 next_frame_time = 0.0, fixed_time = 0.0, fixed_accumulator = 0.0;
+
+
+   /* ------------- */
+
+   EVENT_CALL( START );
+   SDL_CreateThread( async_thread, "ASync thread", NULL );
+   ASSERT_CRITICAL( async_thread );
+
+L_new_frame:;
+   f64 now = (f64)SDL_GetTicksNS() / 1000000000.0;
+   if( now < next_frame_time )
+   {
+      f64 ms = (u32)floor((next_frame_time - now) * 1000.0);
+      if( ms )
+      {
+         struct timespec r = { 0, ms * 1000000 };
+         nanosleep( &r, NULL );
+      }
+      else
+      {
+         __asm__ __volatile__("pause\n");
+      }
+      goto L_new_frame;
+   }
+
+   _engine.time_delta = now - _engine.time;
+   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;
+   next_frame_time = now + 1.0/_engine.framerate_limit;
+
+   while( _task_queue_process( 0 ) ) {}
+
+   /* normal update */
+   EVENT_CALL( ENGINE_NEW_FRAME );
+
+   /* 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;
+         EVENT_CALL( ENGINE_FIXED_UPDATE );
+      }
+      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) )
+   {
+      EVENT_CALL( ENGINE_WINDOW_RESIZE );
+   }
+
+   EVENT_CALL( ENGINE_RENDER );
+   _engine_ui_pre_render();
+   EVENT_CALL( ENGINE_UI );
+   _engine_ui_post_render();
+
+   //glfwSwapBuffers(_engine.window_handle);
+   SDL_GL_SwapWindow( _engine.window_handle );
+
+   SDL_Event e;
+   while( SDL_PollEvent( &e ) )
+   {
+      if( e.type == SDL_EVENT_QUIT )
+      {
+         SDL_DestroyWindow( _engine.window_handle );
+         SDL_Quit();
+         _normal_exit();
+      }
+      else if( e.type == SDL_EVENT_KEY_DOWN || e.type == SDL_EVENT_KEY_UP )
+      {
+         _input_keyboard_event( (SDL_KeyboardEvent *)&e );
+      }
+   }
+
+   goto L_new_frame;
+}
diff --git a/source/engine/vg_engine.h b/source/engine/vg_engine.h
new file mode 100644 (file)
index 0000000..091cc2f
--- /dev/null
@@ -0,0 +1,21 @@
+struct _engine
+{
+   b8 vsync;
+   f64 framerate_limit;
+   f64 time, time_delta, time_fixed_extrapolate;
+
+   enum quality_profile
+   {
+      k_quality_profile_high = 0,
+      k_quality_profile_low = 1,
+      k_quality_profile_min = 2
+   }
+   quality;
+
+   void *window_handle;
+   void *opengl_handle;
+   i32 w, h;
+
+   i32 native_fbo;
+}
+extern _engine;
diff --git a/source/engine/vg_engine_core.kv b/source/engine/vg_engine_core.kv
new file mode 100644 (file)
index 0000000..d639c16
--- /dev/null
@@ -0,0 +1,379 @@
+{
+   add vg_engine.c
+   hook
+   {
+      event OPTIONS
+      function _engine_options
+   }
+   event
+   {
+      name ENGINE_NEW_FRAME
+      prototype "void"
+   }
+   event
+   {
+      name ENGINE_FIXED_UPDATE
+      prototype "void"
+   }
+   event
+   {
+      name ENGINE_RENDER
+      prototype "void"
+   }
+   event
+   {
+      name ENGINE_UI
+      prototype "void"
+   }
+   event
+   {
+      name ENGINE_WINDOW_RESIZE
+      prototype "void"
+   }
+}
+
+{
+   add ui.c
+   hook
+   {
+      event START
+      function _engine_ui_init
+   }
+}
+
+{
+   add input.c
+   hook
+   {
+      event START
+      function _input_init
+   }
+   hook
+   {
+      event ENGINE_NEW_FRAME
+      function _input_update
+   }
+   ccmd
+   {
+      name bind
+      function _input_bind_ccmd
+      description "Bind device input to a button, action or axis"
+
+      parameter
+      {
+         description "Device input alias"
+      }
+
+      parameter
+      {
+         description "button,action,axis name"
+      }
+   }
+   ccmd
+   {
+      name unbind
+      description "Unbind all bindings that match"
+      function _input_unbind_ccmd
+
+      parameter
+      {
+         description "binding"
+      }
+   }
+}
+
+{
+   add console.c
+   hook
+   {
+      event START
+      function _engine_console_init
+   }
+   hook
+   {
+      event ENGINE_NEW_FRAME
+      function _engine_console_update
+   }
+   hook
+   {
+      event ENGINE_UI
+      function _engine_console_ui
+      affinity 9999
+   }
+}
+
+{
+   add vg_framebuffer.c
+   hook 
+   {
+      event ENGINE_WINDOW_RESIZE
+      function _framebuffer_resize
+   }
+}
+
+{
+add source/engine/shader.c
+input_layer
+{
+   name console
+}
+input_layer
+{
+   name ui
+}
+input
+{
+   name console
+   type action
+   layer_mask console
+}
+
+append async.kv
+thread
+{
+   name main
+   queue_size_m 40
+   flag MAIN
+   flag OPENGL
+}
+thread
+{
+   name async
+   queue_size_m 40
+   flag ASYNC
+}
+thread
+{
+   name audio
+}
+thread
+{
+   name exitor
+   queue_size_m 1
+}
+
+append graphics.kv
+
+ccmd
+{
+   name exec
+   function _console_exec_ccmd
+   description "Execute a configuration file"
+
+   parameter
+   {
+      description "The path to the config"
+   }
+}
+config "bind ALT+GRAVE_ACCENT console"
+
+config "bind BACKSPACE ui_backspace"
+config "bind DELETE ui_delete"
+config "bind ENTER ui_enter"
+config "bind TAB ui_indent"
+config "bind HOME ui_home"
+config "bind END ui_end"
+config "bind LEFT ui_left"
+config "bind RIGHT ui_right"
+config "bind UP ui_up"
+config "bind DOWN ui_down"
+config "bind MOUSE_LEFT ui_click"
+
+config "bind LEFT_SHIFT ui_select"
+config "bind RIGHT_SHIFT ui_select"
+
+input
+{
+   name ui_delete
+   type action
+   layer_mask ui
+}
+input
+{
+   name ui_backspace
+   type action
+   layer_mask ui
+}
+input
+{
+   name ui_enter
+   type action
+   layer_mask ui
+}
+input
+{
+   name ui_indent
+   type action
+   layer_mask ui
+}
+
+input
+{
+   name ui_select
+   type button
+   layer_mask ui
+}
+input
+{
+   name ui_home
+   type button
+   layer_mask ui
+}
+input
+{
+   name ui_end
+   type button
+   layer_mask ui
+}
+input
+{
+   name ui_left
+   type button
+   layer_mask ui
+}
+input
+{
+   name ui_right
+   type button
+   layer_mask ui
+}
+input
+{
+   name ui_up
+   type button
+   layer_mask ui
+}
+input
+{
+   name ui_down
+   type button
+   layer_mask ui
+}
+input 
+{
+   name ui_click
+   type action
+   layer_mask ui
+}
+
+shader
+{
+   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
+      }
+   }
+}
+
+cvar
+{
+   name test
+   type u32
+   default 5
+   cheat 1
+   description "This is just a test variable!"
+}
+
+hook
+{
+   event START
+   function "_shaders_compile"
+}
+}
+
+
+
+
+
+
+add vg_camera.c
+
+
+add vg_tex.c
+hook
+{
+   event START
+   function _vg_tex_init
+}
+
+
+add vg_lines.c
+hook
+{
+   event START
+   function _vg_lines_init
+}
+cvar
+{
+   name debug_lines
+   type u32
+   default 0
+   cheat 1
+   description "Show line debuggers"
+}
+shader
+{
+   name debug_lines
+   subshader
+   {
+      type vertex
+      add shaders/debug_lines.vs
+      uniform
+      {
+         type mat4
+         alias uPv
+      }
+   }
+   subshader
+   {
+      type fragment
+      add shaders/debug_lines.fs
+   }
+}
+
+
+add vg_audio.c
+add vg_audio_dsp.c
+add vorbis.c
+hook
+{
+   affinity -3008
+   event START
+   function _audio_init
+}
+hook
+{
+   affinity -6004
+   event START
+   function _dsp_init
+}
+
+
+add model.c
+add metascene.c
+add array_file.c
index 76efbc59a8fe48dab3d3679cdb6a531cdcb629b3..aa77caad2f0e39edb64f1371f531f8be53479a39 100644 (file)
@@ -1,9 +1,9 @@
 #include "vg_framebuffer.h"
-#include "common_api.h"
-#include "engine_interface.h"
+#include "foundation.h"
+#include "vg_engine.h"
 #include "vg_tex.h"
-#include "maths/common_maths.h"
-#include "common_thread_api.h"
+#include "vg_async.h"
+#include "common_maths.h"
 
 struct
 {
@@ -13,7 +13,7 @@ struct
 }
 static _framebuffer;
 
-struct framebuffer *_framebuffer_allocate( struct stack_allocator *stack, u32 attachment_count, bool track )
+struct framebuffer *_framebuffer_allocate( struct stack_allocator *stack, u32 attachment_count, b8 track )
 {
    u32 size = sizeof(struct framebuffer) + sizeof(struct framebuffer_attachment)*attachment_count;
    struct framebuffer *fb = stack_allocate( stack, size, 8, "Framebuffer metadata" );
index eed53ac3a25228290518c45d724a11f7c78369a0..866a55693186fa98e6f45aff368cedb28e776c0e 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
-#include "opengl.h"
+#include "vg_opengl.h"
 #include "vg_tex.h"
+#include "vg_async.h"
 
 #define VG_FRAMEBUFFER_GLOBAL 1
 #define VG_FRAMEBUFFER_SPECIALIZED 0
@@ -51,7 +52,7 @@ struct framebuffer
    attachments[];
 };
 
-struct framebuffer *_framebuffer_allocate( struct stack_allocator *stack, u32 attachment_count, bool track );
+struct framebuffer *_framebuffer_allocate( struct stack_allocator *stack, u32 attachment_count, b8 track );
 
 /* 
  * Get the current (automatically scaled or fixed) resolution of framebuffer
diff --git a/source/engine/vg_input.c b/source/engine/vg_input.c
new file mode 100644 (file)
index 0000000..20b3677
--- /dev/null
@@ -0,0 +1,723 @@
+#include "foundation.h"
+#include "vg_engine.h"
+#include "vg_input.h"
+#include "console_system.h"
+#include "generated/input.c"
+#include "SDL3/SDL.h"
+
+struct input_alias
+{
+   const c8 *alias;
+   u8 device_type;
+   u32 key;
+   u32 alias_hash;
+}
+_input_aliases[] = 
+{
+   { "SHIFT", k_input_device_modifier, .key = SDL_KMOD_LSHIFT },
+   { "CONTROL", k_input_device_modifier, .key = SDL_KMOD_LCTRL },
+   { "RETURN", k_input_device_keyboard, .key = SDLK_RETURN },
+   { "ESCAPE", k_input_device_keyboard, .key = SDLK_ESCAPE },
+   { "BACKSPACE", k_input_device_keyboard, .key = SDLK_BACKSPACE },
+   { "TAB", k_input_device_keyboard, .key = SDLK_TAB },
+   { "SPACE", k_input_device_keyboard, .key = SDLK_SPACE },
+   { "EXCLAIM", k_input_device_keyboard, .key = SDLK_EXCLAIM },
+   { "DBLAPOSTROPHE", k_input_device_keyboard, .key = SDLK_DBLAPOSTROPHE },
+   { "HASH", k_input_device_keyboard, .key = SDLK_HASH },
+   { "DOLLAR", k_input_device_keyboard, .key = SDLK_DOLLAR },
+   { "PERCENT", k_input_device_keyboard, .key = SDLK_PERCENT },
+   { "AMPERSAND", k_input_device_keyboard, .key = SDLK_AMPERSAND },
+   { "APOSTROPHE", k_input_device_keyboard, .key = SDLK_APOSTROPHE },
+   { "LEFTPAREN", k_input_device_keyboard, .key = SDLK_LEFTPAREN },
+   { "RIGHTPAREN", k_input_device_keyboard, .key = SDLK_RIGHTPAREN },
+   { "ASTERISK", k_input_device_keyboard, .key = SDLK_ASTERISK },
+   { "PLUS", k_input_device_keyboard, .key = SDLK_PLUS },
+   { "COMMA", k_input_device_keyboard, .key = SDLK_COMMA },
+   { "MINUS", k_input_device_keyboard, .key = SDLK_MINUS },
+   { "PERIOD", k_input_device_keyboard, .key = SDLK_PERIOD },
+   { "SLASH", k_input_device_keyboard, .key = SDLK_SLASH },
+   { "0", k_input_device_keyboard, .key = SDLK_0 },
+   { "1", k_input_device_keyboard, .key = SDLK_1 },
+   { "2", k_input_device_keyboard, .key = SDLK_2 },
+   { "3", k_input_device_keyboard, .key = SDLK_3 },
+   { "4", k_input_device_keyboard, .key = SDLK_4 },
+   { "5", k_input_device_keyboard, .key = SDLK_5 },
+   { "6", k_input_device_keyboard, .key = SDLK_6 },
+   { "7", k_input_device_keyboard, .key = SDLK_7 },
+   { "8", k_input_device_keyboard, .key = SDLK_8 },
+   { "9", k_input_device_keyboard, .key = SDLK_9 },
+   { "COLON", k_input_device_keyboard, .key = SDLK_COLON },
+   { "SEMICOLON", k_input_device_keyboard, .key = SDLK_SEMICOLON },
+   { "LESS", k_input_device_keyboard, .key = SDLK_LESS },
+   { "EQUALS", k_input_device_keyboard, .key = SDLK_EQUALS },
+   { "GREATER", k_input_device_keyboard, .key = SDLK_GREATER },
+   { "QUESTION", k_input_device_keyboard, .key = SDLK_QUESTION },
+   { "AT", k_input_device_keyboard, .key = SDLK_AT },
+   { "LEFTBRACKET", k_input_device_keyboard, .key = SDLK_LEFTBRACKET },
+   { "BACKSLASH", k_input_device_keyboard, .key = SDLK_BACKSLASH },
+   { "RIGHTBRACKET", k_input_device_keyboard, .key = SDLK_RIGHTBRACKET },
+   { "CARET", k_input_device_keyboard, .key = SDLK_CARET },
+   { "UNDERSCORE", k_input_device_keyboard, .key = SDLK_UNDERSCORE },
+   { "GRAVE", k_input_device_keyboard, .key = SDLK_GRAVE },
+   { "A", k_input_device_keyboard, .key = SDLK_A },
+   { "B", k_input_device_keyboard, .key = SDLK_B },
+   { "C", k_input_device_keyboard, .key = SDLK_C },
+   { "D", k_input_device_keyboard, .key = SDLK_D },
+   { "E", k_input_device_keyboard, .key = SDLK_E },
+   { "F", k_input_device_keyboard, .key = SDLK_F },
+   { "G", k_input_device_keyboard, .key = SDLK_G },
+   { "H", k_input_device_keyboard, .key = SDLK_H },
+   { "I", k_input_device_keyboard, .key = SDLK_I },
+   { "J", k_input_device_keyboard, .key = SDLK_J },
+   { "K", k_input_device_keyboard, .key = SDLK_K },
+   { "L", k_input_device_keyboard, .key = SDLK_L },
+   { "M", k_input_device_keyboard, .key = SDLK_M },
+   { "N", k_input_device_keyboard, .key = SDLK_N },
+   { "O", k_input_device_keyboard, .key = SDLK_O },
+   { "P", k_input_device_keyboard, .key = SDLK_P },
+   { "Q", k_input_device_keyboard, .key = SDLK_Q },
+   { "R", k_input_device_keyboard, .key = SDLK_R },
+   { "S", k_input_device_keyboard, .key = SDLK_S },
+   { "T", k_input_device_keyboard, .key = SDLK_T },
+   { "U", k_input_device_keyboard, .key = SDLK_U },
+   { "V", k_input_device_keyboard, .key = SDLK_V },
+   { "W", k_input_device_keyboard, .key = SDLK_W },
+   { "X", k_input_device_keyboard, .key = SDLK_X },
+   { "Y", k_input_device_keyboard, .key = SDLK_Y },
+   { "Z", k_input_device_keyboard, .key = SDLK_Z },
+   { "LEFTBRACE", k_input_device_keyboard, .key = SDLK_LEFTBRACE },
+   { "PIPE", k_input_device_keyboard, .key = SDLK_PIPE },
+   { "RIGHTBRACE", k_input_device_keyboard, .key = SDLK_RIGHTBRACE },
+   { "TILDE", k_input_device_keyboard, .key = SDLK_TILDE },
+   { "DELETE", k_input_device_keyboard, .key = SDLK_DELETE },
+   { "PLUSMINUS", k_input_device_keyboard, .key = SDLK_PLUSMINUS },
+   { "CAPSLOCK", k_input_device_keyboard, .key = SDLK_CAPSLOCK },
+   { "F1", k_input_device_keyboard, .key = SDLK_F1 },
+   { "F2", k_input_device_keyboard, .key = SDLK_F2 },
+   { "F3", k_input_device_keyboard, .key = SDLK_F3 },
+   { "F4", k_input_device_keyboard, .key = SDLK_F4 },
+   { "F5", k_input_device_keyboard, .key = SDLK_F5 },
+   { "F6", k_input_device_keyboard, .key = SDLK_F6 },
+   { "F7", k_input_device_keyboard, .key = SDLK_F7 },
+   { "F8", k_input_device_keyboard, .key = SDLK_F8 },
+   { "F9", k_input_device_keyboard, .key = SDLK_F9 },
+   { "F10", k_input_device_keyboard, .key = SDLK_F10 },
+   { "F11", k_input_device_keyboard, .key = SDLK_F11 },
+   { "F12", k_input_device_keyboard, .key = SDLK_F12 },
+   { "PRINTSCREEN", k_input_device_keyboard, .key = SDLK_PRINTSCREEN },
+   { "SCROLLLOCK", k_input_device_keyboard, .key = SDLK_SCROLLLOCK },
+   { "PAUSE", k_input_device_keyboard, .key = SDLK_PAUSE },
+   { "INSERT", k_input_device_keyboard, .key = SDLK_INSERT },
+   { "HOME", k_input_device_keyboard, .key = SDLK_HOME },
+   { "PAGEUP", k_input_device_keyboard, .key = SDLK_PAGEUP },
+   { "END", k_input_device_keyboard, .key = SDLK_END },
+   { "PAGEDOWN", k_input_device_keyboard, .key = SDLK_PAGEDOWN },
+   { "RIGHT", k_input_device_keyboard, .key = SDLK_RIGHT },
+   { "LEFT", k_input_device_keyboard, .key = SDLK_LEFT },
+   { "DOWN", k_input_device_keyboard, .key = SDLK_DOWN },
+   { "UP", k_input_device_keyboard, .key = SDLK_UP },
+   { "NUMLOCKCLEAR", k_input_device_keyboard, .key = SDLK_NUMLOCKCLEAR },
+   { "KP_DIVIDE", k_input_device_keyboard, .key = SDLK_KP_DIVIDE },
+   { "KP_MULTIPLY", k_input_device_keyboard, .key = SDLK_KP_MULTIPLY },
+   { "KP_MINUS", k_input_device_keyboard, .key = SDLK_KP_MINUS },
+   { "KP_PLUS", k_input_device_keyboard, .key = SDLK_KP_PLUS },
+   { "KP_ENTER", k_input_device_keyboard, .key = SDLK_KP_ENTER },
+   { "KP_1", k_input_device_keyboard, .key = SDLK_KP_1 },
+   { "KP_2", k_input_device_keyboard, .key = SDLK_KP_2 },
+   { "KP_3", k_input_device_keyboard, .key = SDLK_KP_3 },
+   { "KP_4", k_input_device_keyboard, .key = SDLK_KP_4 },
+   { "KP_5", k_input_device_keyboard, .key = SDLK_KP_5 },
+   { "KP_6", k_input_device_keyboard, .key = SDLK_KP_6 },
+   { "KP_7", k_input_device_keyboard, .key = SDLK_KP_7 },
+   { "KP_8", k_input_device_keyboard, .key = SDLK_KP_8 },
+   { "KP_9", k_input_device_keyboard, .key = SDLK_KP_9 },
+   { "KP_0", k_input_device_keyboard, .key = SDLK_KP_0 },
+   { "KP_PERIOD", k_input_device_keyboard, .key = SDLK_KP_PERIOD },
+   { "APPLICATION", k_input_device_keyboard, .key = SDLK_APPLICATION },
+   { "POWER", k_input_device_keyboard, .key = SDLK_POWER },
+   { "KP_EQUALS", k_input_device_keyboard, .key = SDLK_KP_EQUALS },
+   { "F13", k_input_device_keyboard, .key = SDLK_F13 },
+   { "F14", k_input_device_keyboard, .key = SDLK_F14 },
+   { "F15", k_input_device_keyboard, .key = SDLK_F15 },
+   { "F16", k_input_device_keyboard, .key = SDLK_F16 },
+   { "F17", k_input_device_keyboard, .key = SDLK_F17 },
+   { "F18", k_input_device_keyboard, .key = SDLK_F18 },
+   { "F19", k_input_device_keyboard, .key = SDLK_F19 },
+   { "F20", k_input_device_keyboard, .key = SDLK_F20 },
+   { "F21", k_input_device_keyboard, .key = SDLK_F21 },
+   { "F22", k_input_device_keyboard, .key = SDLK_F22 },
+   { "F23", k_input_device_keyboard, .key = SDLK_F23 },
+   { "F24", k_input_device_keyboard, .key = SDLK_F24 },
+   { "EXECUTE", k_input_device_keyboard, .key = SDLK_EXECUTE },
+   { "HELP", k_input_device_keyboard, .key = SDLK_HELP },
+   { "MENU", k_input_device_keyboard, .key = SDLK_MENU },
+   { "SELECT", k_input_device_keyboard, .key = SDLK_SELECT },
+   { "STOP", k_input_device_keyboard, .key = SDLK_STOP },
+   { "AGAIN", k_input_device_keyboard, .key = SDLK_AGAIN },
+   { "UNDO", k_input_device_keyboard, .key = SDLK_UNDO },
+   { "CUT", k_input_device_keyboard, .key = SDLK_CUT },
+   { "COPY", k_input_device_keyboard, .key = SDLK_COPY },
+   { "PASTE", k_input_device_keyboard, .key = SDLK_PASTE },
+   { "FIND", k_input_device_keyboard, .key = SDLK_FIND },
+   { "MUTE", k_input_device_keyboard, .key = SDLK_MUTE },
+   { "VOLUMEUP", k_input_device_keyboard, .key = SDLK_VOLUMEUP },
+   { "VOLUMEDOWN", k_input_device_keyboard, .key = SDLK_VOLUMEDOWN },
+   { "KP_COMMA", k_input_device_keyboard, .key = SDLK_KP_COMMA },
+   { "KP_EQUALSAS400", k_input_device_keyboard, .key = SDLK_KP_EQUALSAS400 },
+   { "ALTERASE", k_input_device_keyboard, .key = SDLK_ALTERASE },
+   { "SYSREQ", k_input_device_keyboard, .key = SDLK_SYSREQ },
+   { "CANCEL", k_input_device_keyboard, .key = SDLK_CANCEL },
+   { "CLEAR", k_input_device_keyboard, .key = SDLK_CLEAR },
+   { "PRIOR", k_input_device_keyboard, .key = SDLK_PRIOR },
+   { "RETURN2", k_input_device_keyboard, .key = SDLK_RETURN2 },
+   { "SEPARATOR", k_input_device_keyboard, .key = SDLK_SEPARATOR },
+   { "OUT", k_input_device_keyboard, .key = SDLK_OUT },
+   { "OPER", k_input_device_keyboard, .key = SDLK_OPER },
+   { "CLEARAGAIN", k_input_device_keyboard, .key = SDLK_CLEARAGAIN },
+   { "CRSEL", k_input_device_keyboard, .key = SDLK_CRSEL },
+   { "EXSEL", k_input_device_keyboard, .key = SDLK_EXSEL },
+   { "KP_00", k_input_device_keyboard, .key = SDLK_KP_00 },
+   { "KP_000", k_input_device_keyboard, .key = SDLK_KP_000 },
+   { "THOUSANDSSEPARATOR", k_input_device_keyboard, .key = SDLK_THOUSANDSSEPARATOR },
+   { "DECIMALSEPARATOR", k_input_device_keyboard, .key = SDLK_DECIMALSEPARATOR },
+   { "CURRENCYUNIT", k_input_device_keyboard, .key = SDLK_CURRENCYUNIT },
+   { "CURRENCYSUBUNIT", k_input_device_keyboard, .key = SDLK_CURRENCYSUBUNIT },
+   { "KP_LEFTPAREN", k_input_device_keyboard, .key = SDLK_KP_LEFTPAREN },
+   { "KP_RIGHTPAREN", k_input_device_keyboard, .key = SDLK_KP_RIGHTPAREN },
+   { "KP_LEFTBRACE", k_input_device_keyboard, .key = SDLK_KP_LEFTBRACE },
+   { "KP_RIGHTBRACE", k_input_device_keyboard, .key = SDLK_KP_RIGHTBRACE },
+   { "KP_TAB", k_input_device_keyboard, .key = SDLK_KP_TAB },
+   { "KP_BACKSPACE", k_input_device_keyboard, .key = SDLK_KP_BACKSPACE },
+   { "KP_A", k_input_device_keyboard, .key = SDLK_KP_A },
+   { "KP_B", k_input_device_keyboard, .key = SDLK_KP_B },
+   { "KP_C", k_input_device_keyboard, .key = SDLK_KP_C },
+   { "KP_D", k_input_device_keyboard, .key = SDLK_KP_D },
+   { "KP_E", k_input_device_keyboard, .key = SDLK_KP_E },
+   { "KP_F", k_input_device_keyboard, .key = SDLK_KP_F },
+   { "KP_XOR", k_input_device_keyboard, .key = SDLK_KP_XOR },
+   { "KP_POWER", k_input_device_keyboard, .key = SDLK_KP_POWER },
+   { "KP_PERCENT", k_input_device_keyboard, .key = SDLK_KP_PERCENT },
+   { "KP_LESS", k_input_device_keyboard, .key = SDLK_KP_LESS },
+   { "KP_GREATER", k_input_device_keyboard, .key = SDLK_KP_GREATER },
+   { "KP_AMPERSAND", k_input_device_keyboard, .key = SDLK_KP_AMPERSAND },
+   { "KP_DBLAMPERSAND", k_input_device_keyboard, .key = SDLK_KP_DBLAMPERSAND },
+   { "KP_VERTICALBAR", k_input_device_keyboard, .key = SDLK_KP_VERTICALBAR },
+   { "KP_DBLVERTICALBAR", k_input_device_keyboard, .key = SDLK_KP_DBLVERTICALBAR },
+   { "KP_COLON", k_input_device_keyboard, .key = SDLK_KP_COLON },
+   { "KP_HASH", k_input_device_keyboard, .key = SDLK_KP_HASH },
+   { "KP_SPACE", k_input_device_keyboard, .key = SDLK_KP_SPACE },
+   { "KP_AT", k_input_device_keyboard, .key = SDLK_KP_AT },
+   { "KP_EXCLAM", k_input_device_keyboard, .key = SDLK_KP_EXCLAM },
+   { "KP_MEMSTORE", k_input_device_keyboard, .key = SDLK_KP_MEMSTORE },
+   { "KP_MEMRECALL", k_input_device_keyboard, .key = SDLK_KP_MEMRECALL },
+   { "KP_MEMCLEAR", k_input_device_keyboard, .key = SDLK_KP_MEMCLEAR },
+   { "KP_MEMADD", k_input_device_keyboard, .key = SDLK_KP_MEMADD },
+   { "KP_MEMSUBTRACT", k_input_device_keyboard, .key = SDLK_KP_MEMSUBTRACT },
+   { "KP_MEMMULTIPLY", k_input_device_keyboard, .key = SDLK_KP_MEMMULTIPLY },
+   { "KP_MEMDIVIDE", k_input_device_keyboard, .key = SDLK_KP_MEMDIVIDE },
+   { "KP_PLUSMINUS", k_input_device_keyboard, .key = SDLK_KP_PLUSMINUS },
+   { "KP_CLEAR", k_input_device_keyboard, .key = SDLK_KP_CLEAR },
+   { "KP_CLEARENTRY", k_input_device_keyboard, .key = SDLK_KP_CLEARENTRY },
+   { "KP_BINARY", k_input_device_keyboard, .key = SDLK_KP_BINARY },
+   { "KP_OCTAL", k_input_device_keyboard, .key = SDLK_KP_OCTAL },
+   { "KP_DECIMAL", k_input_device_keyboard, .key = SDLK_KP_DECIMAL },
+   { "KP_HEXADECIMAL", k_input_device_keyboard, .key = SDLK_KP_HEXADECIMAL },
+   { "LCTRL", k_input_device_keyboard, .key = SDLK_LCTRL },
+   { "LSHIFT", k_input_device_keyboard, .key = SDLK_LSHIFT },
+   { "LALT", k_input_device_keyboard, .key = SDLK_LALT },
+   { "LGUI", k_input_device_keyboard, .key = SDLK_LGUI },
+   { "RCTRL", k_input_device_keyboard, .key = SDLK_RCTRL },
+   { "RSHIFT", k_input_device_keyboard, .key = SDLK_RSHIFT },
+   { "RALT", k_input_device_keyboard, .key = SDLK_RALT },
+   { "RGUI", k_input_device_keyboard, .key = SDLK_RGUI },
+   { "MODE", k_input_device_keyboard, .key = SDLK_MODE },
+   { "SLEEP", k_input_device_keyboard, .key = SDLK_SLEEP },
+   { "WAKE", k_input_device_keyboard, .key = SDLK_WAKE },
+   { "CHANNEL_INCREMENT", k_input_device_keyboard, .key = SDLK_CHANNEL_INCREMENT },
+   { "CHANNEL_DECREMENT", k_input_device_keyboard, .key = SDLK_CHANNEL_DECREMENT },
+   { "MEDIA_PLAY", k_input_device_keyboard, .key = SDLK_MEDIA_PLAY },
+   { "MEDIA_PAUSE", k_input_device_keyboard, .key = SDLK_MEDIA_PAUSE },
+   { "MEDIA_RECORD", k_input_device_keyboard, .key = SDLK_MEDIA_RECORD },
+   { "MEDIA_FAST_FORWARD", k_input_device_keyboard, .key = SDLK_MEDIA_FAST_FORWARD },
+   { "MEDIA_REWIND", k_input_device_keyboard, .key = SDLK_MEDIA_REWIND },
+   { "MEDIA_NEXT_TRACK", k_input_device_keyboard, .key = SDLK_MEDIA_NEXT_TRACK },
+   { "MEDIA_PREVIOUS_TRACK", k_input_device_keyboard, .key = SDLK_MEDIA_PREVIOUS_TRACK },
+   { "MEDIA_STOP", k_input_device_keyboard, .key = SDLK_MEDIA_STOP },
+   { "MEDIA_EJECT", k_input_device_keyboard, .key = SDLK_MEDIA_EJECT },
+   { "MEDIA_PLAY_PAUSE", k_input_device_keyboard, .key = SDLK_MEDIA_PLAY_PAUSE },
+   { "MEDIA_SELECT", k_input_device_keyboard, .key = SDLK_MEDIA_SELECT },
+   { "AC_NEW", k_input_device_keyboard, .key = SDLK_AC_NEW },
+   { "AC_OPEN", k_input_device_keyboard, .key = SDLK_AC_OPEN },
+   { "AC_CLOSE", k_input_device_keyboard, .key = SDLK_AC_CLOSE },
+   { "AC_EXIT", k_input_device_keyboard, .key = SDLK_AC_EXIT },
+   { "AC_SAVE", k_input_device_keyboard, .key = SDLK_AC_SAVE },
+   { "AC_PRINT", k_input_device_keyboard, .key = SDLK_AC_PRINT },
+   { "AC_PROPERTIES", k_input_device_keyboard, .key = SDLK_AC_PROPERTIES },
+   { "AC_SEARCH", k_input_device_keyboard, .key = SDLK_AC_SEARCH },
+   { "AC_HOME", k_input_device_keyboard, .key = SDLK_AC_HOME },
+   { "AC_BACK", k_input_device_keyboard, .key = SDLK_AC_BACK },
+   { "AC_FORWARD", k_input_device_keyboard, .key = SDLK_AC_FORWARD },
+   { "AC_STOP", k_input_device_keyboard, .key = SDLK_AC_STOP },
+   { "AC_REFRESH", k_input_device_keyboard, .key = SDLK_AC_REFRESH },
+   { "AC_BOOKMARKS", k_input_device_keyboard, .key = SDLK_AC_BOOKMARKS },
+   { "SOFTLEFT", k_input_device_keyboard, .key = SDLK_SOFTLEFT },
+   { "SOFTRIGHT", k_input_device_keyboard, .key = SDLK_SOFTRIGHT },
+   { "CALL", k_input_device_keyboard, .key = SDLK_CALL },
+   { "ENDCALL", k_input_device_keyboard, .key = SDLK_ENDCALL },
+   { "LEFT_TAB", k_input_device_keyboard, .key = SDLK_LEFT_TAB },
+   { "LEVEL5_SHIFT", k_input_device_keyboard, .key = SDLK_LEVEL5_SHIFT },
+   { "MULTI_KEY_COMPOSE", k_input_device_keyboard, .key = SDLK_MULTI_KEY_COMPOSE },
+   { "LMETA", k_input_device_keyboard, .key = SDLK_LMETA },
+   { "RMETA", k_input_device_keyboard, .key = SDLK_RMETA },
+   { "LHYPER", k_input_device_keyboard, .key = SDLK_LHYPER },
+   { "RHYPER", k_input_device_keyboard, .key = SDLK_RHYPER },
+
+#if 0
+   { "MOUSE_LEFT", k_input_device_mouse, .key = GLFW_MOUSE_BUTTON_1 },
+   { "MOUSE_RIGHT", k_input_device_mouse, .key = GLFW_MOUSE_BUTTON_2 },
+   { "MOUSE_MIDDLE", k_input_device_mouse, .key = GLFW_MOUSE_BUTTON_3 },
+   { "SPACE", k_input_device_keyboard, .key = GLFW_KEY_SPACE },
+   { "APOSTROPHE", k_input_device_keyboard, .key = GLFW_KEY_APOSTROPHE },
+   { "COMMA", k_input_device_keyboard, .key = GLFW_KEY_COMMA },
+   { "MINUS", k_input_device_keyboard, .key = GLFW_KEY_MINUS },
+   { "PERIOD", k_input_device_keyboard, .key = GLFW_KEY_PERIOD },
+   { "SLASH", k_input_device_keyboard, .key = GLFW_KEY_SLASH },
+   { "0", k_input_device_keyboard, .key = GLFW_KEY_0 },
+   { "1", k_input_device_keyboard, .key = GLFW_KEY_1 },
+   { "2", k_input_device_keyboard, .key = GLFW_KEY_2 },
+   { "3", k_input_device_keyboard, .key = GLFW_KEY_3 },
+   { "4", k_input_device_keyboard, .key = GLFW_KEY_4 },
+   { "5", k_input_device_keyboard, .key = GLFW_KEY_5 },
+   { "6", k_input_device_keyboard, .key = GLFW_KEY_6 },
+   { "7", k_input_device_keyboard, .key = GLFW_KEY_7 },
+   { "8", k_input_device_keyboard, .key = GLFW_KEY_8 },
+   { "9", k_input_device_keyboard, .key = GLFW_KEY_9 },
+   { "SEMICOLON", k_input_device_keyboard, .key = GLFW_KEY_SEMICOLON },
+   { "EQUAL", k_input_device_keyboard, .key = GLFW_KEY_EQUAL },
+   { "A", k_input_device_keyboard, .key = GLFW_KEY_A },
+   { "B", k_input_device_keyboard, .key = GLFW_KEY_B },
+   { "C", k_input_device_keyboard, .key = GLFW_KEY_C },
+   { "D", k_input_device_keyboard, .key = GLFW_KEY_D },
+   { "E", k_input_device_keyboard, .key = GLFW_KEY_E },
+   { "F", k_input_device_keyboard, .key = GLFW_KEY_F },
+   { "G", k_input_device_keyboard, .key = GLFW_KEY_G },
+   { "H", k_input_device_keyboard, .key = GLFW_KEY_H },
+   { "I", k_input_device_keyboard, .key = GLFW_KEY_I },
+   { "J", k_input_device_keyboard, .key = GLFW_KEY_J },
+   { "K", k_input_device_keyboard, .key = GLFW_KEY_K },
+   { "L", k_input_device_keyboard, .key = GLFW_KEY_L },
+   { "M", k_input_device_keyboard, .key = GLFW_KEY_M },
+   { "N", k_input_device_keyboard, .key = GLFW_KEY_N },
+   { "O", k_input_device_keyboard, .key = GLFW_KEY_O },
+   { "P", k_input_device_keyboard, .key = GLFW_KEY_P },
+   { "Q", k_input_device_keyboard, .key = GLFW_KEY_Q },
+   { "R", k_input_device_keyboard, .key = GLFW_KEY_R },
+   { "S", k_input_device_keyboard, .key = GLFW_KEY_S },
+   { "T", k_input_device_keyboard, .key = GLFW_KEY_T },
+   { "U", k_input_device_keyboard, .key = GLFW_KEY_U },
+   { "V", k_input_device_keyboard, .key = GLFW_KEY_V },
+   { "W", k_input_device_keyboard, .key = GLFW_KEY_W },
+   { "X", k_input_device_keyboard, .key = GLFW_KEY_X },
+   { "Y", k_input_device_keyboard, .key = GLFW_KEY_Y },
+   { "Z", k_input_device_keyboard, .key = GLFW_KEY_Z },
+   { "LEFT_BRACKET", k_input_device_keyboard, .key = GLFW_KEY_LEFT_BRACKET },
+   { "BACKSLASH", k_input_device_keyboard, .key = GLFW_KEY_BACKSLASH },
+   { "RIGHT_BRACKET", k_input_device_keyboard, .key = GLFW_KEY_RIGHT_BRACKET },
+   { "GRAVE_ACCENT", k_input_device_keyboard, .key = GLFW_KEY_GRAVE_ACCENT },
+   { "WORLD_1", k_input_device_keyboard, .key = GLFW_KEY_WORLD_1 },
+   { "WORLD_2", k_input_device_keyboard, .key = GLFW_KEY_WORLD_2 },
+   { "ESCAPE", k_input_device_keyboard, .key = GLFW_KEY_ESCAPE },
+   { "ENTER", k_input_device_keyboard, .key = GLFW_KEY_ENTER },
+   { "TAB", k_input_device_keyboard, .key = GLFW_KEY_TAB },
+   { "BACKSPACE", k_input_device_keyboard, .key = GLFW_KEY_BACKSPACE },
+   { "INSERT", k_input_device_keyboard, .key = GLFW_KEY_INSERT },
+   { "DELETE", k_input_device_keyboard, .key = GLFW_KEY_DELETE },
+   { "RIGHT", k_input_device_keyboard, .key = GLFW_KEY_RIGHT },
+   { "LEFT", k_input_device_keyboard, .key = GLFW_KEY_LEFT },
+   { "DOWN", k_input_device_keyboard, .key = GLFW_KEY_DOWN },
+   { "UP", k_input_device_keyboard, .key = GLFW_KEY_UP },
+   { "PAGE_UP", k_input_device_keyboard, .key = GLFW_KEY_PAGE_UP },
+   { "PAGE_DOWN", k_input_device_keyboard, .key = GLFW_KEY_PAGE_DOWN },
+   { "HOME", k_input_device_keyboard, .key = GLFW_KEY_HOME },
+   { "END", k_input_device_keyboard, .key = GLFW_KEY_END },
+   { "CAPS_LOCK", k_input_device_keyboard, .key = GLFW_KEY_CAPS_LOCK },
+   { "SCROLL_LOCK", k_input_device_keyboard, .key = GLFW_KEY_SCROLL_LOCK },
+   { "NUM_LOCK", k_input_device_keyboard, .key = GLFW_KEY_NUM_LOCK },
+   { "PRINT_SCREEN", k_input_device_keyboard, .key = GLFW_KEY_PRINT_SCREEN },
+   { "PAUSE", k_input_device_keyboard, .key = GLFW_KEY_PAUSE },
+   { "F1", k_input_device_keyboard, .key = GLFW_KEY_F1 },
+   { "F2", k_input_device_keyboard, .key = GLFW_KEY_F2 },
+   { "F3", k_input_device_keyboard, .key = GLFW_KEY_F3 },
+   { "F4", k_input_device_keyboard, .key = GLFW_KEY_F4 },
+   { "F5", k_input_device_keyboard, .key = GLFW_KEY_F5 },
+   { "F6", k_input_device_keyboard, .key = GLFW_KEY_F6 },
+   { "F7", k_input_device_keyboard, .key = GLFW_KEY_F7 },
+   { "F8", k_input_device_keyboard, .key = GLFW_KEY_F8 },
+   { "F9", k_input_device_keyboard, .key = GLFW_KEY_F9 },
+   { "F10", k_input_device_keyboard, .key = GLFW_KEY_F10 },
+   { "F11", k_input_device_keyboard, .key = GLFW_KEY_F11 },
+   { "F12", k_input_device_keyboard, .key = GLFW_KEY_F12 },
+   { "F13", k_input_device_keyboard, .key = GLFW_KEY_F13 },
+   { "F14", k_input_device_keyboard, .key = GLFW_KEY_F14 },
+   { "F15", k_input_device_keyboard, .key = GLFW_KEY_F15 },
+   { "F16", k_input_device_keyboard, .key = GLFW_KEY_F16 },
+   { "F17", k_input_device_keyboard, .key = GLFW_KEY_F17 },
+   { "F18", k_input_device_keyboard, .key = GLFW_KEY_F18 },
+   { "F19", k_input_device_keyboard, .key = GLFW_KEY_F19 },
+   { "F20", k_input_device_keyboard, .key = GLFW_KEY_F20 },
+   { "F21", k_input_device_keyboard, .key = GLFW_KEY_F21 },
+   { "F22", k_input_device_keyboard, .key = GLFW_KEY_F22 },
+   { "F23", k_input_device_keyboard, .key = GLFW_KEY_F23 },
+   { "F24", k_input_device_keyboard, .key = GLFW_KEY_F24 },
+   { "F25", k_input_device_keyboard, .key = GLFW_KEY_F25 },
+   { "KP_0", k_input_device_keyboard, .key = GLFW_KEY_KP_0 },
+   { "KP_1", k_input_device_keyboard, .key = GLFW_KEY_KP_1 },
+   { "KP_2", k_input_device_keyboard, .key = GLFW_KEY_KP_2 },
+   { "KP_3", k_input_device_keyboard, .key = GLFW_KEY_KP_3 },
+   { "KP_4", k_input_device_keyboard, .key = GLFW_KEY_KP_4 },
+   { "KP_5", k_input_device_keyboard, .key = GLFW_KEY_KP_5 },
+   { "KP_6", k_input_device_keyboard, .key = GLFW_KEY_KP_6 },
+   { "KP_7", k_input_device_keyboard, .key = GLFW_KEY_KP_7 },
+   { "KP_8", k_input_device_keyboard, .key = GLFW_KEY_KP_8 },
+   { "KP_9", k_input_device_keyboard, .key = GLFW_KEY_KP_9 },
+   { "KP_DECIMAL", k_input_device_keyboard, .key = GLFW_KEY_KP_DECIMAL },
+   { "KP_DIVIDE", k_input_device_keyboard, .key = GLFW_KEY_KP_DIVIDE },
+   { "KP_MULTIPLY", k_input_device_keyboard, .key = GLFW_KEY_KP_MULTIPLY },
+   { "KP_SUBTRACT", k_input_device_keyboard, .key = GLFW_KEY_KP_SUBTRACT },
+   { "KP_ADD", k_input_device_keyboard, .key = GLFW_KEY_KP_ADD },
+   { "KP_ENTER", k_input_device_keyboard, .key = GLFW_KEY_KP_ENTER },
+   { "KP_EQUAL", k_input_device_keyboard, .key = GLFW_KEY_KP_EQUAL },
+   { "LEFT_SHIFT", k_input_device_keyboard, .key = GLFW_KEY_LEFT_SHIFT },
+   { "LEFT_CONTROL", k_input_device_keyboard, .key = GLFW_KEY_LEFT_CONTROL },
+   { "LEFT_ALT", k_input_device_keyboard, .key = GLFW_KEY_LEFT_ALT },
+   { "LEFT_SUPER", k_input_device_keyboard, .key = GLFW_KEY_LEFT_SUPER },
+   { "RIGHT_SHIFT", k_input_device_keyboard, .key = GLFW_KEY_RIGHT_SHIFT },
+   { "RIGHT_CONTROL", k_input_device_keyboard, .key = GLFW_KEY_RIGHT_CONTROL },
+   { "RIGHT_ALT", k_input_device_keyboard, .key = GLFW_KEY_RIGHT_ALT },
+   { "RIGHT_SUPER", k_input_device_keyboard, .key = GLFW_KEY_RIGHT_SUPER },
+   { "MENU", k_input_device_keyboard, .key = GLFW_KEY_MENU },
+   { "SHIFT", k_input_device_modifier, .key = GLFW_MOD_SHIFT },
+   { "CONTROL", k_input_device_modifier, .key = GLFW_MOD_CONTROL },
+   { "ALT", k_input_device_modifier, .key = GLFW_MOD_ALT },
+   { "SUPER", k_input_device_modifier, .key = GLFW_MOD_SUPER },
+#endif
+};
+
+static struct input_alias *_input_alias_find( const c8 *alias, u32 alias_length )
+{
+   u32 hash = buffer_djb2( alias, alias_length );
+   for( u32 i=0; i<ARRAY_COUNT( _input_aliases ); i ++ )
+   {
+      struct input_alias *alias_i = &_input_aliases[i];
+      if( alias_i->alias_hash == hash )
+         if( compare_buffers( alias_i->alias, 0, alias, alias_length ) )
+            return alias_i;
+   }
+   return NULL;
+}
+
+static i32 _input_page = 0;
+
+union input_state
+{
+   struct
+   {
+      u8 hold_count, activation_count, release_count, repeat_count;
+   }
+   button;
+
+   struct
+   {
+      f32 value;
+   }
+   axis;
+}
+static _input_states[2][k_input_count];
+
+struct bind
+{
+   u16 device, input_index;
+   u32 id;
+   u32 modifiers;
+};
+struct stretchy_allocator _bind_allocator;
+
+i32 _input_bind_ccmd( struct console_arguments *args )
+{
+   const c8 *buttons = console_get_argument( args, 0 );
+   if( !buttons )
+   {
+      $log( $error, {"Usage: bind <button> <action>"} );
+      return -1;
+   }
+
+   const c8 *input_name = console_get_argument( args, 1 );
+   if( !input_name )
+   {
+      $log( $error, {"Usage: bind <button> <action>"} );
+      return -1;
+   }
+
+   i32 input_id = -1;
+   for( u32 i=0; i<k_input_count; i ++ )
+   {
+      struct input_info *input = &_input_infos[ i ];
+      if( compare_buffers( input->name, 0, input_name, 0 ) )
+      {
+         input_id = i;
+         break;
+      }
+   }
+
+   if( input_id == -1 )
+   {
+      $log( $error, {"Don't know an input called '"}, {input_name}, {"'"} );
+      return -1;
+   }
+
+   struct bind new_bind = { .input_index = input_id };
+   while(1)
+   {
+      i32 plus_index = buffer_first_index( buttons, '+', 0 ),
+          length = plus_index == -1? 0: plus_index;
+
+      struct input_alias *alias = _input_alias_find( buttons, length );
+      if( alias )
+      {
+         if( (alias->device_type == k_input_device_keyboard) || (alias->device_type == k_input_device_mouse) )
+         {
+            if( new_bind.device )
+            {
+               $log( $error, {"Cannot specify more than one normal buttons in bind"} );
+               return -1;
+            }
+            new_bind.id = alias->key;
+            new_bind.device = alias->device_type;
+         }
+         else if( alias->device_type == k_input_device_modifier )
+         {
+            if( new_bind.device )
+            {
+               $log( $error, {"Modifiers must come first"} );
+               return -1;
+            }
+            new_bind.modifiers |= alias->key;
+         }
+         else
+         {
+            // TODO
+            $log( $error, {"Currently dont support device type'"}, $unsigned(alias->device_type), {"'"} );
+            return -1;
+         }
+      }
+      else
+      {
+         $log( $error, {"Don't know what a '"}, $string( buttons, .length = length ), {"' is"} );
+         return -1;
+      }
+
+      if( plus_index == -1 )
+         break;
+      else
+         buttons += plus_index+1;
+   }
+
+   if( (new_bind.device == k_input_device_keyboard) && !new_bind.id )
+   {
+      $log( $error, {"No key specified for bind"} );
+      if( new_bind.modifiers )
+         $log( $info, {"If you're trying to bind SHIFT to a normal button, use LEFT_SHIFT or RIGHT_ALT etc."} );
+      return -1;
+   }
+
+   if( !stretchy_count( &_bind_allocator ) )
+      stretchy_init( &_bind_allocator, sizeof(struct bind ) );
+   struct bind *a = stretchy_append( &_bind_allocator );
+   *a = new_bind;
+   return 0;
+}
+
+i32 _input_unbind_ccmd( struct console_arguments *args )
+{
+   return -1;
+}
+
+enum button_action
+{
+   k_button_down,
+   k_button_os_repeat,
+   k_button_up
+};
+
+static void _input_callback_buttonlike( enum input_device device, i32 id, enum button_action action, u32 mods )
+{
+   for( u32 i=0; i<stretchy_count( &_bind_allocator ); i ++ )
+   {
+      struct bind *bind = stretchy_get( &_bind_allocator, i );
+      if( bind->device == device )
+      {
+         if( bind->id == id )
+         {
+            struct input_info *input = &_input_infos[ bind->input_index ];
+            union input_state *state = &_input_states[ _input_page^0x1 ][ bind->input_index ];
+
+            if( (input->type == k_input_type_action) || (input->type == k_input_type_button) )
+            {
+               b8 allow_activation = 1;
+               if( input->type == k_input_type_action )
+                  if( bind->modifiers != mods )
+                     allow_activation = 0;
+
+               if( allow_activation && (action == k_button_down) )
+               {
+                  ASSERT_CRITICAL( state->button.activation_count < 255 );
+                  state->button.activation_count ++;
+
+                  ASSERT_CRITICAL( state->button.hold_count < 255 );
+                  state->button.hold_count ++;
+               }
+
+               if( state->button.hold_count && allow_activation && (action == k_button_os_repeat || action == k_button_down) )
+               {
+                  ASSERT_CRITICAL( state->button.repeat_count < 255 );
+                  state->button.repeat_count ++;
+               }
+               
+               if( action == k_button_up )
+               {
+                  if( state->button.hold_count )
+                  {
+                     state->button.hold_count --;
+                     state->button.release_count ++;
+                  }
+               }
+            }
+            else if( input->type == k_input_type_axis )
+            {
+               ASSERT_CRITICAL( 0 );
+            }
+         }
+      }
+   }
+}
+
+void _input_keyboard_event( SDL_KeyboardEvent *ev )
+{
+   enum button_action action;
+   if( ev->type == SDL_EVENT_KEY_DOWN )
+   {
+      if( ev->repeat )
+         action = k_button_os_repeat;
+      else
+         action = k_button_down;
+   }
+   else action = k_button_up;
+   _input_callback_buttonlike( k_input_device_keyboard, ev->key, action, ev->mod );
+}
+
+void _input_init(void)
+{
+#if 0
+   glfwSetKeyCallback( _engine.window_handle, _input_key_callback );
+   glfwSetMouseButtonCallback( _engine.window_handle, _input_mouse_callback );
+#endif
+   for( u32 i=0; i<ARRAY_COUNT( _input_aliases ); i ++ )
+      _input_aliases[i].alias_hash = buffer_djb2( _input_aliases[i].alias, 0 );
+}
+
+void _input_update(void)
+{
+   /* flip to read page and reset the reciever one */
+   _input_page ^= 0x1;
+   for( u32 i=0; i<k_input_count; i ++ )
+   {
+      struct input_info *input = &_input_infos[ i ];
+      union input_state *back_state = &_input_states[ _input_page^0x1 ][ i ],
+                        *state = &_input_states[ _input_page ][ i ];
+
+      if( (input->type == k_input_type_action) || (input->type == k_input_type_button) )
+      {
+         back_state->button.activation_count = 0;
+         back_state->button.repeat_count = 0;
+         back_state->button.release_count = 0;
+         back_state->button.hold_count = state->button.hold_count;
+      }
+   }
+}
+
+static union input_state *_get_input_state( enum input_id id )
+{
+   return &_input_states[ _input_page ][ id ];
+}
+
+static u32 _whitelist = 0x00;
+
+
+b8 _input_layer_filter( u32 mask )
+{
+   return (_whitelist & mask) || !_whitelist;
+}
+
+b8 _filter_input( enum input_id id )
+{
+   return _input_layer_filter( _input_infos[ id ].layer_mask );
+}
+
+u8 _input_button_down( enum input_id id, b8 allow_repeats )
+{
+   if( _filter_input(id) )
+   {
+      union input_state *state = _get_input_state( id );
+      return allow_repeats? state->button.repeat_count: state->button.activation_count;
+   }
+   else return 0;
+}
+
+u8 _input_button_up( enum input_id id )
+{
+   return _filter_input(id) && _get_input_state( id )->button.release_count;
+}
+
+u8 _input_button( enum input_id id )
+{
+   return _filter_input(id) && _get_input_state( id )->button.hold_count;
+}
+
+void _input_layer_whitelist( u32 whitelist )
+{
+   _whitelist = whitelist; 
+}
+
+void _input_string( struct stream *stream, enum input_id id )
+{
+   u32 matched = 0;
+   
+   for( u32 i=0; i<stretchy_count( &_bind_allocator ); i ++ )
+   {
+      struct bind *bind = stretchy_get( &_bind_allocator, i );
+      if( bind->input_index == id )
+      {
+         if( matched )
+            $v_string( stream, {" or "} );
+
+         matched ++;
+
+         for( u32 j=0; j<ARRAY_COUNT( _input_aliases ); j ++ )
+         {
+            struct input_alias *alias_j = &_input_aliases[j];
+            if( (alias_j->device_type == k_input_device_modifier) && (bind->modifiers & alias_j->key) )
+            {
+               $v_string( stream, {alias_j->alias}, {"+"} );
+            }
+         }
+         for( u32 j=0; j<ARRAY_COUNT( _input_aliases ); j ++ )
+         {
+            struct input_alias *alias_j = &_input_aliases[j];
+            if( (alias_j->device_type == bind->device) && (alias_j->key == bind->id) )
+            {
+               $v_string( stream, {alias_j->alias} );
+            }
+         }
+      }
+   }
+}
diff --git a/source/engine/vg_input.h b/source/engine/vg_input.h
new file mode 100644 (file)
index 0000000..1e058a4
--- /dev/null
@@ -0,0 +1,38 @@
+#include "SDL3/SDL.h"
+
+enum input_type
+{
+   k_input_type_action,
+   k_input_type_button,
+   k_input_type_axis
+};
+
+enum input_device
+{
+   k_input_device_none = 0,
+   k_input_device_keyboard,
+   k_input_device_controller,
+   k_input_device_mouse,
+   k_input_device_modifier,
+};
+
+struct input_info
+{
+   const c8 *name;
+   enum input_type type;
+   u32 layer_mask;
+};
+
+enum input_id;
+
+void _input_keyboard_event( SDL_KeyboardEvent *ev );
+
+u8 _input_button_down( enum input_id id, b8 allow_repeats );
+u8 _input_button_up( enum input_id id );
+u8 _input_button( enum input_id id );
+
+void _input_layer_whitelist( u32 whitelist );
+b8 _input_layer_filter( u32 mask );
+void _input_string( struct stream *stream, enum input_id id );
+
+#include "generated/input.h"
diff --git a/source/engine/vg_lines.c b/source/engine/vg_lines.c
new file mode 100644 (file)
index 0000000..ec9d611
--- /dev/null
@@ -0,0 +1,290 @@
+#include "vg_opengl.h"
+#include "foundation.h"
+#include "common_maths.h"
+#include "vg_async.h"
+#include "vg_shader.h"
+#include <stddef.h>
+
+struct vg_lines
+{
+   struct stack_allocator vertex_stack;
+   struct vg_lines_vert
+   {
+      f32 co[3];
+      u32 colour;
+   } 
+   *vertex_buffer;
+   u32 vertex_count;
+
+       GLuint vao, vbo;
+}
+static _vg_lines;
+
+#define VG_LINES_MAX_VERTS 50000
+
+void _vg_lines_init(void)
+{
+   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_OPENGL ) );
+
+   _vg_lines.vertex_buffer = _heap_allocate( VG_LINES_MAX_VERTS*sizeof(struct vg_lines_vert) );
+   glGenVertexArrays( 1, &_vg_lines.vao );
+   glGenBuffers( 1, &_vg_lines.vbo );
+   glBindVertexArray( _vg_lines.vao );
+   glBindBuffer( GL_ARRAY_BUFFER, _vg_lines.vbo );
+   glBufferData( GL_ARRAY_BUFFER, VG_LINES_MAX_VERTS*sizeof(struct vg_lines_vert), NULL, GL_DYNAMIC_DRAW );
+   glBindVertexArray( _vg_lines.vao );
+
+   /* Pointers */
+   glVertexAttribPointer( 
+      0, 
+      3,
+      GL_FLOAT, 
+      GL_FALSE, 
+      sizeof( struct vg_lines_vert ), 
+      (void *)0 
+   );
+   glEnableVertexAttribArray( 0 );
+   
+   glVertexAttribPointer( 
+      1, 
+      4, 
+      GL_UNSIGNED_BYTE, 
+      GL_TRUE, 
+      sizeof( struct vg_lines_vert ), 
+      (void*)(offsetof( struct vg_lines_vert, colour ))
+   );
+   glEnableVertexAttribArray( 1 );
+}
+
+void vg_lines_draw( f32 pv[4][4] )
+{
+   _shader_bind( k_shader_debug_lines );
+   _shader_debug_lines_uPv( pv );
+
+       glBindVertexArray( _vg_lines.vao );
+       glBindBuffer( GL_ARRAY_BUFFER, _vg_lines.vbo );
+       glBufferSubData( GL_ARRAY_BUFFER, 0, _vg_lines.vertex_count*sizeof(struct vg_lines_vert), _vg_lines.vertex_buffer );
+
+       glEnable( GL_BLEND );
+       glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+       glBlendEquation( GL_FUNC_ADD );
+   glDrawArrays( GL_LINES, 0, _vg_lines.vertex_count );
+       glDisable( GL_BLEND );
+}
+
+void vg_lines_clear(void)
+{
+   _vg_lines.vertex_count = 0;
+}
+
+void vg_line2( f32 from[3], f32 to[3], u32 fc, u32 tc )
+{
+   if( _vg_lines.vertex_count < VG_LINES_MAX_VERTS )
+   {
+      struct vg_lines_vert *v = &_vg_lines.vertex_buffer[ _vg_lines.vertex_count ];
+
+      v3_copy( from, v[0].co );
+      v3_copy( to, v[1].co );
+
+      v[0].colour = fc;
+      v[1].colour = tc;
+
+      _vg_lines.vertex_count += 2;
+   }
+}
+
+void vg_line( f32 from[3], f32 to[3], u32 colour )
+{
+       vg_line2( from, to, colour, colour );
+}
+
+void vg_line_arrow( f32 co[3], f32 dir[3], f32 size, u32 colour )
+{
+   f32 p1[3], tx[3], ty[3], p2[3], p3[3];
+   v3_muladds( co, dir, size, p1 );
+   v3_tangent_basis( dir, tx, ty );
+
+   v3_muladds( p1, dir, -size * 0.125f, p2 );
+   v3_muladds( p2, ty,  size * 0.125f, p3 );
+   v3_muladds( p2, ty, -size * 0.125f, p2 );
+
+   vg_line( co, p1, colour );
+   vg_line( p1, p2, colour );
+   vg_line( p1, p3, colour );
+}
+
+void vg_line_box_verts( f32 box[2][3], f32 verts[8][3] )
+{
+   for( u32 i=0; i<8; i++ )
+      for( u32 j=0; j<3; j++ )
+         verts[i][j] = i&(0x1<<j)? box[1][j]: box[0][j];
+}
+
+void vg_line_mesh( f32 verts[][3], u32 indices[][2], u32 indice_count, u32 colour )
+{
+   for( u32 i=0; i<indice_count; i++ )
+      vg_line( verts[indices[i][0]], verts[indices[i][1]], colour );
+}
+
+void vg_line_boxf( f32 box[2][3], u32 colour )
+{
+   f32 verts[8][3];
+   vg_line_box_verts( box, verts );
+   u32 indices[][2] = {{0,1},{1,3},{3,2},{2,0},
+                       {4,5},{5,7},{7,6},{6,4},
+                       {4,0},{5,1},{6,2},{7,3}};
+
+   vg_line_mesh( verts, indices, ARRAY_COUNT(indices), colour );
+}
+
+void vg_line_boxf_transformed( f32 m[4][3], f32 box[2][3], u32 colour )
+{
+   f32 verts[8][3];
+   vg_line_box_verts( box, verts );
+
+   for( u32 i=0; i<8; i++ )
+      m4x3_mulv( m, verts[i], verts[i] );
+
+   u32 indices[][2] = {{0,1},{1,3},{3,2},{2,0},
+                       {4,5},{5,7},{7,6},{6,4},
+                       {4,0},{5,1},{6,2},{7,3}};
+   vg_line_mesh( verts, indices, ARRAY_COUNT(indices), colour );
+}
+
+void vg_line_cross( f32 pos[3], u32 colour, f32 scale )
+{
+   f32 p0[3], p1[3];
+   v3_add( (f32[]){ scale,0.0f,0.0f}, pos, p0 );
+   v3_add( (f32[]){-scale,0.0f,0.0f}, pos, p1 );
+   vg_line( p0, p1, colour );
+   v3_add( (f32[]){0.0f, scale,0.0f}, pos, p0 );
+   v3_add( (f32[]){0.0f,-scale,0.0f}, pos, p1 );
+   vg_line( p0, p1, colour );
+   v3_add( (f32[]){0.0f,0.0f, scale}, pos, p0 );
+   v3_add( (f32[]){0.0f,0.0f,-scale}, pos, p1 );
+   vg_line( p0, p1, colour );
+}
+
+void vg_line_point( f32 pt[3], f32 size, u32 colour )
+{
+   f32 box[][3] =
+   {
+      { pt[0]-size, pt[1]-size, pt[2]-size },
+      { pt[0]+size, pt[1]+size, pt[2]+size }
+   };
+
+   vg_line_boxf( box, colour );
+}
+
+
+void vg_line_sphere( f32 m[4][3], f32 radius, u32 colour )
+{
+   f32 ly[3] = { 0.0f, 0.0f, radius },
+       lx[3] = { 0.0f, radius, 0.0f },
+       lz[3] = { 0.0f, 0.0f, radius };
+   
+   for( int i=0; i<16; i++ )
+   {
+      f32 t = ((f32)(i+1) * (1.0f/16.0f)) * VG_PIf * 2.0f,
+          s = sinf(t),
+          c = cosf(t);
+
+      f32 py[3] = { s*radius, 0.0f, c*radius },
+          px[3] = { s*radius, c*radius, 0.0f },
+          pz[3] = { 0.0f, s*radius, c*radius };
+
+      f32 p0[3], p1[3], p2[3], p3[3], p4[3], p5[3];
+      m4x3_mulv( m, py, p0 );
+      m4x3_mulv( m, ly, p1 );
+      m4x3_mulv( m, px, p2 );
+      m4x3_mulv( m, lx, p3 );
+      m4x3_mulv( m, pz, p4 );
+      m4x3_mulv( m, lz, p5 );
+
+      vg_line( p0, p1, colour == 0x00? 0xff00ff00: colour );
+      vg_line( p2, p3, colour == 0x00? 0xff0000ff: colour );
+      vg_line( p4, p5, colour == 0x00? 0xffff0000: colour );
+
+      v3_copy( py, ly );
+      v3_copy( px, lx );
+      v3_copy( pz, lz );
+   }
+}
+
+void vg_line_capsule( f32 m[4][3], f32 radius, f32 h, u32 colour )
+{
+   f32 s0 = sinf(0.0f)*radius,
+       c0 = cosf(0.0f)*radius;
+
+   f32 p0[3], p1[3], up[3], right[3], forward[3];
+   m3x3_mulv( m, (f32[]){0.0f,1.0f,0.0f}, up );
+   m3x3_mulv( m, (f32[]){1.0f,0.0f,0.0f}, right );
+   m3x3_mulv( m, (f32[]){0.0f,0.0f,-1.0f}, forward );
+   v3_muladds( m[3], up, -h*0.5f+radius, p0 );
+   v3_muladds( m[3], up,  h*0.5f-radius, p1 );
+
+   f32 a0[3], a1[3], b0[3], b1[3];
+   v3_muladds( p0, right, radius, a0 );
+   v3_muladds( p1, right, radius, a1 );
+   v3_muladds( p0, forward, radius, b0 );
+   v3_muladds( p1, forward, radius, b1 );
+   vg_line( a0, a1, colour );
+   vg_line( b0, b1, colour );
+
+   v3_muladds( p0, right, -radius, a0 );
+   v3_muladds( p1, right, -radius, a1 );
+   v3_muladds( p0, forward, -radius, b0 );
+   v3_muladds( p1, forward, -radius, b1 );
+   vg_line( a0, a1, colour );
+   vg_line( b0, b1, colour );
+   
+   for( i32 i=0; i<16; i++ )
+   {
+      f32 t = ((f32)(i+1) * (1.0f/16.0f)) * VG_PIf * 2.0f,
+          s1 = sinf(t)*radius,
+          c1 = cosf(t)*radius;
+
+      f32 e0[3] = { s0, 0.0f, c0 },
+          e1[3] = { s1, 0.0f, c1 },
+          e2[3] = { s0, c0, 0.0f },
+          e3[3] = { s1, c1, 0.0f },
+          e4[3] = { 0.0f, c0, s0 },
+          e5[3] = { 0.0f, c1, s1 };
+
+      m3x3_mulv( m, e0, e0 );
+      m3x3_mulv( m, e1, e1 );
+      m3x3_mulv( m, e2, e2 );
+      m3x3_mulv( m, e3, e3 );
+      m3x3_mulv( m, e4, e4 );
+      m3x3_mulv( m, e5, e5 );
+
+      v3_add( p0, e0, a0 );
+      v3_add( p0, e1, a1 );
+      v3_add( p1, e0, b0 );
+      v3_add( p1, e1, b1 );
+
+      vg_line( a0, a1, colour );
+      vg_line( b0, b1, colour );
+
+      if( c0 < 0.0f )
+      {
+         v3_add( p0, e2, a0 );
+         v3_add( p0, e3, a1 );
+         v3_add( p0, e4, b0 );
+         v3_add( p0, e5, b1 );
+      }
+      else
+      {
+         v3_add( p1, e2, a0 );
+         v3_add( p1, e3, a1 );
+         v3_add( p1, e4, b0 );
+         v3_add( p1, e5, b1 );
+      }
+
+      vg_line( a0, a1, colour );
+      vg_line( b0, b1, colour );
+
+      s0 = s1;
+      c0 = c1;
+   }
+}
diff --git a/source/engine/vg_lines.h b/source/engine/vg_lines.h
new file mode 100644 (file)
index 0000000..62fb781
--- /dev/null
@@ -0,0 +1,25 @@
+#define LINE_RED   0xff0000ff
+#define LINE_GREEN 0xff00ff00
+#define LINE_BLUE  0xffff0000
+#define LINE_WHITE 0xffffffff
+#define LINE_BLACK 0xff000000
+#define LINE_CLEAR 0x00ffffff
+#define LINE_PINK  0xffff00ff
+#define LINE_YELOW 0xff00ffff
+#define LINE_CYAN  0xffffff00
+#define LINE_NONE  0x00000000
+
+void vg_lines_clear(void);
+void vg_lines_draw( f32 pv[4][4] );
+
+void vg_line_capsule( f32 m[4][3], f32 radius, f32 h, u32 colour );
+void vg_line_sphere( f32 m[4][3], f32 radius, u32 colour );
+void vg_line_point( f32 pt[3], f32 size, u32 colour );
+void vg_line_boxf_transformed( f32 m[4][3], f32 box[2][3], u32 colour );
+void vg_line_boxf( f32 box[2][3], u32 colour );
+void vg_line_mesh( f32 verts[][3], u32 indices[][2], u32 indice_count, u32 colour );
+void vg_line_box_verts( f32 box[2][3], f32 verts[8][3] );
+void vg_line_cross( f32 pos[3], u32 colour, f32 scale );
+void vg_line_arrow( f32 co[3], f32 dir[3], f32 size, u32 colour );
+void vg_line( f32 from[3], f32 to[3], u32 colour );
+void vg_line2( f32 from[3], f32 to[3], u32 fc, u32 tc );
diff --git a/source/engine/vg_opengl.h b/source/engine/vg_opengl.h
new file mode 100644 (file)
index 0000000..09a79e4
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+#include "SDL3/SDL.h"
+#include "vg/dep/glad.4.3/glad/glad.h"
diff --git a/source/engine/vg_render.c b/source/engine/vg_render.c
new file mode 100644 (file)
index 0000000..8ef24ee
--- /dev/null
@@ -0,0 +1,33 @@
+#include "foundation.h"
+#include "vg_render.h"
+#include "vg_opengl.h"
+
+static GLuint quad_vao, quad_vbo;
+
+void _vg_render_init(void)
+{
+   $log( $info, {"[INIT] _vg_render_init"} );
+
+   f32 quad[] = 
+   { 
+      0.00f,0.00f, 1.00f,1.00f, 0.00f,1.00f,
+      0.00f,0.00f, 1.00f,0.00f, 1.00f,1.00f,    
+   };
+
+   glGenVertexArrays( 1, &quad_vao );
+   glGenBuffers( 1, &quad_vbo );
+   glBindVertexArray( quad_vao );
+   glBindBuffer( GL_ARRAY_BUFFER, quad_vbo );
+   glBufferData( GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW );
+
+
+   glBindVertexArray( quad_vao ); // WTF
+   glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, sizeof(f32)*2, (void*)0 );
+   glEnableVertexAttribArray( 0 );
+}
+
+void _render_fullscreen_quad(void)
+{
+   glBindVertexArray( quad_vao );
+   glDrawArrays( GL_TRIANGLES, 0, 6 );
+}
diff --git a/source/engine/vg_render.h b/source/engine/vg_render.h
new file mode 100644 (file)
index 0000000..f052b0a
--- /dev/null
@@ -0,0 +1,2 @@
+#pragma once
+void _render_fullscreen_quad(void);
diff --git a/source/engine/vg_shader.c b/source/engine/vg_shader.c
new file mode 100644 (file)
index 0000000..81b9adf
--- /dev/null
@@ -0,0 +1,142 @@
+#include "foundation.h"
+#include "vg_opengl.h"
+#include "vg_shader.h"
+
+GLuint compile_opengl_subshader( GLint type, const c8 *sources[], u32 source_count, b8 critical, const c8 *name )
+{
+       GLuint shader = glCreateShader( type );
+   if( shader == 0 )
+   {
+      $log( $fatal, {"glCreateShader returned 0.\n"} );
+      _fatal_exit();
+   }
+
+       glShaderSource( shader, source_count, sources, NULL );
+       glCompileShader( shader );
+
+       GLint status;
+       glGetShaderiv( shader, GL_COMPILE_STATUS, &status );
+   if( status == GL_TRUE )
+      return shader;
+   else
+   {
+      GLchar info[1024];
+      GLsizei len;
+      glGetShaderInfoLog( shader, sizeof(info), &len, info );
+
+      const c8 *type_str = "?";
+           if( type == GL_VERTEX_SHADER )   type_str = "GL_VERTEX_SHADER";
+      else if( type == GL_FRAGMENT_SHADER ) type_str = "GL_FRAGMENT_SHADER";
+      if( critical ) 
+      {
+         $log( $fatal, {"subshader compile error\n"}, {name}, {": "}, {type_str}, {info} );
+         _fatal_exit();
+      }
+      return 0;
+   }
+}
+
+b8 link_opengl_program( GLuint program, b8 critical )
+{
+       glLinkProgram( program );
+       GLint success;
+       glGetProgramiv( program, GL_LINK_STATUS, &success );
+       if( success ) 
+      return 1;
+   else
+       {
+      char info[ 512 ];
+               glGetProgramInfoLog( program, sizeof(info), NULL, info );
+      if( critical ) 
+      {
+         $log( $fatal, {"Shader link error\n"}, {info} );
+         _fatal_exit();
+      }
+               return 0;
+       }
+}
+
+#if 0
+GLuint compile_opengl_shader( const c8 *vs, const c8 *fs )
+{
+   GLuint vert = compile_opengl_subshader( GL_VERTEX_SHADER, vs, 1, NULL ),
+          frag = compile_opengl_subshader( GL_FRAGMENT_SHADER, fs, 1, NULL ),
+              program = glCreateProgram();
+       glAttachShader( program, vert );
+       glAttachShader( program, frag );
+   link_opengl_program( program, 1 );
+       glDeleteShader( vert );
+       glDeleteShader( frag );
+   return program;
+}
+#endif
+
+GLuint _shader_programs[ k_shader_count ];
+GLuint _uniform_locations[ k_shader_uniform_count ];
+
+struct shader
+{
+   const c8 *name;
+   u32 subshader_count, uniform_start, uniform_count;
+
+   struct subshader 
+   {
+      enum subshader_type
+      {
+         k_subshader_vertex,
+         k_subshader_fragment,
+         k_subshader_geometry
+      }
+      type;
+      u32 source_count;
+      const c8 **source_list;
+      const c8 *source_static;
+      const c8 *source_uniforms;
+   }
+   *subshaders;
+};
+
+#include "generated/shaders.c"
+
+void _shaders_compile(void)
+{
+   for( u32 i=0; i<k_shader_count; i ++ )
+   {
+      struct shader *shader = &_shader_definitions[ i ];
+      GLuint new_program = glCreateProgram();
+      GLuint sub_programs[3];
+
+      for( u32 j=0; j<shader->subshader_count; j ++ )
+      {
+         struct subshader *subshader = &shader->subshaders[j];
+
+         const c8 *sources[32] = { "#version 330 core\n", NULL };
+         u32 source_count = 1;
+         sources[ source_count ++ ] = subshader->source_uniforms;
+         sources[ source_count ++ ] = subshader->source_static;
+
+         ASSERT_CRITICAL( subshader->type != k_subshader_geometry );
+         sub_programs[j] = compile_opengl_subshader( 
+               (subshader->type == k_subshader_vertex? GL_VERTEX_SHADER: GL_FRAGMENT_SHADER), 
+               sources, source_count, 1, shader->name );
+         glAttachShader( new_program, sub_programs[j] );
+      }
+
+      link_opengl_program( new_program, 1 );
+      _shader_programs[i] = new_program;
+
+      for( u32 j=0; j<shader->subshader_count; j ++ )
+         glDeleteShader( sub_programs[j] );
+
+      for( u32 j=0; j<shader->uniform_count; j ++ )
+      {
+         u32 index = shader->uniform_start+j;
+         _uniform_locations[ index ] = glGetUniformLocation( new_program, _uniform_aliases[ index ] );
+      }
+   }
+}
+
+void _shader_bind( enum shader_id id )
+{
+   glUseProgram( _shader_programs[ id ] );
+}
diff --git a/source/engine/vg_shader.h b/source/engine/vg_shader.h
new file mode 100644 (file)
index 0000000..76ee8e7
--- /dev/null
@@ -0,0 +1,6 @@
+#include "vg_opengl.h"
+#include "generated/shaders.h"
+
+GLuint compile_opengl_subshader( GLint type, const c8 *sources[], u32 source_count, b8 critical, const c8 *name );
+b8 link_opengl_program( GLuint program, b8 critical );
+void _shader_bind( enum shader_id id );
diff --git a/source/engine/vg_tex.c b/source/engine/vg_tex.c
new file mode 100644 (file)
index 0000000..01559f7
--- /dev/null
@@ -0,0 +1,438 @@
+#include "foundation.h"
+#include "vg_tex.h"
+#include "vg_async.h"
+
+#define QOI_OP_INDEX  0x00 /* 00xxxxxx */
+#define QOI_OP_DIFF   0x40 /* 01xxxxxx */
+#define QOI_OP_LUMA   0x80 /* 10xxxxxx */
+#define QOI_OP_RUN    0xc0 /* 11xxxxxx */
+#define QOI_OP_RGB    0xfe /* 11111110 */
+#define QOI_OP_RGBA   0xff /* 11111111 */
+#define QOI_MASK_2    0xc0 /* 11000000 */
+
+#define QOI_COLOR_HASH(C) (C.rgba[0]*3 + C.rgba[1]*5 + C.rgba[2]*7 + C.rgba[3]*11)
+#define QOI_MAGIC \
+   (((u32)'q')       | ((u32)'o') << 8 | \
+    ((u32)'i') << 16 | ((u32)'f') << 24)
+
+static const u8 qoi_padding[8] = {0,0,0,0,0,0,0,1};
+
+#define cpu_to_big32 big32_to_cpu
+static inline u32 big32_to_cpu( u32 x )
+{
+   return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | (x >> 24);
+}
+
+b8 vg_qoi_validate( const struct qoi_desc *desc )
+{
+   if( (desc->width == 0)    || (desc->height == 0) ||
+       (desc->width >= 2048) || (desc->height >= 2048) )
+   {
+      $log( $error, {"QOI file is invalid; Unpermitted size: "}, $unsigned( desc->width ), {" by "}, $unsigned( desc->height ) );
+      return 0;
+   }
+
+   if( !(desc->channels == 3 || desc->channels == 4) )
+   {
+      $log( $error, {"QOI file is invalid; Only 3 or 4 channels allowed, file has: "}, $unsigned( desc->channels ) );
+      return 0;
+   }
+   return 1;
+}
+
+/* Initalize stream context and return the number of bytes required to store the final RGB/A image data. */
+u32 vg_qoi_stream_init( struct qoi_desc *desc, struct stream *stream )
+{
+   stream_read( stream, desc, sizeof(struct qoi_desc) );
+   if( desc->magic != QOI_MAGIC )
+   {
+      $log( $error, {"QOI file is invalid; Magic Number incorrect."} );
+      return 0;
+   }
+
+   desc->width = big32_to_cpu( desc->width );
+   desc->height = big32_to_cpu( desc->height );
+
+   if( vg_qoi_validate( desc ) )
+      return desc->width * desc->height * desc->channels;
+   else return 0;
+}
+
+void vg_qoi_stream_decode( struct qoi_desc *desc, struct stream *stream, u8 *pixels, b8 v_flip )
+{
+   union qoi_rgba_t index[64], px;
+   zero_buffer( index, sizeof(union qoi_rgba_t)*64 );
+   zero_buffer( &px, sizeof(union qoi_rgba_t) );
+   px.rgba[3] = 255;
+
+   u32 run=0;
+   for( u32 y=0; y<desc->height; y ++ )
+   {
+      for( u32 x=0; x<desc->width; x ++ )
+      {
+         if( run > 0 )
+            run --;
+         else 
+         {
+            u8 b1;
+            stream_read( stream, &b1, 1 );
+
+            if( b1 == QOI_OP_RGB ) 
+               stream_read( stream, px.rgba, 3 );
+            else if( b1 == QOI_OP_RGBA ) 
+               stream_read( stream, px.rgba, 4 );
+            else if( (b1 & QOI_MASK_2) == QOI_OP_INDEX ) 
+               px = index[b1];
+            else if( (b1 & QOI_MASK_2) == QOI_OP_DIFF ) 
+            {
+               px.rgba[0] += (i32)((b1 >> 4) & 0x03) - 2;
+               px.rgba[1] += (i32)((b1 >> 2) & 0x03) - 2;
+               px.rgba[2] += (i32)( b1       & 0x03) - 2;
+            }
+            else if( (b1 & QOI_MASK_2) == QOI_OP_LUMA ) 
+            {
+               u8 b2;
+               stream_read( stream, &b2, 1 );
+               i32 vg = (i32)(b1 & 0x3f) - 32;
+               px.rgba[0] += vg - 8 + (i32)((b2 >> 4) & 0x0f);
+               px.rgba[1] += vg;
+               px.rgba[2] += vg - 8 + (i32)(b2 & 0x0f);
+            }
+            else if( (b1 & QOI_MASK_2) == QOI_OP_RUN ) 
+               run = (b1 & 0x3f);
+            index[ QOI_COLOR_HASH(px) % 64 ] = px;
+         }
+
+         u32 row = v_flip? desc->height-(y+1): y;
+         for( u32 i=0; i < desc->channels; i ++ )
+            pixels[ ((row*desc->width) + x)*desc->channels + i ] = px.rgba[i];
+      }
+   }
+}
+
+u32 vg_query_qoi_max_compressed_size( const struct qoi_desc *desc )
+{
+   return desc->width * desc->height * (desc->channels + 1) + sizeof(struct qoi_desc) + sizeof(qoi_padding);
+}
+
+u32 vg_qoi_stream_encode( const struct qoi_desc *desc, const u8 *pixels, struct stream *stream, b8 v_flip )
+{
+   if( !vg_qoi_validate( desc ) )
+      return 0;
+
+   struct qoi_desc file_header = *desc;
+   file_header.magic  = QOI_MAGIC;
+   file_header.width  = cpu_to_big32( file_header.width );
+   file_header.height = cpu_to_big32( file_header.height );
+   stream_write( stream, &file_header, sizeof(struct qoi_desc) );
+   
+   union qoi_rgba_t index[64];
+   zero_buffer( index, sizeof(union qoi_rgba_t)*64 );
+   union qoi_rgba_t px_prev = { .rgba = { 0, 0, 0, 255 } };
+   union qoi_rgba_t px = px_prev;
+
+   u32 run = 0;
+   for( u32 y=0; y<desc->height; y ++ )
+   {
+      for( u32 x=0; x<desc->width; x ++ )
+      {
+         u32 row = v_flip? desc->height-(y+1): y;
+         for( u32 i=0; i < desc->channels; i ++ )
+            px.rgba[i] = pixels[ ((row*desc->width) + x)*desc->channels + i ];
+
+         if( px.v == px_prev.v )
+         {
+            run ++;
+            if( run == 62 || ((y+1 == desc->height) && (x+1 == desc->width)) )
+            {
+               u8 b1 = QOI_OP_RUN | (run - 1);
+               stream_write( stream, &b1, 1 );
+               run = 0;
+            }
+         }
+         else
+         {
+            if( run > 0 )
+            {
+               u8 b1 = QOI_OP_RUN | (run - 1);
+               stream_write( stream, &b1, 1 );
+               run = 0;
+            }
+
+            u32 index_pos = QOI_COLOR_HASH( px ) % 64;
+            if( index[ index_pos ].v == px.v )
+            {
+               u8 b1 = QOI_OP_INDEX | index_pos;
+               stream_write( stream, &b1, 1 );
+            }
+            else
+            {
+               index[ index_pos ] = px;
+               if( px.rgba[3] == px_prev.rgba[3] )
+               {
+                  i8 vr = px.rgba[0] - px_prev.rgba[0],
+                     vg = px.rgba[1] - px_prev.rgba[1],
+                     vb = px.rgba[2] - px_prev.rgba[2],
+                     vg_r = vr - vg,
+                     vg_b = vb - vg;
+
+                  if( vr > -3 && vr < 2 &&
+                      vg > -3 && vg < 2 &&
+                      vb > -3 && vb < 2 ) 
+                  {
+                     stream_write( stream, (u8[]){ QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2) }, 1 );
+                  }
+                  else if( vg_r >  -9 && vg_r <  8 &&
+                           vg   > -33 && vg   < 32 &&
+                           vg_b >  -9 && vg_b <  8 ) 
+                  {
+                     stream_write( stream, (u8[]){ QOI_OP_LUMA | (vg   + 32), (vg_r + 8) << 4 | (vg_b +  8) }, 2 );
+                  }
+                  else 
+                  {
+                     stream_write( stream, (u8[]){ QOI_OP_RGB }, 1 );
+                     stream_write( stream, px.rgba, 3 );
+                  }
+               }
+               else
+               {
+                  stream_write( stream, (u8 []){ QOI_OP_RGBA }, 1 );
+                  stream_write( stream, px.rgba, 4 );
+               }
+            }
+         }
+         px_prev = px;
+      }
+   }
+   stream_write( stream, qoi_padding, sizeof(qoi_padding) );
+   return 1;
+}
+
+/* VG_PART 
+ * ------------------------------------------------------------------------------------------------------------------ */
+
+struct
+{
+   GLuint error2d, errorcube;
+}
+static _vg_tex;
+
+void _vg_tex_init(void)
+{
+   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_OPENGL ) );
+   $log( $info, {"[INIT] _vg_tex_init"} );
+
+   static u8 const_vg_tex2d_err[] =
+   {
+      0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff,
+      0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 
+      0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff,
+      0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 
+   };
+
+   glGenTextures( 1, &_vg_tex.error2d );
+   glBindTexture( GL_TEXTURE_2D, _vg_tex.error2d );
+   glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, const_vg_tex2d_err );
+   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
+   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
+
+   glGenTextures( 1, &_vg_tex.errorcube );
+   glBindTexture( GL_TEXTURE_CUBE_MAP, _vg_tex.errorcube );
+   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
+
+   for( u32 j=0; j<6; j ++ ) 
+   {
+      glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, 0, GL_RGBA, 4, 4, 
+                    0, GL_RGBA, GL_UNSIGNED_BYTE, const_vg_tex2d_err );
+   }
+}
+
+struct tex_upload_task 
+{
+   struct vg_tex *tex;
+   u32 width, height, channels, flags;
+   u8 image_buffer[];
+};
+
+static void _vg_tex_upload( struct task *task )
+{
+   struct tex_upload_task *in_args = task_buffer( task );
+   ASSERT_CRITICAL( _vg_tex.errorcube && _vg_tex.error2d );
+   u32 flags = in_args->flags;
+   struct vg_tex *tex = in_args->tex;
+
+   if( flags & VG_TEX_ERROR )
+   {
+      tex->name  = (flags & VG_TEX_CUBEMAP)? _vg_tex.errorcube: _vg_tex.error2d;
+      tex->flags = (flags & VG_TEX_CUBEMAP) | VG_TEX_ERROR | VG_TEX_COMPLETE;
+   }
+   else
+   {
+      u32 pixel_format = 0;
+      if( in_args->channels == 3 ) pixel_format = GL_RGB;
+      else if( in_args->channels == 4 ) pixel_format = GL_RGBA;
+      else 
+      {
+         $log( $fatal, {"Can't upload texture with "}, $unsigned( in_args->channels ), {" channels."} );
+         _fatal_exit();
+      }
+
+      glGenTextures( 1, &tex->name );
+      u32 filter_min = 0,
+          filter_mag = 0;
+      if( flags & VG_TEX_LINEAR )
+      {
+         if( flags & VG_TEX_NOMIP ) filter_min = GL_LINEAR;
+         else                       filter_min = GL_LINEAR_MIPMAP_LINEAR;
+         filter_mag = GL_LINEAR;
+      }
+      else
+      {
+         ASSERT_CRITICAL( flags & VG_TEX_NEAREST );
+         filter_min = GL_NEAREST;
+         filter_mag = GL_NEAREST;
+      }
+
+      if( flags & VG_TEX_CUBEMAP )
+      {
+         u32 w = in_args->width,
+             h = in_args->height/6;
+
+         glBindTexture(   GL_TEXTURE_CUBE_MAP, tex->name );
+         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, filter_min );
+         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, filter_mag );
+         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); /* can this be anything else? */
+         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
+
+         for( u32 j=0; j<6; j ++ ) 
+         {
+            u32 offset = w*h*j*in_args->channels;
+            glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, 0, pixel_format,
+                           w, h,
+                           0, pixel_format, GL_UNSIGNED_BYTE, in_args->image_buffer + offset );
+         }
+
+         if( !(flags & VG_TEX_NOMIP) )
+            glGenerateMipmap( GL_TEXTURE_CUBE_MAP );
+      }
+      else
+      {
+         glBindTexture( GL_TEXTURE_2D, tex->name );
+         glTexImage2D( GL_TEXTURE_2D, 0, pixel_format, in_args->width, in_args->height,
+                        0, pixel_format, GL_UNSIGNED_BYTE, in_args->image_buffer );
+
+         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_min );
+         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_mag );
+
+         u32 wrap_s = 0,
+             wrap_t = 0;
+
+         if( flags & VG_TEX_CLAMP )
+         {
+            wrap_s = GL_CLAMP_TO_EDGE;
+            wrap_t = GL_CLAMP_TO_EDGE;
+         }
+         else
+         {
+            ASSERT_CRITICAL( flags & VG_TEX_REPEAT );
+            wrap_s = GL_REPEAT;
+            wrap_t = GL_REPEAT;
+         }
+
+         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s );
+         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t );
+
+         if( !(flags & VG_TEX_NOMIP) )
+            glGenerateMipmap( GL_TEXTURE_2D );
+      }
+      tex->flags = flags | VG_TEX_COMPLETE;
+   }
+}
+
+b8 _vg_tex_load_stream( struct vg_tex *out_tex, struct stream *in_stream, u32 flags )
+{
+   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
+
+   struct qoi_desc qoi;
+   u32 size = vg_qoi_stream_init( &qoi, in_stream );
+   if( size )
+   {
+      _async_push_groups( ASYNC_GROUP_OPENGL, 0 );
+
+      struct task *upload_task = _task_new( k_thread_main, sizeof( struct tex_upload_task ) + size, 0, "Texture upload task" );
+      struct tex_upload_task *args = task_buffer( upload_task );
+      vg_qoi_stream_decode( &qoi, in_stream, args->image_buffer, flags & VG_TEX_FLIP_V? 1: 0 );
+      args->tex = out_tex;
+      args->width = qoi.width;
+      args->height = qoi.height;
+      args->channels = qoi.channels;
+      args->flags = flags;
+      task_send( upload_task, _vg_tex_upload );
+      _async_pop_groups();
+      return 1;
+   }
+   else 
+      return 0;
+}
+
+void _vg_tex_load( struct vg_tex *out_tex, const c8 *path, u32 flags )
+{
+   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
+
+   b8 error = 0;
+   struct stream file;
+   if( stream_open_file( &file, path, k_stream_read ) )
+   {
+      if( !_vg_tex_load_stream( out_tex, &file, flags ) )
+         error = 1;
+      stream_close( &file );
+   }
+   else 
+      error = 1;
+
+   if( error )
+   {
+      _async_push_groups( ASYNC_GROUP_OPENGL, 0 );
+      struct task *upload_task = _task_new( k_thread_main, sizeof( struct tex_upload_task ), 0, "Texture upload task" );
+      struct tex_upload_task *args = task_buffer( upload_task );
+      args->tex = out_tex;
+      args->width = 0;
+      args->height = 0;
+      args->channels = 0;
+      args->flags = VG_TEX_ERROR;
+      task_send( upload_task, _vg_tex_upload );
+      _async_pop_groups();
+   }
+}
+
+u32 vg_tex_name( GLuint target, struct vg_tex *tex )
+{
+   if( !tex ) 
+   {
+      return (target == GL_TEXTURE_2D)? _vg_tex.error2d: _vg_tex.errorcube;
+   }
+   if( tex->flags & VG_TEX_COMPLETE ) return tex->name;
+   else                               return (target == GL_TEXTURE_2D)? _vg_tex.error2d: _vg_tex.errorcube;
+}
+
+void vg_tex_bind( GLuint target, struct vg_tex *tex, u32 slot )
+{
+   glActiveTexture( GL_TEXTURE0 + slot );
+   glBindTexture( target, vg_tex_name( target, tex ) );
+}
+
+void vg_tex_delete( struct vg_tex *tex )
+{
+   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_OPENGL ) );
+   ASSERT_CRITICAL( tex->flags & VG_TEX_COMPLETE );
+   if( !(tex->flags & VG_TEX_ERROR) )
+      glDeleteTextures( 1, &tex->name );
+   zero_buffer( tex, sizeof(struct vg_tex) );
+}
diff --git a/source/engine/vg_tex.h b/source/engine/vg_tex.h
new file mode 100644 (file)
index 0000000..89c14c4
--- /dev/null
@@ -0,0 +1,55 @@
+#pragma once
+#include "foundation.h"
+#include "vg_opengl.h"
+
+struct vg_tex
+{
+   u32 name;
+   u32 flags;
+};
+
+#pragma pack(push,1)
+union qoi_rgba_t
+{
+   u8 rgba[4];
+   u32 v;
+};
+
+struct qoi_desc
+{
+   u32 magic;
+   u32 width;
+   u32 height;
+   u8  channels;
+   u8  colorspace;
+};
+#pragma pack(pop)
+
+# define QOI_SRGB   0
+# define QOI_LINEAR 1
+
+/* Reading qois */
+u32 vg_qoi_stream_init( struct qoi_desc *desc, struct stream *file );
+void vg_qoi_stream_decode( struct qoi_desc *desc, struct stream *file, u8 *dest_buffer, b8 v_flip );
+
+/* Writing qois */
+u32 vg_query_qoi_max_compressed_size( const struct qoi_desc *desc );
+u32 vg_qoi_stream_encode( const struct qoi_desc *desc, const u8 *pixels, struct stream *stream, b8 v_flip );
+
+#  define VG_TEX_LINEAR   0x1
+#  define VG_TEX_NEAREST  0x2
+#  define VG_TEX_REPEAT   0x4
+#  define VG_TEX_CLAMP    0x8
+#  define VG_TEX_NOMIP    0x10
+#  define VG_TEX_CUBEMAP  0x20
+#  define VG_TEX_ERROR    0x40
+#  define VG_TEX_COMPLETE 0x80
+#  define VG_TEX_FLIP_V   0x100
+#  define VG_TEX_FRAMEBUFFER_ATTACHMENT 0x200
+#  define VG_TEX_PRIVATE  0x400 /* used on renderbuffers... */
+
+u32  vg_tex_name( GLuint target, struct vg_tex *tex );
+void vg_tex_bind( GLuint target, struct vg_tex *tex, u32 slot );
+void vg_tex_delete( struct vg_tex *tex );
+void _vg_tex_load( struct vg_tex *out_tex, const c8 *path, u32 flags );
+b8 _vg_tex_load_stream( struct vg_tex *out_tex, struct stream *in_stream, u32 flags );
diff --git a/source/engine/vg_ui.c b/source/engine/vg_ui.c
new file mode 100644 (file)
index 0000000..14ca3cc
--- /dev/null
@@ -0,0 +1,71 @@
+#include "foundation.h"
+#include "vg_engine.h"
+#include "vg_graphics.h"
+#include "vg_render.h"
+#include "vg_opengl.h"
+#include "vg_shader.h"
+
+struct graphics_target _ui_surface = 
+{
+   .mode = k_graphics_mode_software,
+   .size = { 1920, 1080 },
+};
+
+static GLuint _ui_surface_texture;
+
+#include <unistd.h>
+void _engine_ui_init(void)
+{
+   _ui_surface.buffer = _heap_allocate( 1920*1080*4 );
+
+   glGenTextures( 1, &_ui_surface_texture );
+   glActiveTexture( GL_TEXTURE0 );
+   glBindTexture( GL_TEXTURE_2D, _ui_surface_texture );
+   glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL );
+   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+   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 );
+}
+
+void _engine_ui_pre_render(void)
+{
+   ASSERT_CRITICAL( _engine.w <= 1920 );
+   ASSERT_CRITICAL( _engine.h <= 1080 );
+   
+   f64 x = 0, y = 0;
+#if 0
+   glfwGetCursorPos( _engine.window_handle, &x, &y );
+#endif
+   _ui_set_mouse( x, y );
+   _ui_update();
+
+   _graphics_set_target( &_ui_surface );
+   _graphics_viewport( 0, 0, _engine.w, _engine.h );
+   //_graphics_fill_rect( (i16[]){ 0, 0, _engine.w, _engine.h }, (union colour){{0,0,0,255}} );
+}
+
+void _engine_ui_input_character( u32 codepoint )
+{
+   if( (codepoint >= (u32)' ') && (codepoint <= (u32)'~') )
+      _ui_input_text( (const c8[]){ codepoint, 0 } );
+}
+
+void _engine_ui_post_render(void)
+{
+   glEnable( GL_BLEND );
+   glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+   glBlendEquation( GL_FUNC_ADD );
+
+   glActiveTexture( GL_TEXTURE0 );
+   glBindTexture( GL_TEXTURE_2D, _ui_surface_texture );
+   glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 1920, 1080, GL_BGRA, GL_UNSIGNED_BYTE, _ui_surface.buffer );
+
+   _shader_bind( k_shader_blit );
+   _shader_blit_uTexMain( 0 );
+   _shader_blit_uFlip( 1 );
+   _shader_blit_uInverseRatio( (f32[2]){ (f64)_engine.w/1920.0, (f64)_engine.h/1080.0 } );
+
+   _render_fullscreen_quad();
+   glDisable( GL_BLEND );
+}
diff --git a/source/engine/vg_ui.h b/source/engine/vg_ui.h
new file mode 100644 (file)
index 0000000..34e36cc
--- /dev/null
@@ -0,0 +1,4 @@
+#pragma once
+
+void _engine_ui_pre_render(void);
+void _engine_ui_post_render(void);
index b55bfabcd5e532f59557b6af5409f83a59653930..ef5f0fa2c7f90ac396e3765474280be02f8dd6b0 100644 (file)
@@ -1,5 +1,5 @@
+#include "foundation.h"
 #include <stdlib.h>
-#include "common_api.h"
 
 void *_heap_allocate( u64 size )
 {
index 6301acf7d7c03a94a4cb92cd120f6be49303a4e5..7bfa9cda06bbb32af6a7551c4f2d099ec8a2f2c2 100644 (file)
@@ -1,4 +1,4 @@
-#include "common_api.h"
+#include "foundation.h"
 
 void pool_init( struct pool_allocator *pool, struct pool_node *nodes, u16 node_count, struct pool_chain *full_chain )
 {
@@ -25,7 +25,7 @@ u32 pool_index( struct pool_allocator *pool, u16 pool_id )
    return pool_id-1;
 }
 
-u16 pool_reference( struct pool_allocator *pool, u16 pool_id, bool increment )
+u16 pool_reference( struct pool_allocator *pool, u16 pool_id, b8 increment )
 {
    ASSERT_CRITICAL( pool_id );
    ASSERT_CRITICAL( pool );
@@ -43,7 +43,7 @@ u16 pool_reference( struct pool_allocator *pool, u16 pool_id, bool increment )
    return pnode->refcount;
 }
 
-u16 pool_next( struct pool_allocator *pool, u16 pool_id, bool right )
+u16 pool_next( struct pool_allocator *pool, u16 pool_id, b8 right )
 {
    ASSERT_CRITICAL( pool_id );
    if( right ) return pool->nodes[ pool_id -1 ].r;
index 7b15aeb5e6a44425d8576a37ff2a3ae1dc0adbb7..11f577284ea02e278d3442f3a012dcb52004dec3 100644 (file)
@@ -1,4 +1,4 @@
-#include "common_api.h"
+#include "foundation.h"
 
 void queue_init( struct queue_allocator *queue, void *buffer, u32 buffer_size )
 {
@@ -95,7 +95,7 @@ u32 queue_item_size( struct queue_allocator *queue, u32 item_id )
    return item->alloc_size;
 }
 
-bool queue_next( struct queue_allocator *queue, u32 item_id, u32 *out_next )
+b8 queue_next( struct queue_allocator *queue, u32 item_id, u32 *out_next )
 {
    if( item_id != queue->head_offset )
    {
@@ -107,7 +107,7 @@ bool queue_next( struct queue_allocator *queue, u32 item_id, u32 *out_next )
    else return 0;
 }
 
-bool queue_previous( struct queue_allocator *queue, u32 item_id, u32 *out_prev )
+b8 queue_previous( struct queue_allocator *queue, u32 item_id, u32 *out_prev )
 {
    struct queue_item *item = queue->buffer + item_id;
    if( item_id != queue->tail_offset )
index 88d9d128940d15bf55edfef4b391668dfc312e08..f09f2e0e6b16ee0bf3ed956b1305f4b7e6860082 100644 (file)
@@ -1,4 +1,4 @@
-#include "common_api.h"
+#include "foundation.h"
 
 void stack_init( struct stack_allocator *stack, void *buffer, u32 capacity, const c8 *debug_name )
 {
index e4628e315df5eb0c0c8e2807022dd66329e1901d..df2239f0cd2102f0c4c7368ec6ecc62e5e9cc05b 100644 (file)
@@ -1,4 +1,5 @@
-#include "common_api.h"
+#include "foundation.h"
+
 #define SMALL_SEGMENTS 4
 void stretchy_init( struct stretchy_allocator *stretchy, u32 element_size )
 {
diff --git a/source/foundation/async.c b/source/foundation/async.c
deleted file mode 100644 (file)
index 69a9379..0000000
+++ /dev/null
@@ -1,295 +0,0 @@
-#include "common_api.h"
-#include "common_thread_api.h"
-#include "generated/threads.c"
-
-#include <threads.h>
-
-#define ASYNC_DEBUG_GROUP_COUNTS
-
-struct thread_context
-{
-   u16 async_groups[ 8 ];
-   u32 async_group_depth;
-}
-_thread_contexts[ k_thread_count ];
-
-struct thread_info *_get_thread_info( enum thread_id thread )
-{
-   ASSERT_CRITICAL( thread < k_thread_count );
-   return &_thread_infos[ thread ];
-}
-
-bool _thread_has_flags( enum thread_id thread, u32 flags )
-{
-   return (_get_thread_info( thread )->flags & flags) == flags;
-}
-
-void _async_push_groups( u16 groups, bool exclusive )
-{
-   struct thread_context *context = &_thread_contexts[ _get_thread_id() ];
-
-   ASSERT_CRITICAL( context->async_group_depth < ARRAY_COUNT( context->async_groups ) );
-   context->async_group_depth ++;
-   if( !exclusive )
-      groups |= context->async_groups[ context->async_group_depth-1 ];
-   context->async_groups[ context->async_group_depth ] = groups;
-}
-
-void _async_pop_groups(void)
-{
-   struct thread_context *context = &_thread_contexts[ _get_thread_id() ];
-   ASSERT_CRITICAL( context->async_group_depth );
-   context->async_group_depth --;
-}
-
-u16 _async_get_groups(void)
-{
-   struct thread_context *context = &_thread_contexts[ _get_thread_id() ];
-   return context->async_groups[ context->async_group_depth ];
-}
-
-struct 
-{
-   i16 group_counts[ 16 ];
-   mtx_t count_lock;
-}
-_async;
-
-struct task_queue
-{
-   u32 count;
-
-   cnd_t blocking_signal, work_signal;
-   mtx_t lock, data_lock;
-   struct queue_allocator queue;
-
-   struct task *allocating_task;
-}
-static _task_queues[ k_thread_count ];
-
-struct task
-{
-   const c8 *alloc_debug_info;
-
-   union
-   {
-      void (*fn)( struct task *task );
-      enum thread_id target_thread;
-   };
-
-   u32 buffer_size;
-   u16 groups, unused0;
-
-   union
-   {
-      u64 _force_8byte_align[];
-      u8 buffer[];
-   };
-};
-
-_Thread_local enum thread_id _thread_id;
-
-void _set_thread_id( enum thread_id id )
-{
-   _thread_id = id;
-}
-
-enum thread_id _get_thread_id(void)
-{
-   return _thread_id;
-}
-
-void _async_init( void )
-{
-   ASSERT_CRITICAL( mtx_init( &_async.count_lock, mtx_plain ) == thrd_success );
-
-   for( u32 i=0; i<k_thread_count; i ++ )
-   {
-      struct thread_info *thread_info = _get_thread_info( i );
-
-      if( thread_info->queue_size_m )
-      {
-         struct task_queue *queue = &_task_queues[ i ];
-         ASSERT_CRITICAL( mtx_init( &queue->lock, mtx_plain ) == thrd_success );
-         ASSERT_CRITICAL( mtx_init( &queue->data_lock, mtx_plain ) == thrd_success );
-         ASSERT_CRITICAL( cnd_init( &queue->blocking_signal ) == thrd_success );
-         ASSERT_CRITICAL( cnd_init( &queue->work_signal ) == thrd_success );
-         u32 bytes = BYTES_MB(thread_info->queue_size_m);
-         queue->queue.buffer = _heap_allocate( bytes );
-         queue->queue.size = bytes;
-      }
-   }
-}
-
-static void _async_group_increment( u16 groups, i16 dir, const c8 *who )
-{
-   if( !groups )
-      return;
-
-   ASSERT_CRITICAL( mtx_lock( &_async.count_lock ) == thrd_success );
-   for( u16 i=0; i<16; i ++ )
-   {
-      if( (groups >> i) & 0x1 )
-      {
-         _async.group_counts[i] += dir;
-         ASSERT_CRITICAL( _async.group_counts[i] >= 0 );
-         ASSERT_CRITICAL( _async.group_counts[i] <= 2048 );
-
-#if defined( ASYNC_DEBUG_GROUP_COUNTS )
-         $log( $warning, {"The task count for group "}, $unsigned(i), {" has "}, {dir>0? "increased": "decreased"}, 
-                         {" to "}, $unsigned(_async.group_counts[i]), {" ("}, {who}, {")"} );
-#endif
-      }
-   }
-   ASSERT_CRITICAL( mtx_unlock( &_async.count_lock ) == thrd_success );
-}
-
-i16 _async_group_count( u16 group )
-{
-   ASSERT_CRITICAL( group );
-   u32 index = __builtin_ctz( (u32)group );
-   ASSERT_CRITICAL( mtx_lock( &_async.count_lock ) == thrd_success );
-   i16 count = _async.group_counts[ index ];
-   ASSERT_CRITICAL( mtx_unlock( &_async.count_lock ) == thrd_success );
-   return count;
-}
-
-static struct task_queue *_get_thread_task_queue( enum thread_id thread )
-{
-   ASSERT_CRITICAL( thread < k_thread_count );
-
-   return &_task_queues[ thread ];
-}
-
-bool _task_queue_can_fit( enum thread_id thread, u32 bytes )
-{
-   struct task_queue *queue = _get_thread_task_queue( thread );
-   u32 total_size = sizeof(struct task) + bytes;
-   return total_size <= queue->queue.size;
-}
-
-struct task *_task_new( enum thread_id target_thread, u32 buffer_size, u32 async_flags, const c8 *debug_info )
-{
-   ASSERT_CRITICAL( target_thread != _get_thread_id() );
-
-   struct task_queue *queue = _get_thread_task_queue( target_thread );
-   struct queue_allocator *ring = &queue->queue;
-   u32 total_size = sizeof(struct task) + buffer_size;
-   ASSERT_CRITICAL( total_size <= queue->queue.size );
-
-   ASSERT_CRITICAL( mtx_lock( &queue->data_lock ) == thrd_success );
-   if( queue->allocating_task )
-   {
-      $log( $fatal, {"Overlapping async allocations. \n"
-                     "  Previous allocation began at: "}, {queue->allocating_task->alloc_debug_info}, {"\n"
-                     "  Overlapping call at: "}, {debug_info} );
-      _fatal_exit();
-   }
-   ASSERT_CRITICAL( mtx_lock( &queue->lock ) == thrd_success );
-
-   struct task *task = queue_alloc( ring, total_size );
-   while( ((async_flags & ASYNC_CRITICAL) || !(async_flags & ASYNC_NONBLOCKING)) && !task )
-   {
-      if( async_flags & ASYNC_CRITICAL )
-      {
-         $log( $fatal, { "Too many tasks allocated on this queue, so we cant make this (critical allocation)"} );
-         _fatal_exit();
-      }
-
-      ASSERT_CRITICAL( cnd_wait( &queue->blocking_signal, &queue->lock ) == thrd_success );
-      task = queue_alloc( ring, total_size );
-   }
-
-   if( task )
-   {
-      queue->allocating_task = task;
-      task->target_thread = target_thread;
-      task->alloc_debug_info = debug_info;
-      task->buffer_size = buffer_size;
-      task->groups = _async_get_groups();
-      _async_group_increment( task->groups, +1, task->alloc_debug_info );
-
-      ASSERT_CRITICAL( mtx_unlock( &queue->lock ) == thrd_success );
-      return task;
-   }
-   else
-   {
-      ASSERT_CRITICAL( mtx_unlock( &queue->lock ) == thrd_success );
-      return NULL;
-   }
-}
-
-void *task_buffer( struct task *task )
-{
-   return task->buffer;
-}
-
-u32 task_buffer_size( struct task *task )
-{
-   return task->buffer_size;
-}
-
-void task_send( struct task *task, void (*fn)( struct task *task ) )
-{
-   struct task_queue *queue = _get_thread_task_queue( task->target_thread );
-
-   ASSERT_CRITICAL( task );
-   ASSERT_CRITICAL( queue->allocating_task == task );
-
-   if( fn ) task->fn = fn;
-   else     _async_group_increment( task->groups, -1, task->alloc_debug_info );
-   queue->allocating_task = NULL;
-
-   ASSERT_CRITICAL( mtx_lock( &queue->lock ) == thrd_success );
-   queue->count ++;
-   ASSERT_CRITICAL( mtx_unlock( &queue->lock ) == thrd_success );
-
-   ASSERT_CRITICAL( mtx_unlock( &queue->data_lock ) == thrd_success );
-   ASSERT_CRITICAL( cnd_signal( &queue->work_signal ) == thrd_success );
-}
-
-bool _task_queue_process( bool blocking )
-{
-   struct task_queue *queue = _get_thread_task_queue( _get_thread_id() );
-   ASSERT_CRITICAL( mtx_lock( &queue->lock ) == thrd_success );
-
-   if( blocking )
-   {
-      while( queue->count == 0 )
-         ASSERT_CRITICAL( cnd_wait( &queue->work_signal, &queue->lock ) == thrd_success );
-   }
-   else
-   {
-      if( queue->count == 0)
-      {
-         ASSERT_CRITICAL( mtx_unlock( &queue->lock ) == thrd_success );
-         return 0;
-      }
-   }
-
-   struct task *task = queue_tail_data( &queue->queue );
-   ASSERT_CRITICAL( mtx_unlock( &queue->lock ) == thrd_success );
-
-   if( task )
-   {
-      /* task fn can be NULL if it was cancelled (so this is a NOP). Makes code easier if we do this instead of 
-       * reverting the queue to cancel. */
-      if( task->fn )
-      {
-         _async_push_groups( task->groups, 0 );
-         task->fn( task );
-         _async_pop_groups();
-         _async_group_increment( task->groups, -1, task->alloc_debug_info );
-      }
-
-      ASSERT_CRITICAL( mtx_lock( &queue->lock ) == thrd_success );
-      queue_pop( &queue->queue );
-      queue->count --;
-      ASSERT_CRITICAL( mtx_unlock( &queue->lock ) == thrd_success );
-
-      ASSERT_CRITICAL( cnd_signal( &queue->blocking_signal ) == thrd_success );
-      return 1;
-   }
-   else
-      return 0;
-}
index fb9ff8ed317c6db04ca4052e4dbccc2055c8afa1..17e3ab4a977706a478bfb5d97f29ceb24155e80a 100644 (file)
@@ -1,4 +1,4 @@
-#include "common_api.h"
+#include "foundation.h"
 
 void zero_buffer( void *buffer, u32 length )
 {
@@ -21,7 +21,7 @@ u32 buffer_djb2( const void *buffer, i32 max_length )
    return hash;
 }
 
-bool compare_buffers( const void *buffer_a, i32 a_max, const void *buffer_b, i32 b_max )
+b8 compare_buffers( const void *buffer_a, i32 a_max, const void *buffer_b, i32 b_max )
 {
    if( !buffer_a || !buffer_b )
       return 0;
@@ -73,7 +73,7 @@ i32 buffer_last_index( const void *buffer, u8 match, i32 max_length )
    return index;
 }
 
-bool buffer_copy( const void *buffer_src, u32 src_length, void *buffer_dest, u32 dest_length )
+b8 buffer_copy( const void *buffer_src, u32 src_length, void *buffer_dest, u32 dest_length )
 {
    ASSERT_CRITICAL( dest_length );
 
index a25a680591c56f38a988f7006e96a888f06a6244..b9e6af93470c898679253ad9bad33873080c3fa7 100644 (file)
@@ -1,4 +1,4 @@
-#include "common_api.h"
+#include "foundation.h"
 #include <signal.h>
 #include <execinfo.h>
 #include <stdio.h>
@@ -9,11 +9,26 @@
 static void sync_signal_handler( i32 signum )
 {
    raise( SIGTRAP );
-   if( signum == SIGSEGV ) $log( $fatal, { "SIGSEGV" } );
-   if( signum == SIGBUS  ) $log( $fatal, { "SIGBUS" } );
-   if( signum == SIGFPE  ) $log( $fatal, { "SIGFPE" } );
-   if( signum == SIGILL  ) $log( $fatal, { "SIGILL" } );
-   $log( $fatal, { "UNKNOWN SIGNAL" } );
+   if( signum == SIGSEGV )
+   {
+      $log( $fatal, { "SIGSEGV" } );
+   }
+   if( signum == SIGBUS  ) 
+   {
+      $log( $fatal, { "SIGBUS" } );
+   }
+   if( signum == SIGFPE  ) 
+   {
+      $log( $fatal, { "SIGFPE" } );
+   }
+   if( signum == SIGILL  ) 
+   {
+      $log( $fatal, { "SIGILL" } );
+   }
+   else
+   {
+      $log( $fatal, { "UNKNOWN SIGNAL" } );
+   }
    _fatal_exit();
 }
 
diff --git a/source/foundation/exit_windows.c b/source/foundation/exit_windows.c
new file mode 100644 (file)
index 0000000..6612ac4
--- /dev/null
@@ -0,0 +1,120 @@
+#include "foundation.h"
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+
+static void sync_signal_handler( i32 signum )
+{
+   if( signum == SIGSEGV ) 
+   {
+      $log( $fatal, { "SIGSEGV" } );
+   }
+   if( signum == SIGFPE  ) 
+   {
+      $log( $fatal, { "SIGFPE" } );
+   }
+   if( signum == SIGILL  ) 
+   {
+      $log( $fatal, { "SIGILL" } );
+   }
+   else 
+   {
+      $log( $fatal, { "UNKNOWN SIGNAL" } );
+   }
+   _fatal_exit();
+}
+
+void _exit_init(void)
+{
+   signal( SIGSEGV, sync_signal_handler );
+   signal( SIGFPE,  sync_signal_handler );
+   signal( SIGILL,  sync_signal_handler );
+}
+
+b8 vg_str_flushfd( struct stream *stream, int fd )
+{
+   b8 good = write( fd, stream->buffer_write, stream->offset ) == stream->offset;
+   string_clip( stream, 0 );
+   return good;
+}
+
+void _fatal_exit(void)
+{
+   fflush( stdout );
+
+   int fd = open( "crash.txt", O_CREAT | O_WRONLY, 0666 );
+   if( fd == -1 )
+      exit(-3);
+
+   c8 buf[ 1024 ];
+   struct stream line;
+   stream_open_buffer_write( &line, buf, sizeof(buf), k_stream_null_terminate );
+
+   HANDLE       process = GetCurrentProcess();
+   HANDLE       thread  = GetCurrentThread();
+   CONTEXT      context;
+   STACKFRAME64 stack;
+   DWORD        machine_type;
+
+   RtlCaptureContext( &context );
+   ZeroMemory( &stack, sizeof(STACKFRAME64) );
+   machine_type           = IMAGE_FILE_MACHINE_AMD64;
+   stack.AddrPC.Offset    = context.Rip;
+   stack.AddrFrame.Offset = context.Rsp;
+   stack.AddrStack.Offset = context.Rsp;
+   stack.AddrPC.Mode    = AddrModeFlat;
+   stack.AddrFrame.Mode = AddrModeFlat;
+   stack.AddrStack.Mode = AddrModeFlat;
+
+   SymInitialize( process, NULL, TRUE );
+   SymSetOptions( SYMOPT_LOAD_LINES | SYMOPT_UNDNAME );
+
+   $v_string( &line, {"OS: Windows\n"
+                      "Comment: "}, {"NONE"},
+                      {"\nStack trace\n"
+                     "-----------------------------------\n"} );
+   vg_str_flushfd( &line, fd );
+
+   DWORD frame_number = 0;
+   while( StackWalk64( machine_type, process, thread, &stack, &context, NULL, SymFunctionTableAccess64,
+                       SymGetModuleBase64, NULL))
+   {
+      if( stack.AddrPC.Offset == 0 )
+         break;
+
+      DWORD64 symbol_addr  = stack.AddrPC.Offset;
+      DWORD64 displacement = 0;
+      char symbol_buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)] = {0};
+      SYMBOL_INFO *symbol  = (SYMBOL_INFO *)symbol_buffer;
+      symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+      symbol->MaxNameLen   = MAX_SYM_NAME;
+
+      char function_name[ MAX_SYM_NAME ] = "Unknown";
+      if( SymFromAddr( process, symbol_addr, &displacement, symbol ) )
+      {
+         strncpy( function_name, symbol->Name, MAX_SYM_NAME - 1 );
+         function_name[ MAX_SYM_NAME - 1 ] = '\0';
+      }
+
+      $v_string( &line, {"  ["}, $unsigned(frame_number), {"] "}, $unsigned( symbol_addr, .base=16 ),
+                        {" "}, {function_name}, {"\n"} );
+      vg_str_flushfd( &line, fd );
+      frame_number ++;
+   }
+
+   SymCleanup(process);
+   exit(-1);
+}
+
+void _normal_exit(void)
+{
+   exit(0);
+}
diff --git a/source/foundation/foundation.h b/source/foundation/foundation.h
new file mode 100644 (file)
index 0000000..b682a41
--- /dev/null
@@ -0,0 +1,385 @@
+/* Voyager common application interface */
+#pragma once
+
+#define VG_PRE_MAIN \
+   _exit_init(); \
+   _log_init(); \
+   _options_init( argc, argv ); \
+   EVENT_CALL( OPTIONS ); \
+   _options_check_end(); 
+
+#define BYTES_KB( X ) X*1024
+#define BYTES_MB( X ) X*1024*1024
+#define BYTES_GB( X ) X*1024*1024*1024
+#define ARRAY_COUNT( X ) (sizeof((X))/sizeof((X)[0]))
+#define i8_MAX  0x7F
+#define u8_MAX  0XFF
+#define i16_MAX 0x7FFF
+#define u16_MAX 0xFFFF
+#define i32_MAX 0x7FFFFFFF
+#define u32_MAX 0xFFFFFFFF
+#define i64_MAX 0x7FFFFFFFFFFFFFFF
+#define u64_MAX 0xFFFFFFFFFFFFFFFF
+
+#define PAD_TO_4( X )  (((u32)X+0x3) & ~(u32)0x3)
+#define PAD_TO_8( X )  (((u32)X+0x7) & ~(u32)0x7)
+#define PAD_TO_16( X ) (((u32)X+0xf) & ~(u32)0xf)
+
+static inline f32 f32_min( f32 a, f32 b ){ return a < b? a: b; }
+static inline f32 f32_max( f32 a, f32 b ){ return a > b? a: b; }
+static inline i32 i32_min( i32 a, i32 b ){ return a < b? a: b; }
+static inline i32 i32_max( i32 a, i32 b ){ return a > b? a: b; }
+static inline i32 u32_min( u32 a, u32 b ){ return a < b? a: b; }
+static inline i32 u32_max( u32 a, u32 b ){ return a > b? a: b; }
+static inline i16 i16_min( i16 a, i16 b ){ return a < b? a: b; }
+static inline i16 i16_max( i16 a, i16 b ){ return a > b? a: b; }
+static inline f32 f32_clamp( f32 a, f32 min, f32 max ) { return f32_min( max, f32_max( a, min ) ); }
+static inline i16 i16_clamp( i16 a, i16 min, i16 max ){ return i16_min( max, i16_max( a, min ) ); }
+static inline i16 i32_clamp( i32 a, i32 min, i32 max ){ return i32_min( max, i32_max( a, min ) ); }
+static inline f32 f32_sign( f32 a ) { return a < 0.0f? -1.0f: 1.0f; }
+
+void _exit_init(void);
+void _fatal_exit(void);
+void _normal_exit(void);
+#define ASSERT_CRITICAL( CONDITION ) \
+   if( !(CONDITION) ) { $log( $fatal, {"(" $TO_STRING(CONDITION) ")" KRED " == 0" KNRM } ); _fatal_exit(); }
+
+/* Command line options
+ * ------------------------------------------------------------------------------------------------------------------ */
+void _options_init( i32 argc, const c8 *argv[] );
+void _options_check_end( void );
+b8 _option_flag( c8 c, const c8 *desc );
+const c8 *_option_argument( char c, const c8 *desc );
+b8 _option_long( c8 *name, const c8 *desc );
+const c8 *_option_long_argument( char *name, const c8 *desc );
+const c8 *_option(void);
+
+/* Heap allocation
+ * ------------------------------------------------------------------------------------------------------------------ */
+void *_heap_allocate( u64 size );
+void *_heap_reallocate( void *buf, u64 size );
+void  _heap_free( void *buf );
+
+void zero_buffer( void *buffer, u32 length );
+
+/* if max_length is 0, the operation runs until a 0 is found in the buffer */
+u32 buffer_djb2( const void *buffer, i32 max_length );
+b8 compare_buffers( const void *buffer_a, i32 a_max, const void *buffer_b, i32 b_max );
+i32 buffer_first_index( const void *buffer, u8 match, i32 max_length );
+i32 buffer_last_index( const void *buffer, u8 match, i32 max_length );
+b8 buffer_copy( const void *buffer_src, u32 src_length, void *buffer_dest, u32 dest_length );
+#define COPY( SRC, DST, LEN ) buffer_copy( SRC, LEN, DST, LEN )
+
+struct stretchy_allocator
+{
+   i32 count, element_size;
+   void *segments[26];
+};
+void stretchy_init( struct stretchy_allocator *stretchy, u32 element_size );
+void *stretchy_append( struct stretchy_allocator *stretchy );
+void *stretchy_get( struct stretchy_allocator *stretchy, u32 index );
+void stretchy_delete( struct stretchy_allocator *stretchy, u32 index );
+void stretchy_shrink( struct stretchy_allocator *stretchy );
+u32 stretchy_count( struct stretchy_allocator *stretchy );
+void stretchy_free( struct stretchy_allocator *stretchy );
+
+/* Stack
+ * ------------------------------------------------------------------------------------------------------------------ */
+struct stack_allocator
+{
+   u32 capacity, offset; /* bytes */
+   void *data;
+
+   void *check_pointer;
+};
+void  stack_init( struct stack_allocator *stack, void *buffer, u32 capacity, const c8 *debug_name );
+void *stack_allocate( struct stack_allocator *stack, u32 size, u32 alignment, const c8 *debug_name );
+void  stack_clear( struct stack_allocator *stack );
+void  stack_extend_last( struct stack_allocator *stack, void *check_pointer, i32 extra_bytes );
+u32   stack_offset( struct stack_allocator *stack, void *pointer );
+void *stack_pointer( struct stack_allocator *stack, u32 offset );
+
+u32  _start_temporary_frame(void);
+void _end_temporary_frame( u32 whence );
+void *_temporary_allocate( u32 bytes, u32 alignment );
+struct stack_allocator *_temporary_stack_allocator(void);
+
+/* Pool
+ * ------------------------------------------------------------------------------------------------------------------ */
+struct pool_node
+{
+   u16 l, r, refcount, unused0;
+};
+struct pool_chain
+{
+   u16 head, tail, count, unused0;
+};
+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 );
+void pool_switch( struct pool_allocator *pool, struct pool_chain *source, struct pool_chain *dest, u16 which );
+
+/* Queue
+ * ------------------------------------------------------------------------------------------------------------------ */
+struct queue_item
+{
+   u32 alloc_size,prev_size;
+   u8 data[];
+};
+struct queue_allocator
+{
+   void *buffer;
+   u32 size;
+   u32 head_offset, tail_offset;
+   u32 allocation_count;
+};
+void  queue_init( struct queue_allocator *queue, void *buffer, u32 buffer_size );
+void *queue_alloc( struct queue_allocator *queue, u32 size );
+void *queue_data( struct queue_allocator *queue, u32 offset );
+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 );
+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 );
+void  queue_clear( struct queue_allocator *queue );
+
+/* Stream
+ * ------------------------------------------------------------------------------------------------------------------ */
+enum stream_flag
+{
+   /* options */
+   k_stream_overflow_error = 0x1000,
+   k_stream_null_terminate = 0x2000,
+
+   /* memory type */
+   k_stream_buffer = 0x10,
+   k_stream_procedural = 0x20,
+   k_stream_stack = 0x40,
+   k_stream_posix = 0x80,
+   k_stream_auto = 0x100,
+
+   /* modes */
+   k_stream_write = 0x1,
+   k_stream_read  = 0x2
+};
+struct stream
+{
+   u32 flags, buffer_length, offset;
+   union
+   {
+      void *posix_stream;
+      u8 *buffer_write;
+      const u8 *buffer_read;
+      u32 (*write_procedure)( struct stream *stream, const void *buffer, u32 length );
+      u32 (*read_procedure)( struct stream *stream, void *buffer, u32 length );
+   };
+
+   union
+   {
+      struct stack_allocator *stack;
+      void *procedure_userdata;
+   };
+};
+
+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 );
+
+void stream_close( struct stream *stream );
+u32  stream_read( struct stream *stream, void *buffer, u32 length );
+void *stream_read_all( struct stream *stream, struct stack_allocator *stack, u32 alignment, u32 *length );
+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 );
+
+/* String (Stream subset)
+ * ------------------------------------------------------------------------------------------------------------------ */
+
+const c8 *string_get( struct stream *string );
+void string_clip( struct stream *string, i32 length );
+void string_append( struct stream *string, const c8 *substring, u32 length );
+void string_append_c8( struct stream *string, c8 c );
+void string_append_i64( struct stream *string, i64 value, u64 base );
+void string_append_i64r( struct stream *string, i64 value, u64 base, u32 width, c8 blank_c8acter );
+void string_append_u64( struct stream *string, u64 value, u64 base );
+void string_append_f64( struct stream *string, f64 value, u64 base, u32 decimal_places );
+
+struct v_string_arg
+{
+   const c8 *_string;
+   union
+   {
+      u64 _u64;
+      i64 _i64;
+      f64 _f64;
+   };
+
+   u32 type;
+   u32 base;
+   union
+   {
+   u32 decimals;
+   u32 length;
+   };
+};
+void v_string( struct stream *string, struct v_string_arg *argument_list );
+
+#define k_$end 99
+#define k_$string 0
+#define k_$unsigned 2
+#define k_$signed 3
+#define k_$float 4
+#define k_$errno 5
+
+#define $string( X, ... ) { X, __VA_ARGS__ }
+#define $unsigned( X, ... ) { .type=k_$unsigned, ._u64=X, __VA_ARGS__ }
+#define $signed( X, ... ) { .type=k_$signed, ._i64=X, __VA_ARGS__ }
+#define $float( X, ... ) { .type=k_$float, ._f64=X, __VA_ARGS__ }
+#define $errno( ... ) { .type=k_$errno, __VA_ARGS__ }
+#define $v_string( STRING, ... ) v_string( STRING, (struct v_string_arg[]){ __VA_ARGS__, {.type=k_$end} } )
+
+enum string_parse_result
+{
+   k_string_parse_ok,
+   k_string_parse_eof,
+   k_string_parse_error,
+   k_string_parse_whitespace
+};
+enum string_parse_result string_parse_c8 ( struct stream *string, c8 *c );
+enum string_parse_result string_parse_u64( struct stream *string, u64 *value );
+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
+ * ------------------------------------------------------------------------------------------------------------------ */
+
+#define KNRM  "\x1B[0m"
+#define KBLK  "\x1B[30m"
+#define KRED  "\x1B[31m"
+#define KGRN  "\x1B[32m"
+#define KYEL  "\x1B[33m"
+#define KBLU  "\x1B[34m"
+#define KMAG  "\x1B[35m"
+#define KCYN  "\x1B[36m"
+#define KWHT  "\x1B[37m"
+
+#define $low      0x1
+#define $info     0x2
+#define $ok       0x4
+#define $warning  0x8
+#define $error    0x10
+#define $fatal    0x20
+#define $shell    0x40
+#define $raw      0x80
+
+/* One day we will replace this shit with a good pre-processor. */
+#define $TO_STRING( S ) $TO_STRING_1( S )
+#define $TO_STRING_1( S ) #S
+#define $line __FILE__ ":" $TO_STRING(__LINE__)
+#define $log( C, ... ) { $v_string( _log_event( C, $line ), __VA_ARGS__ ); _log_end_event(); }
+
+struct stream *_log_event( u32 type, const c8 *code_location );
+void _log_init(void);
+void _log_end_event(void);
+void _log_add_listener( void (*fn)(const c8 *line, u32 length, u32 type), u32 filter, b8 accept_vt_codes );
+
+/* Keyvalues
+ * ------------------------------------------------------------------------------------------------------------------ */
+struct keyvalues
+{
+   struct stack_allocator *stack;
+   u32 root_offset;
+   u32 kv_page_offset, kv_page_count;
+};
+void keyvalues_init( struct keyvalues *kvs, struct stack_allocator *stack );
+void keyvalues_parse_stream( struct keyvalues *kvs, u32 root_offset, struct stream *in_stream );
+void keyvalues_write_stream( struct keyvalues *kvs, struct stream *out_stream, u32 node, u32 depth );
+
+b8 keyvalues_read_file( struct keyvalues *kvs, const char *path, struct stack_allocator *stack );
+b8 keyvalues_write_file( struct keyvalues *kvs, const char *path );
+
+enum keyvalue_type
+{
+   k_keyvalue_type_frame   = 0,
+   k_keyvalue_type_pair    = 1,
+   k_keyvalue_type_unused2 = 2,
+   k_keyvalue_type_unused3 = 3
+};
+u32 keyvalues_type( struct keyvalues *kvs, u32 kv_offset );
+c8 *keyvalues_key( struct keyvalues *kvs, u32 kv_offset, u32 *out_length );
+c8 *keyvalues_value( struct keyvalues *kvs, u32 kv_offset, u32 *out_length );
+
+u32 keyvalues_get( struct keyvalues *kvs, u32 root_offset, const c8 *key, b8 relative );
+u32 keyvalues_get_next( struct keyvalues *kvs, u32 kv_offset );
+u32 keyvalues_get_child( struct keyvalues *kvs, u32 root_offset, u32 index );
+
+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 );
+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 );
+u32 keyvalues_append_u32s( struct keyvalues *kvs, u32 parent_offset, const c8 *key, u32 *values, u32 len );
+u32 keyvalues_append_f32s( struct keyvalues *kvs, u32 parent_offset, const c8 *key, f32 *values, u32 len );
+
+/* IO */
+struct directory;
+
+enum directory_entry_type
+{
+   k_directory_entry_type_unknown,
+   k_directory_entry_type_file,
+   k_directory_entry_type_dir
+};
+
+enum directory_status
+{
+   k_directory_status_none,
+   k_directory_status_ok,
+   k_directory_status_path_too_long,
+   k_directory_status_invalid_path,
+   k_directory_status_is_file
+};
+
+struct directory *directory_open( const c8 *path, struct stack_allocator *stack );
+enum directory_status directory_status( struct directory *directory );
+const c8 *directory_entry_name( struct directory *directory );
+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;
+   i32 value;
+};
+void index_sort( struct sort_index *indices, u32 indice_count );
+
+/* THREAD STUFF
+ * ------------------------------- */
+void *_mutex_create(void);
+void _mutex_lock( void *mutex );
+void _mutex_unlock( void *mutex );
+
+void *_condition_create(void);
+void _condition_wait( void *condition, void *mutex );
+void _condition_signal( void *condition );
diff --git a/source/foundation/foundation.kv b/source/foundation/foundation.kv
new file mode 100644 (file)
index 0000000..83ff4ea
--- /dev/null
@@ -0,0 +1,52 @@
+include ""
+
+event
+{
+   name OPTIONS
+   prototype "void"
+}
+event
+{
+   name START
+   prototype "void"
+}
+
+add options.c
+add logging.c
+{
+   hook OPTIONS
+   function _log_options
+}
+
+add allocator_heap.c
+add allocator_stack.c
+add allocator_pool.c
+add allocator_queue.c
+add allocator_stretchy.c
+add stream.c
+add string.c
+add keyvalues.c
+add buffer_operations.c
+add temporary.c
+
+{
+   if linux
+   add io.c
+   add exit.c
+}
+
+{
+   if windows
+   add io_windows.c
+   add exit_windows.c
+}
+
+{
+   if SDL
+   add threads_sdl.c
+}
+
+{
+   if !SDL
+   add threads_c11.c
+}
index 3078d3014c130aebeae62e8b554708f8fb8dbdcb..64db62491f7c54c67d6f74001104e4b09e8b5884 100644 (file)
@@ -1,5 +1,4 @@
-#include "common_api.h"
-
+#include "foundation.h"
 #include <dirent.h>
 #include <stdio.h>
 #include <sys/stat.h>
@@ -47,7 +46,7 @@ const c8 *directory_entry_name( struct directory *directory )
    return directory->data->d_name;
 }
 
-static bool directory_skip( struct directory *directory )
+static b8 directory_skip( struct directory *directory )
 {
    const c8 *s = directory_entry_name( directory );
    if( s[0] == '.' )
@@ -63,7 +62,7 @@ static bool directory_skip( struct directory *directory )
    return 0;
 }
 
-bool directory_next_entry( struct directory *directory )
+b8 directory_next_entry( struct directory *directory )
 {
    while( (directory->data = readdir(directory->handle)) )
    {
index 21f108350ea4a1a9ca4bbf0a13dc1125ad93164d..0fb5a853972841ff9f91b359b5bb3956e56fec75 100644 (file)
@@ -1,4 +1,4 @@
-#include "common_api.h"
+#include "foundation.h"
 #include <fileapi.h>
 #include <shlobj.h>
 #include <stdio.h>
@@ -14,36 +14,41 @@ struct directory
 
 struct directory *directory_open( const c8 *path, struct stack_allocator *stack )
 {
-#error
+   ASSERT_CRITICAL(0);
+   return NULL;
 }
 
 enum directory_status directory_status( struct directory *directory )
 {
-#error
+   ASSERT_CRITICAL(0);
    return directory->status;
 }
 
 const c8 *directory_entry_name( struct directory *directory )
 {
-#error
+   ASSERT_CRITICAL(0);
+   return NULL;
 }
 
-static bool directory_skip( struct directory *directory )
+static b8 directory_skip( struct directory *directory )
 {
-#error
+   ASSERT_CRITICAL(0);
+   return 0;
 }
 
-bool directory_next_entry( struct directory *directory )
+b8 directory_next_entry( struct directory *directory )
 {
-#error
+   ASSERT_CRITICAL(0);
+   return 0;
 }
 
 enum directory_entry_type directory_entry_type( struct directory *directory )
 {
-#error
+   ASSERT_CRITICAL(0);
+   return 0;
 }
 
 void directory_close( struct directory *directory )
 {
-#error
+   ASSERT_CRITICAL(0);
 }
index d1466a1e326a488f139b6005ec8f3cfe4d31bd4a..5710f8102c1cc3a5c89d11c643332034f1b6aefc 100644 (file)
@@ -1,4 +1,4 @@
-#include "common_api.h"
+#include "foundation.h"
 #include <string.h>
 
 #define KV_PAGE_COUNT 32
@@ -169,7 +169,7 @@ u32 keyvalues_get_child( struct keyvalues *kvs, u32 root_offset, u32 index )
    else return 0;
 }
 
-u32 keyvalues_get( struct keyvalues *kvs, u32 root_offset, const c8 *key, bool relative )
+u32 keyvalues_get( struct keyvalues *kvs, u32 root_offset, const c8 *key, b8 relative )
 {
    if( root_offset == 0 )
       root_offset = kvs->root_offset;
@@ -204,9 +204,9 @@ u32 keyvalues_get( struct keyvalues *kvs, u32 root_offset, const c8 *key, bool r
    return 0;
 }
 
-bool keyvalues_read_i32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, i32 *default_values, i32 *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 )
 {
-   bool good = 1;
+   b8 good = 1;
 
    u32 value_length;
    char *value = keyvalues_value( kvs, keyvalues_get( kvs, root_offset, key, 0 ), &value_length );
@@ -228,9 +228,9 @@ bool keyvalues_read_i32s( struct keyvalues *kvs, u32 root_offset, const c8 *key,
    return good;
 }
 
-bool keyvalues_read_u32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, u32 *default_values, u32 *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 )
 {
-   bool good = 1;
+   b8 good = 1;
 
    u32 value_length;
    char *value = keyvalues_value( kvs, keyvalues_get( kvs, root_offset, key, 0 ), &value_length );
@@ -252,9 +252,9 @@ bool keyvalues_read_u32s( struct keyvalues *kvs, u32 root_offset, const c8 *key,
    return good;
 }
 
-bool keyvalues_read_f32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, f32 *default_values, f32 *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 )
 {
-   bool good = 1;
+   b8 good = 1;
 
    u32 value_length;
    char *value = keyvalues_value( kvs, keyvalues_get( kvs, root_offset, key, 0 ), &value_length );
@@ -372,7 +372,7 @@ void keyvalues_parse_stream( struct keyvalues *kvs, u32 root_offset, struct stre
       if( c == '\0' )
          break;
 
-      bool is_control_character = 0;
+      b8 is_control_character = 0;
       if( parser.token0_deliminator )
       {
          if( c == parser.token0_deliminator )
@@ -483,7 +483,7 @@ void keyvalues_parse_stream( struct keyvalues *kvs, u32 root_offset, struct stre
 static void kv_write_string( struct stream *out_stream, const c8 *string )
 {
    c8 delim=0;
-   bool passed = 0;
+   b8 passed = 0;
 
    for( u32 i=0; i<4096; i ++ )
    {
@@ -644,7 +644,7 @@ void vg_kv_write_tree( vg_kv_write *w, vg_kvs *kvs, u32 root_offset )
 }
 #endif
 
-bool keyvalues_read_file( struct keyvalues *kvs, const c8 *path, struct stack_allocator *stack )
+b8 keyvalues_read_file( struct keyvalues *kvs, const c8 *path, struct stack_allocator *stack )
 {
    struct stream stream;
    if( stream_open_file( &stream, path, k_stream_read ) )
@@ -658,7 +658,7 @@ bool keyvalues_read_file( struct keyvalues *kvs, const c8 *path, struct stack_al
 }
 
 #if 0
-bool vg_kv_write_file( vg_kvs *kvs, const c8 *path )
+b8 vg_kv_write_file( vg_kvs *kvs, const c8 *path )
 {
    vg_stream stream;
    if( vg_file_stream_open( &stream, path, VG_STREAM_WRITE ) )
index 91f70aa4cdad61e00d1895f81d28a0ac8d8de98e..17b7d6bdaaf61bb8806e71fc84b5acae5de32964 100644 (file)
@@ -1,7 +1,6 @@
-#include "common_api.h"
+#include "foundation.h"
 #include <stdio.h>
 #include <string.h>
-#include <threads.h>
 
 static struct stream _log_stream;
 static struct stream _stdout_stream;
@@ -12,8 +11,8 @@ static struct stream _journal_stream;
 
 struct
 {
-   bool writing_event;
-   mtx_t lock;
+   b8 writing_event;
+   void *lock;
 
    c8 line[ OUTPUT_LINE_LENGTH + VT_MAX_LENGTH + 2 /* \n\0 */ ];
    u32 line_length, line_length_visual, vt_filter;
@@ -23,7 +22,7 @@ struct
    struct log_line_callback
    {
       u32 filter;
-      bool accept_vt_codes;
+      b8 accept_vt_codes;
       void( *fn )( const c8 *line, u32 length, u32 type );
    }
    callbacks[ 8 ];
@@ -31,7 +30,7 @@ struct
 }
 _log;
 
-void _log_add_listener( void (*fn)(const c8 *line, u32 length, u32 type), u32 filter, bool accept_vt_codes )
+void _log_add_listener( void (*fn)(const c8 *line, u32 length, u32 type), u32 filter, b8 accept_vt_codes )
 {
    ASSERT_CRITICAL( _log.callback_count < ARRAY_COUNT( _log.callbacks ) );
    struct log_line_callback *cb = &_log.callbacks[ _log.callback_count ++ ];
@@ -67,7 +66,7 @@ static u32 _log_stream_passthrough( struct stream *stream, const void *buffer, u
 
       _log.line_length_visual ++;
 
-      bool wrap = (_log.line_length == (ARRAY_COUNT( _log.line )-VT_MAX_LENGTH-2));
+      b8 wrap = (_log.line_length == (ARRAY_COUNT( _log.line )-VT_MAX_LENGTH-2));
       if( wrap )
          _log.line[ _log.line_length ++ ] = '\n';
 
@@ -136,7 +135,7 @@ static u32 _log_stream_passthrough( struct stream *stream, const void *buffer, u
 
 struct stream *_log_event( u32 type, const c8 *code_location )
 {
-   ASSERT_CRITICAL( mtx_lock( &_log.lock ) == thrd_success );
+   _mutex_lock( _log.lock );
    _log.current_type = type;
 
    struct stream *output = &_log_stream;
@@ -172,12 +171,12 @@ void _log_end_event(void)
    _log.writing_event = 0;
    struct stream *output = &_log_stream;
    string_append( output, "\n", 0 );
-   ASSERT_CRITICAL( mtx_unlock( &_log.lock ) == thrd_success );
+   _mutex_unlock( _log.lock );
 }
 
 void _log_init(void)
 {
-   ASSERT_CRITICAL( mtx_init( &_log.lock, mtx_plain ) == thrd_success );
+   _log.lock = _mutex_create();
    _stdout_stream.posix_stream = stdout;
    _stdout_stream.offset = 0;
    _stdout_stream.buffer_length = 0;
index c2dfba4763b9221f04d429f5913d14b8e3246ca6..6bb869661367135cce7513595690c09b3d8ebc98 100644 (file)
@@ -1,4 +1,5 @@
-#include "common_api.h"
+#include "foundation.h"
+
 #define MAX_OPTIONS 32
 #define MAX_ARGUMENTS 32
 
@@ -133,7 +134,7 @@ void _options_check_end(void)
       _normal_exit();
    }
 
-   bool errors = 0;
+   b8 errors = 0;
    for( u32 i=0; i<_options.argument_count; i ++ )
    {
       struct argument *argument = &_options.arguments[ i ];
@@ -161,7 +162,7 @@ void _options_check_end(void)
       _normal_exit();
 }
 
-bool _option_flag( c8 c, const c8 *desc )
+b8 _option_flag( c8 c, const c8 *desc )
 {
    _option_register( (struct option){ .alias=NULL, .alias_c=c, .desc=desc, .type=k_option_type_flag } );
    for( u32 i=0; i<_options.argument_count; i ++ )
@@ -222,7 +223,7 @@ static struct argument *_argument_get_named( enum argument_type type, const c8 *
    return NULL;
 }
 
-bool _option_long( c8 *name, const c8 *desc ) 
+b8 _option_long( c8 *name, const c8 *desc ) 
 { 
    _option_register( (struct option){ .alias=name, .alias_c=0, .desc=desc, .type=k_option_type_long_flag } );
    return _argument_get_named( k_argument_long, name ) != NULL;
index fe72936e65cfaf7df1b823769dce5799054dc95c..cc4eeeddb27db83ac18fbe06cbccc0ddb22e41e3 100644 (file)
@@ -1,4 +1,4 @@
-#include "common_api.h"
+#include "foundation.h"
 #include <stdio.h>
 #include <errno.h>
 #include <string.h>
@@ -58,7 +58,7 @@ void stream_open_buffer_write( struct stream *stream, void *buffer, u32 buffer_l
    }
 }
 
-bool stream_open_file( struct stream *stream, const c8 *path, u32 flags )
+b8 stream_open_file( struct stream *stream, const c8 *path, u32 flags )
 {
 #if defined( VG_ENGINE )
    if( !_thread_has_flags( ENGINE_THREAD_BACKGROUND ) )
@@ -143,6 +143,18 @@ u32 stream_read( struct stream *stream, void *buffer, u32 length )
    return read_length;
 }
 
+void *stream_read_all( struct stream *stream, struct stack_allocator *stack, u32 alignment, u32 *length )
+{
+   stack_allocate( stack, 0, alignment, "Alignment" );
+   i32 space = ((i32)stack->capacity - (i32)alignment) - (i32)stack->offset;
+   void *buffer = stack_allocate( stack, space, alignment, "Remaining buffer" );
+   u32 l = stream_read( stream, buffer, space );
+   stack_extend_last( stack, buffer, -(i32)( space - l ) );
+
+   *length = l;
+   return buffer;
+}
+
 u32 stream_write( struct stream *stream, const void *buffer, u32 length )
 {
    ASSERT_CRITICAL( stream->flags & k_stream_write );
@@ -219,7 +231,7 @@ void stream_seek( struct stream *stream, u32 offset )
    stream->offset = offset;
 }
 
-bool stream_error( struct stream *stream )
+b8 stream_error( struct stream *stream )
 {
    return stream->flags & k_stream_overflow_error? 1: 0;
 }
index 3fba6709c0ccb9064140ce29bc761d449b151d3f..1639112ca3460c7c47606c3899e9b272df37c4d7 100644 (file)
@@ -1,4 +1,4 @@
-#include "common_api.h"
+#include "foundation.h"
 #include <errno.h>
 #include <string.h>
 
@@ -180,7 +180,7 @@ enum string_parse_result string_parse_u64( struct stream *string, u64 *value )
       info = string_parse_c8( string, &c );
 
    u64 result = 0;
-   bool got = 0;
+   b8 got = 0;
    while( info == k_string_parse_ok )
    {
       if( c >= '0' && c <= '9' )
@@ -219,7 +219,7 @@ enum string_parse_result string_parse_i64( struct stream *string, i64 *value )
       info = string_parse_c8( string, &c );
    }
 
-   bool got = 0;
+   b8 got = 0;
    while( info == k_string_parse_ok )
    {
       if( c >= '0' && c <= '9' )
@@ -261,7 +261,7 @@ enum string_parse_result string_parse_f64( struct stream *string, f64 *value )
       info = string_parse_c8( string, &c );
    }
 
-   bool got = 0, got_decimal = 0;
+   b8 got = 0, got_decimal = 0;
    while( info == k_string_parse_ok )
    {
       if( c == '.' )
index 55f1604a0d5dc157df38f2e303100d471895202a..cf2f9ffa04b4480e38f01a2ddd5d6095e05d3c9b 100644 (file)
@@ -1,9 +1,9 @@
-#include "common_api.h"
+#include "foundation.h"
 #define TEMP_STACK_MAX 64
 
-__thread struct stack_allocator _temp_stack;
-__thread u32 _temp_offsets[ TEMP_STACK_MAX ];
-__thread u32 _temp_stack_depth = 0;
+_Thread_local struct stack_allocator _temp_stack;
+_Thread_local u32 _temp_offsets[ TEMP_STACK_MAX ];
+_Thread_local u32 _temp_stack_depth = 0;
 
 u32 _start_temporary_frame(void)
 {
diff --git a/source/foundation/threads_c11.c b/source/foundation/threads_c11.c
new file mode 100644 (file)
index 0000000..1f3b598
--- /dev/null
@@ -0,0 +1,25 @@
+#include "SDL3/SDL.h"
+
+void *_mutex_create(void)
+{
+   return NULL;
+}
+void _mutex_lock( void *mutex )
+{
+}
+void _mutex_unlock( void *mutex )
+{
+}
+
+void *_condition_create(void)
+{
+   return NULL;
+}
+
+void _condition_wait( void *condition, void *mutex )
+{
+}
+
+void _condition_signal( void *condition )
+{
+}
diff --git a/source/foundation/threads_sdl.c b/source/foundation/threads_sdl.c
new file mode 100644 (file)
index 0000000..f8541bf
--- /dev/null
@@ -0,0 +1,34 @@
+#include "foundation.h"
+#include "SDL3/SDL.h"
+
+void *_mutex_create(void)
+{
+   void *mutex = SDL_CreateMutex();
+   ASSERT_CRITICAL( mutex );
+   return mutex;
+}
+void _mutex_lock( void *mutex )
+{
+   SDL_LockMutex( mutex );
+}
+void _mutex_unlock( void *mutex )
+{
+   SDL_UnlockMutex( mutex );
+}
+
+void *_condition_create(void)
+{
+   void *condition = SDL_CreateCondition();
+   ASSERT_CRITICAL( condition );
+   return condition;
+}
+
+void _condition_wait( void *condition, void *mutex )
+{
+   SDL_WaitCondition( condition, mutex );
+}
+
+void _condition_signal( void *condition )
+{
+   SDL_SignalCondition( condition );
+}
index 1441e8852a2f8c62f71bfa06812f43c76bc0f1f5..e6a0dcd5963666e4482499ac253c7a28b74615ff 100644 (file)
@@ -1,5 +1,5 @@
-#include "common_api.h"
-#include "graphics_api.h"
+#include "foundation.h"
+#include "vg_graphics.h"
 
 struct _font _font = 
 {
@@ -662,7 +662,7 @@ void _font_get_glyph_uvwh( u32 glyph, i16 out_uvwh[5] )
    _font_get_kerning( out_uvwh+2 );
 }
 
-bool _font_decode_bitmap( i16 uv[2] )
+b8 _font_decode_bitmap( i16 uv[2] )
 {
    u32 sheet_bit_index  = uv[1]*_font.bitmap_size[0] + uv[0],
        sheet_word_index = sheet_bit_index/32,
diff --git a/source/graphics/graphics.c b/source/graphics/graphics.c
deleted file mode 100644 (file)
index 9e1d1a2..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#include "common_api.h"
-#include "graphics_api.h"
-
-struct graphics_target *_graphics_target;
-
-void _graphics_set_target( struct graphics_target *target )
-{
-   _graphics_target = target;
-}
-
-void rect_clip( i16 child[4], i16 parent[4], i16 clipped[4] )
-{
-   i16 px = parent[0], py = parent[1], pw = parent[2], ph = parent[3];
-   clipped[0] = i16_clamp( child[0], px, px+pw );
-   clipped[1] = i16_clamp( child[1], py, py+ph );
-   clipped[2] = i16_clamp( child[0]+child[2], px, px+pw ) - clipped[0];
-   clipped[3] = i16_clamp( child[1]+child[3], py, py+ph ) - clipped[1];
-}
-
-void rect_copy( i16 a[4], i16 b[4] )
-{
-   for( u32 i=0; i<4; i ++ )
-      b[i] = a[i];
-}
-
-void rect_pad( i16 rect[4], i16 padding[2] )
-{
-   rect[0] += padding[0];
-   rect[1] += padding[1];
-   rect[2] -= padding[0]*2;
-   rect[3] -= padding[1]*2;
-}
-
-void rect_split( i16 rect[4], u32 vertical, i16 width, i16 gap, i16 out_left[4], i16 out_right[4] )
-{
-   u32 dir = vertical ^ 0x1;
-   if( width < 0 ) 
-      width = rect[ 2+dir ] + width;
-   i16 temp[4];
-   rect_copy( rect, temp );
-   out_left [ dir ]        = temp[ dir ];
-   out_right[ dir ]        = temp[ dir ] + width + (gap/2);
-   out_left [ vertical ]   = temp[ vertical ];
-   out_right[ vertical ]   = temp[ vertical ];
-   out_left [ 2+dir ]      = width                 - (gap/2);
-   out_right[ 2+dir ]      = temp[ 2+dir ] - width - (gap/2);
-   out_left [ 2+vertical ] = temp[ 2+vertical ];
-   out_right[ 2+vertical ] = temp[ 2+vertical ];
-}
-
-void rect_split_ratio( i16 rect[4], u32 vertical, f32 ratio, i16 gap, i16 out_left[4], i16 out_right[4] )
-{
-   i16 width = (f32)rect[ 2+(vertical^0x1) ] * ratio;
-   rect_split( rect, vertical, width, gap, out_left, out_right );
-}
diff --git a/source/graphics/graphics.kv b/source/graphics/graphics.kv
new file mode 100644 (file)
index 0000000..ab48eb4
--- /dev/null
@@ -0,0 +1,5 @@
+include ""
+add font.c
+add ui.c
+add vg_graphics.c
+add graphics_software.c
index 9ea00ff700c0e00f0db299713945b6a0ae3754a3..87c913e6110351f3bc2db98ab3c2c9bfb0f85b5f 100644 (file)
@@ -1,5 +1,5 @@
-#include "common_api.h"
-#include "graphics_api.h"
+#include "foundation.h"
+#include "vg_graphics.h"
 
 struct
 {
@@ -144,7 +144,7 @@ static void bresenham_init( struct bresenham *bresenham, i16 p0[2], i16 p1[2] )
    bresenham->p0[1] = p0[1];
 }
 
-static bool bresenham_iter( struct bresenham *bresenham, i16 out_pos[2] )
+static b8 bresenham_iter( struct bresenham *bresenham, i16 out_pos[2] )
 {
    if( bresenham->x > bresenham->delta[0] )
       return 0;
index 54bb3d5c2c173f76aedb9c87d0bbff265eb78b9e..a3160f4023415f700d9bbd7cc8acd8dec1a51184 100644 (file)
@@ -1,7 +1,7 @@
-#include "common_api.h"
-#include "graphics_api.h"
-#include "input_api.h"
-#include "maths/common_maths.h"
+#include "foundation.h"
+#include "vg_graphics.h"
+#include "vg_input.h"
+#include "common_maths.h"
 
 #define KEYBOARD_ALT      0x1
 #define KEYBOARD_SHIFT    0x2
@@ -13,7 +13,7 @@ struct
    //i16 clipping_area[4];
 
    i16 mouse_co[2], mouse_co_clicked[2];
-   bool mouse_went_in_click_hole;
+   b8 mouse_went_in_click_hole;
 
    /* internal state */
    enum ui_control_type
@@ -24,8 +24,8 @@ struct
       k_ui_control_button
    }
    active_control_type;
-   bool active_control_touched;
-   bool redraw;
+   b8 active_control_touched;
+   b8 redraw;
 
    union
    {
@@ -68,7 +68,7 @@ void _ui_set_encoding( enum ui_text_encoding encoding )
    _ui.text_encoding = encoding;
 }
 
-bool ui_inside_rect( i16 rect[4], i16 co[2] )
+b8 ui_inside_rect( i16 rect[4], i16 co[2] )
 {
    return (co[0] >= rect[0]) && (co[1] >= rect[1]) && (co[0] < rect[0]+rect[2]) && (co[1] < rect[1]+rect[3]);
 }
@@ -95,7 +95,7 @@ void ui_string_decode_init( struct ui_string_decoder *decoder, const c8 *string,
    decoder->encoding = encoding;
 }
 
-bool ui_string_decode( struct ui_string_decoder *decoder )
+b8 ui_string_decode( struct ui_string_decoder *decoder )
 {
    if( decoder->c == (u32)'\n' )
    {
@@ -173,7 +173,7 @@ void _ui_text( i16 rect[4], const c8 *text, enum ui_align align, union colour co
       text_root[1] += kerning[2];
    }
 
-   bool align_x = (align & (k_ui_align_x_center|k_ui_align_x_right))? 1:0;
+   b8 align_x = (align & (k_ui_align_x_center|k_ui_align_x_right))? 1:0;
    while( ui_string_decode( &decoder ) )
    {
       if( align_x && (align & (k_ui_align_x_center|k_ui_align_x_right)) )
@@ -214,7 +214,7 @@ 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;
 
-   bool availible = 0;
+   b8 availible = 0;
    if( _ui.active_control_type == k_ui_control_none )
       availible = 1;
    else if( _ui.active_control_type == k_ui_control_button )
@@ -285,7 +285,7 @@ u32 _ui_textbox_visual_to_buffer_index( i16 rect[4], i16 co[2] )
    i16 grid_co[2] = { (co[0] - rect[0]) / kerning[0], (co[1] - rect[1]) / kerning[1] };
 
    u32 index = 0;
-   bool loop; do
+   b8 loop; do
    {
       loop = ui_string_decode( &decoder );
       if( decoder.grid_co[1] <= grid_co[1] )
@@ -338,7 +338,7 @@ static void _ui_textbox_input_string( const c8 *input )
    _ui.controls.textbox.cursor_user = _ui.controls.textbox.cursor_pos;
 }
 
-static void _ui_textbox_set_cursor_user( i32 position, bool super )
+static void _ui_textbox_set_cursor_user( i32 position, b8 super )
 {
    _ui.controls.textbox.cursor_user = position;
    if( !super )
@@ -364,7 +364,7 @@ enum textbox_action _ui_textbox( i16 rect[4], c8 *text_buffer, u32 text_buffer_l
    i16 kerning[3];
    _font_get_kerning( kerning );
 
-   bool init = 0;
+   b8 init = 0;
    if( _ui_button( rect ) == k_button_click )
       init = 1;
 
@@ -410,7 +410,7 @@ enum textbox_action _ui_textbox( i16 rect[4], c8 *text_buffer, u32 text_buffer_l
              i1 = i32_max( _ui.controls.textbox.cursor_pos, _ui.controls.textbox.cursor_user );
          i16 user_co[2];
          
-         bool loop; do
+         b8 loop; do
          {  
             loop = ui_string_decode( &decoder );
             if( decoder.character_index == _ui.controls.textbox.cursor_user )
@@ -510,7 +510,7 @@ enum dropdown_action _ui_dropdown( i16 rect[4], struct ui_dropdown_option *optio
    return k_dropdown_none;
 }
 
-bool _ui_update(void)
+b8 _ui_update(void)
 {
    if( _input_button_down( k_input_action_ui_click, 0 ) )
    {
@@ -548,7 +548,7 @@ bool _ui_update(void)
             _ui.controls.textbox.action = k_textbox_enter;
       }
 
-      bool select_mode = _input_button( k_input_button_ui_select );
+      b8 select_mode = _input_button( k_input_button_ui_select );
 
       for( u8 i=0; i<_input_button_down( k_input_button_ui_home, 1 ); i ++ ) 
          _ui_textbox_set_cursor_user( 0, select_mode );
@@ -573,7 +573,7 @@ bool _ui_update(void)
             struct ui_string_decoder decoder, decoder1;
             ui_string_decode_init( &decoder, _ui.controls.textbox.text_buffer, _ui.controls.textbox.text_buffer_encoding );
             decoder1 = decoder;
-            bool loop; do
+            b8 loop; do
             {  
                loop = ui_string_decode( &decoder );
                if( decoder.character_index == _ui.controls.textbox.cursor_user )
@@ -583,7 +583,7 @@ bool _ui_update(void)
                   loop = 0;
                }
             } while( loop ); 
-            bool loop1; do
+            b8 loop1; do
             {  
                loop1 = ui_string_decode( &decoder1 );
                if( decoder1.grid_co[1] == user_co[1]-1 )
@@ -606,7 +606,7 @@ bool _ui_update(void)
             i32 result_index = -1;
             struct ui_string_decoder decoder;
             ui_string_decode_init( &decoder, _ui.controls.textbox.text_buffer, _ui.controls.textbox.text_buffer_encoding );
-            bool loop; do
+            b8 loop; do
             {  
                loop = ui_string_decode( &decoder );
                if( decoder.character_index == _ui.controls.textbox.cursor_user )
@@ -675,7 +675,7 @@ void _ui_draw(void)
    _graphics_fill_rect( (i16[]){ _ui.mouse_co[0],_ui.mouse_co[1], 3,3 }, (union colour){{255,255,255,255}} );
 }
 
-bool _ui_want_mouse( i16 area[4] )
+b8 _ui_want_mouse( i16 area[4] )
 {
    if( _ui.active_control_touched )
       return 1;
diff --git a/source/graphics/vg_graphics.c b/source/graphics/vg_graphics.c
new file mode 100644 (file)
index 0000000..705fc49
--- /dev/null
@@ -0,0 +1,55 @@
+#include "foundation.h"
+#include "vg_graphics.h"
+
+struct graphics_target *_graphics_target;
+
+void _graphics_set_target( struct graphics_target *target )
+{
+   _graphics_target = target;
+}
+
+void rect_clip( i16 child[4], i16 parent[4], i16 clipped[4] )
+{
+   i16 px = parent[0], py = parent[1], pw = parent[2], ph = parent[3];
+   clipped[0] = i16_clamp( child[0], px, px+pw );
+   clipped[1] = i16_clamp( child[1], py, py+ph );
+   clipped[2] = i16_clamp( child[0]+child[2], px, px+pw ) - clipped[0];
+   clipped[3] = i16_clamp( child[1]+child[3], py, py+ph ) - clipped[1];
+}
+
+void rect_copy( i16 a[4], i16 b[4] )
+{
+   for( u32 i=0; i<4; i ++ )
+      b[i] = a[i];
+}
+
+void rect_pad( i16 rect[4], i16 padding[2] )
+{
+   rect[0] += padding[0];
+   rect[1] += padding[1];
+   rect[2] -= padding[0]*2;
+   rect[3] -= padding[1]*2;
+}
+
+void rect_split( i16 rect[4], u32 vertical, i16 width, i16 gap, i16 out_left[4], i16 out_right[4] )
+{
+   u32 dir = vertical ^ 0x1;
+   if( width < 0 ) 
+      width = rect[ 2+dir ] + width;
+   i16 temp[4];
+   rect_copy( rect, temp );
+   out_left [ dir ]        = temp[ dir ];
+   out_right[ dir ]        = temp[ dir ] + width + (gap/2);
+   out_left [ vertical ]   = temp[ vertical ];
+   out_right[ vertical ]   = temp[ vertical ];
+   out_left [ 2+dir ]      = width                 - (gap/2);
+   out_right[ 2+dir ]      = temp[ 2+dir ] - width - (gap/2);
+   out_left [ 2+vertical ] = temp[ 2+vertical ];
+   out_right[ 2+vertical ] = temp[ 2+vertical ];
+}
+
+void rect_split_ratio( i16 rect[4], u32 vertical, f32 ratio, i16 gap, i16 out_left[4], i16 out_right[4] )
+{
+   i16 width = (f32)rect[ 2+(vertical^0x1) ] * ratio;
+   rect_split( rect, vertical, width, gap, out_left, out_right );
+}
diff --git a/source/graphics/vg_graphics.h b/source/graphics/vg_graphics.h
new file mode 100644 (file)
index 0000000..a3a685d
--- /dev/null
@@ -0,0 +1,178 @@
+/* Basic graphics
+ * ------------------------------------------------------------------------------------------------------------------ */
+
+enum graphics_mode
+{
+   k_graphics_mode_software,
+   k_graphics_mode_opengl
+};
+struct graphics_target 
+{
+   enum graphics_mode mode;
+   i16 size[2];
+   union
+   {
+      u8 *buffer;
+   };
+};
+
+extern struct graphics_target *_graphics_target;
+
+#pragma pack( push, 1 )
+union colour
+{
+   struct{ u8 r, g, b, a; };
+   u8 channels[4];
+   u32 word;
+};
+#pragma pack(pop)
+
+union colour graphics_lerp_colour( union colour c0, union colour c1, i32 d, i32 t );
+
+enum blending_mode
+{
+   k_blending_mode_mix_alpha,
+   k_blending_mode_set,
+   k_blending_mode_add,
+   k_blending_mode_subtract,
+   k_blending_mode_multiply,
+   k_blending_mode_pop = -1
+};
+
+void _graphics_set_target( struct graphics_target *target );
+void _graphics_viewport( i16 x, i16 y, i16 w, i16 h );
+void _graphics_pixel_unclipped( i16 co[2], union colour colour );
+void _graphics_pixel( i16 co[2], union colour colour );
+void _graphics_push_blendmode( enum blending_mode mode );
+
+void _graphics_fill_rect( i16 rect[4], union colour colour );
+void _graphics_line_rect( i16 rect[4], union colour colour );
+void _graphics_line( i16 p0[2], i16 p1[2], union colour colour );
+void _graphics_line2( i16 p0[2], i16 p1[2], union colour c0, union colour c1 );
+void _graphics_line_triangle( i16 p0[2], i16 p1[2], i16 p2[2], union colour colour );
+void _graphics_fill_triangle( i16 in_p0[2], i16 in_p1[2], i16 in_p2[2], union colour colour );
+void _graphics_glyph( i16 co[2], u32 glyph, i16 scale, union colour colour );
+
+void rect_clip( i16 child[4], i16 parent[4], i16 clipped[4] );
+void rect_copy( i16 a[4], i16 b[4] );
+void rect_pad( i16 rect[4], i16 padding[2] );
+void rect_split( i16 rect[4], u32 vertical, i16 width, i16 gap, i16 out_left[4], i16 out_right[4] );
+void rect_split_ratio( i16 rect[4], u32 vertical, f32 ratio, i16 gap, i16 out_left[4], i16 out_right[4] );
+b8 ui_inside_rect( i16 rect[4], i16 co[2] );
+
+/* Font
+ * ------------------------------------------------------------------------------------------------------------------ */
+
+struct font_face_descriptor
+{
+   i16 cw, ch, sx, sy, baseline;
+   i16 map[256][2];
+};
+
+struct _font
+{
+   enum font_face
+   {
+      k_font_face_normal=0,
+      k_font_face_larger,
+      k_font_face_title,
+      k_font_face_pop = -1
+   }
+   face_stack[8];
+   u32 face_stack_depth;
+
+   i16 bitmap_size[2];
+   u32 bitmap[];
+}
+extern _font;
+void _font_push_face( enum font_face face );
+void _font_get_kerning( i16 out_kerning[3] );
+void _font_get_glyph_uvwh( u32 glyph, i16 out_uvwh[5] );
+b8 _font_decode_bitmap( i16 uv[2] );
+
+/* Immediate mode UI
+ * ------------------------------------------------------------------------------------------------------------------ */
+void _ui_set_mouse( i16 x, i16 y );
+void _ui_get_mouse_co( i16 out_co[2] );
+void _ui_input_text( const c8 *text );
+b8 _ui_want_mouse( i16 area[4] );
+
+#define UI_PADDING_PX     8
+#define UI_ROW_PADDING_PX 18
+#define UI_HORIZONTAL 0
+#define UI_VERTICAL   1
+#define MOUSE_LEFT  0x1
+#define MOUSE_RIGHT 0x2
+
+#define UI_AUTOFOCUS 0x1
+#define UI_MULTILINE 0x2
+
+b8 _ui_update(void);
+void _ui_draw(void);
+
+/* Text */
+enum ui_text_encoding
+{
+   k_ui_text_default,
+   k_ui_text_utf8
+};
+void _ui_set_encoding( enum ui_text_encoding encoding );
+
+enum ui_align
+{
+   k_ui_align_x_left   = 0x1,
+   k_ui_align_x_right  = 0x2,
+   k_ui_align_x_center = 0x4,
+
+   k_ui_align_y_top    = 0x10,
+   k_ui_align_y_bottom = 0x20,
+   k_ui_align_y_center = 0x40,
+   k_ui_align_center   = k_ui_align_x_center|k_ui_align_y_center
+};
+void _ui_text( i16 rect[4], const c8 *text, enum ui_align align, union colour colour );
+
+/* Simple controls
+ * -------------------------------------------------  */
+i16 _ui_widget_row_height( i16 row_size );
+void _ui_widget_row( i16 inout_panel[4], i16 out_rect[4], i16 row_size );
+
+/* Button --------------------------------- */
+enum button_action
+{
+   k_button_none = 0,
+   k_button_click = 1,
+   k_button_repeat = 2,
+   k_button_hover  = 3,
+};
+enum button_action _ui_button( i16 rect[4] );
+
+/* Checkbox ------------------------------- */
+enum button_action _ui_checkbox( i16 rect[4], i32 *value );
+
+/* Dropdown ------------------------------- */
+enum dropdown_action
+{
+   k_dropdown_none,
+   k_dropdown_changed
+};
+struct ui_dropdown_option
+{
+   i32 value;
+   const c8 *display_name;
+};
+enum dropdown_action _ui_dropdown( i16 rect[4], struct ui_dropdown_option *options, u32 option_count, i32 *value );
+
+/* Slider ---------------------------------- */
+enum button_action _ui_slider( i16 rect[4], i32 vertical, f32 min, f32 max, f32 *value, f32 *out_t );
+
+/* Textbox --------------------------------- */
+enum textbox_action
+{
+   k_textbox_no_action,
+   k_textbox_enter,
+   k_textbox_input_up,
+   k_textbox_input_down,
+   k_textbox_changed,
+   k_textbox_escape
+};
+enum textbox_action _ui_textbox( i16 rect[4], c8 *text_buffer, u32 text_buffer_length, u32 lines, u32 flags );
index c1dcab529f57c2b69e3b38f797e9ac800ee293e6..5546428dc8a89d659af5b7fce9331315ae51aa47 100644 (file)
@@ -813,138 +813,7 @@ static void vg_capsule_inertia( f32 r, f32 h, f32 mass, m3x3f out_inertia )
    m3x3_setdiagonalv3( out_inertia, (v3f){ ixz, iy, ixz } );
 }
 
-/*
- * -----------------------------------------------------------------------------
- * Section 6.a            PSRNG and some distributions
- * -----------------------------------------------------------------------------
- */
-
-/* An implementation of the MT19937 Algorithm for the Mersenne Twister
- * by Evan Sultanik.  Based upon the pseudocode in: M. Matsumoto and
- * T. Nishimura, "Mersenne Twister: A 623-dimensionally
- * equidistributed uniform pseudorandom number generator," ACM
- * Transactions on Modeling and Computer Simulation Vol. 8, No. 1,
- * January pp.3-30 1998.
- *
- * http://www.sultanik.com/Mersenne_twister
- * https://github.com/ESultanik/mtwister/blob/master/mtwister.c
- */
-
-#define MT_UPPER_MASK         0x80000000
-#define MT_LOWER_MASK         0x7fffffff
-#define MT_TEMPERING_MASK_B   0x9d2c5680
-#define MT_TEMPERING_MASK_C   0xefc60000
-
-#define MT_STATE_VECTOR_LENGTH 624
-
-/* changes to STATE_VECTOR_LENGTH also require changes to this */
-#define MT_STATE_VECTOR_M      397 
-
-typedef struct vg_rand vg_rand;
-struct vg_rand {
-  u32 mt[MT_STATE_VECTOR_LENGTH];
-  i32 index;
-};
-
-static void vg_rand_seed( vg_rand *rand, unsigned long seed ) 
-{
-   /* set initial seeds to mt[STATE_VECTOR_LENGTH] using the generator
-    * from Line 25 of Table 1 in: Donald Knuth, "The Art of Computer
-    * Programming," Vol. 2 (2nd Ed.) pp.102.
-    */
-   rand->mt[0] = seed & 0xffffffff;
-   for( rand->index=1; rand->index<MT_STATE_VECTOR_LENGTH; rand->index++)
-      rand->mt[rand->index] = (6069 * rand->mt[rand->index-1]) & 0xffffffff;
-}
-
-/*
- * Generates a pseudo-randomly generated long.
- */
-static u32 vg_randu32( vg_rand *rand ) 
-{
-   u32 y;
-   /* mag[x] = x * 0x9908b0df for x = 0,1 */
-   static u32 mag[2] = {0x0, 0x9908b0df}; 
-   if( rand->index >= MT_STATE_VECTOR_LENGTH || rand->index < 0 )
-   {
-      /* generate STATE_VECTOR_LENGTH words at a time */
-      int kk;
-      if( rand->index >= MT_STATE_VECTOR_LENGTH+1 || rand->index < 0 )
-         vg_rand_seed( rand, 4357 );
-      for( kk=0; kk<MT_STATE_VECTOR_LENGTH-MT_STATE_VECTOR_M; kk++ )
-      {
-         y = (rand->mt[kk] & MT_UPPER_MASK) | (rand->mt[kk+1] & MT_LOWER_MASK);
-         rand->mt[kk] = rand->mt[kk+MT_STATE_VECTOR_M] ^ (y>>1) ^ mag[y & 0x1];
-      }
-      for( ; kk<MT_STATE_VECTOR_LENGTH-1; kk++ )
-      {
-         y = (rand->mt[kk] & MT_UPPER_MASK) | (rand->mt[kk+1] & MT_LOWER_MASK);
-         rand->mt[kk] = rand->mt[ kk+(MT_STATE_VECTOR_M-MT_STATE_VECTOR_LENGTH)] ^ (y >> 1) ^ mag[y & 0x1];
-      }
-      y = (rand->mt[MT_STATE_VECTOR_LENGTH-1] & MT_UPPER_MASK) | (rand->mt[0] & MT_LOWER_MASK);
-      rand->mt[MT_STATE_VECTOR_LENGTH-1] = rand->mt[MT_STATE_VECTOR_M-1] ^ (y >> 1) ^ mag[y & 0x1];
-      rand->index = 0;
-   }
-   y = rand->mt[rand->index++];
-   y ^= (y >> 11);
-   y ^= (y << 7) & MT_TEMPERING_MASK_B;
-   y ^= (y << 15) & MT_TEMPERING_MASK_C;
-   y ^= (y >> 18);
-   return y;
-}
-
-/*
- * Generates a pseudo-randomly generated f64 in the range [0..1].
- */
-static inline f64 vg_randf64( vg_rand *rand )
-{
-   return (f64)vg_randu32(rand)/(f64)0xffffffff;
-}
-
-static inline f64 vg_randf64_range( vg_rand *rand, f64 min, f64 max )
-{
-   return vg_lerp( min, max, (f64)vg_randf64(rand) );
-}
-
-static inline void vg_rand_dir( vg_rand *rand, v3f dir )
-{
-   dir[0] = vg_randf64(rand);
-   dir[1] = vg_randf64(rand);
-   dir[2] = vg_randf64(rand);
-
-   /* warning: *could* be 0 length.
-    * very unlikely.. 1 in (2^32)^3. but its mathematically wrong. */
-
-   v3_muls( dir, 2.0f, dir );
-   v3_sub( dir, (v3f){1.0f,1.0f,1.0f}, dir );
-   v3_normalize( dir );
-}
-
-static inline void vg_rand_sphere( vg_rand *rand, v3f co )
-{
-   vg_rand_dir(rand,co);
-   v3_muls( co, cbrtf( vg_randf64(rand) ), co );
-}
-
-static void vg_rand_disc( vg_rand *rand, v2f co )
-{
-   f32 a = vg_randf64(rand) * VG_TAUf;
-   co[0] = sinf(a);
-   co[1] = cosf(a); 
-   v2_muls( co, sqrtf( vg_randf64(rand) ), co );
-}
-
-static void vg_rand_cone( vg_rand *rand, v3f out_dir, f32 angle )
-{
-   f32 r = sqrtf(vg_randf64(rand)) * angle * 0.5f,
-       a = vg_randf64(rand) * VG_TAUf;
-
-   out_dir[0] = sinf(a) * sinf(r);
-   out_dir[1] = cosf(a) * sinf(r);
-   out_dir[2] = cosf(r);
-}
-
-static void vg_hsv_rgb( v3f hsv, v3f rgb )
+void vg_hsv_rgb( v3f hsv, v3f rgb )
 {
    i32 i = floorf( hsv[0]*6.0f );
    f32 v = hsv[2],
@@ -964,7 +833,7 @@ static void vg_hsv_rgb( v3f hsv, v3f rgb )
    }
 }
 
-static void vg_rgb_hsv( v3f rgb, v3f hsv )
+void vg_rgb_hsv( v3f rgb, v3f hsv )
 {
    f32 min = v3_minf( rgb ),
        max = v3_maxf( rgb ),
diff --git a/source/maths/common_maths.h b/source/maths/common_maths.h
new file mode 100644 (file)
index 0000000..9945016
--- /dev/null
@@ -0,0 +1,1405 @@
+#include <math.h>
+#define VG_PIf  3.141592653589793238462643
+#define VG_TAUf 6.283185307179586476925286 
+
+static inline u32 f32_raw_bits( f32 a ) { return *((u32 *)(&a)); }
+static inline b8 f32_is_infinite( f32 a ) { return ((f32_raw_bits(a)) & 0x7FFFFFFFU) == 0x7F800000U; }
+static inline b8 f32_is_nan( f32 a ) { return !f32_is_infinite(a) && ((f32_raw_bits(a)) & 0x7F800000U) == 0x7F800000U;}
+static inline b8 f32_is_number( f32 a ) { return ((f32_raw_bits(a)) & 0x7F800000U) != 0x7F800000U; }
+
+/* Scalars ---------------------------------------------------------------------------------------------------------- */
+
+static inline f32 f32_fractional_part( f32 a ) { return a - floorf( a ); }
+static inline f64 f64_fractional_part( f64 a ) { return a - floor( a ); }
+static inline f32 f32_degrees_to_radians( f32 degrees ) { return degrees * VG_PIf/180.0f; }
+static inline f32 f32_friction( f32 velocity, f32 F )
+{
+   return -f32_sign(velocity) * f32_min( F, fabsf(velocity) );
+}
+static inline f32 f32_lerp( f32 a, f32 b, f32 t ) { return a + t*(b-a); }
+static inline f64 f64_lerp( f64 a, f64 b, f64 t ) { return a + t*(b-a); }
+static inline void vg_slewf( f32 *a, f32 b, f32 speed )
+{
+   f32 d = f32_sign( b-*a ),
+       c = *a + d*speed;
+   *a = f32_min( b*d, c*d ) * d;
+}
+static inline f32 f32_smoothstep( f32 x ) { return x*x*(3.0f - 2.0f*x); }
+
+static inline f32 f32_radian_difference( f32 a, f32 b )
+{
+   f32 d = fmodf(b,VG_TAUf)-fmodf(a,VG_TAUf);
+   if( fabsf(d) > VG_PIf )
+      d = -f32_sign(d) * (VG_TAUf - fabsf(d));
+   return d;
+}
+static inline f32 f32_radian_lerp( f32 a, f32 b, f32 t )
+{
+   f32 d = fmodf( b-a, VG_TAUf ),
+       s = fmodf( 2.0f*d, VG_TAUf ) - d;
+   return a + s*t;
+}
+
+static inline u32 f32_quantize( f32 a, u32 bits, f32 min, f32 max )
+{
+   u32 mask = (0x1 << bits) - 1;
+   return f32_clamp((a - min) * ((f32)mask/(max-min)), 0.0f, mask );
+}
+static inline f32 f32_unquantize( u32 q, u32 bits, f32 min, f32 max )
+{
+   return min + (f32)q * ((max-min) / (f32)((0x1 << bits) - 1));
+}
+/* https://iquilezles.org/articles/functions/ 
+ *
+ * Use k to control the stretching of the function. Its maximum, which is 1, 
+ * happens at exactly x = 1/k. 
+ */
+static inline f32 f32_exponential_impulse( f32 x, f32 k )
+{
+    f32 h = k*x;
+    return h*expf(1.0f-h);
+}
+
+/* f32[2] ----------------------------------------------------------------------------------------------------------- */
+static inline void v2_copy( f32 a[2], f32 d[2] )
+{
+   d[0] = a[0]; 
+   d[1] = a[1];
+}
+static inline void v2_add( f32 a[2], f32 b[2], f32 d[2] )
+{
+   d[0] = a[0]+b[0]; 
+   d[1] = a[1]+b[1];
+}
+static inline void v2_sub( f32 a[2], f32 b[2], f32 d[2] )
+{
+   d[0] = a[0]-b[0]; 
+   d[1] = a[1]-b[1];
+}
+static inline void v2_min( f32 a[2], f32 b[2], f32 d[2] )
+{
+   d[0] = f32_min(a[0], b[0]); 
+   d[1] = f32_min(a[1], b[1]);
+}
+static inline void v2_max( f32 a[2], f32 b[2], f32 d[2] )
+{
+   d[0] = f32_max(a[0], b[0]); 
+   d[1] = f32_max(a[1], b[1]);
+}
+static inline f32 v2_dot( f32 a[2], f32 b[2] )
+{
+   return a[0] * b[0] + a[1] * b[1];
+}
+static inline f32 v2_cross( f32 a[2], f32 b[2] )
+{
+   return a[0]*b[1] - a[1]*b[0];
+}
+static inline void v2_abs( f32 a[2], f32 d[2] )
+{
+   d[0] = fabsf( a[0] ); 
+   d[1] = fabsf( a[1] );
+}
+static inline void v2_muls( f32 a[2], f32 s, f32 d[2] )
+{
+   d[0] = a[0]*s; 
+   d[1] = a[1]*s;
+}
+static inline void v2_divs( f32 a[2], f32 s, f32 d[2] )
+{
+   d[0] = a[0]/s; 
+   d[1] = a[1]/s;
+}
+static inline void v2_mul( f32 a[2], f32 b[2], f32 d[2] )
+{
+   d[0] = a[0]*b[0]; 
+   d[1] = a[1]*b[1];
+}
+static inline void v2_div( f32 a[2], f32 b[2], f32 d[2] )
+{
+   d[0] = a[0]/b[0]; 
+   d[1] = a[1]/b[1];
+}
+static inline void v2_muladd( f32 a[2], f32 b[2], f32 s[2], f32 d[2] )
+{
+   d[0] = a[0]+b[0]*s[0]; 
+   d[1] = a[1]+b[1]*s[1];
+}
+static inline void v2_muladds( f32 a[2], f32 b[2], f32 s, f32 d[2] )
+{
+   d[0] = a[0]+b[0]*s; 
+   d[1] = a[1]+b[1]*s;
+}
+static inline f32 v2_length2( f32 a[2] )
+{
+   return a[0]*a[0] + a[1]*a[1];
+}
+static inline f32 v2_length( f32 a[2] )
+{
+   return sqrtf( v2_length2( a ) );
+}
+static inline f32 v2_dist2( f32 a[2], f32 b[2] )
+{
+   f32 delta[2];
+   v2_sub( a, b, delta );
+   return v2_length2( delta );
+}
+static inline f32 v2_dist( f32 a[2], f32 b[2] )
+{
+   return sqrtf( v2_dist2( a, b ) );
+}
+static inline void v2_lerp( f32 a[2], f32 b[2], f32 t, f32 d[2] )
+{
+   d[0] = a[0] + t*(b[0]-a[0]);
+   d[1] = a[1] + t*(b[1]-a[1]);
+}
+static inline void v2_normalize( f32 a[2] )
+{
+   v2_muls( a, 1.0f / v2_length( a ), a );
+}
+static inline void v2_normalize_clamp( f32 a[2] )
+{
+   f32 l2 = v2_length2( a );
+   if( l2 > 1.0f )
+      v2_muls( a, 1.0f/sqrtf(l2), a );
+}
+static inline void v2_floor( f32 a[2], f32 b[2] )
+{
+   b[0] = floorf( a[0] );
+   b[1] = floorf( a[1] );
+}
+static inline void v2_fill( f32 a[2], f32 v )
+{
+   a[0] = v;
+   a[1] = v;
+}
+static inline void v2_copysign( f32 a[2], f32 b[2] )
+{
+   a[0] = copysignf( a[0], b[0] );
+   a[1] = copysignf( a[1], b[1] );
+}
+/* integer variants  */
+static inline void v2i_copy( i32 a[2], i32 b[2] )
+{
+   b[0] = a[0]; 
+   b[1] = a[1];
+}
+static inline int v2i_eq( i32 a[2], i32 b[2] )
+{
+   return ((a[0] == b[0]) && (a[1] == b[1]));
+}
+static inline void v2i_add( i32 a[2], i32 b[2], i32 d[2] )
+{
+   d[0] = a[0]+b[0]; 
+   d[1] = a[1]+b[1];
+}
+static inline void v2i_sub( i32 a[2], i32 b[2], i32 d[2] )
+{
+   d[0] = a[0]-b[0]; 
+   d[1] = a[1]-b[1];
+}
+
+/* f32[3] ----------------------------------------------------------------------------------------------------------- */
+static inline b8 v3_is_numbers( f32 a[3] )
+{
+   return f32_is_number( a[0] ) && f32_is_number( a[1] ) && f32_is_number( a[2] );
+}
+static inline void v3_copy( f32 a[3], f32 b[3] )
+{
+   b[0] = a[0]; b[1] = a[1]; b[2] = a[2];
+}
+static inline void v3_add( f32 a[3], f32 b[3], f32 d[3] )
+{
+   d[0] = a[0]+b[0]; 
+   d[1] = a[1]+b[1]; 
+   d[2] = a[2]+b[2];
+}
+static inline void v3i_add( i32 a[3], i32 b[3], i32 d[3] )
+{
+   d[0] = a[0]+b[0]; 
+   d[1] = a[1]+b[1]; 
+   d[2] = a[2]+b[2];
+}
+static inline void v3_sub( f32 a[3], f32 b[3], f32 d[3] )
+{
+   d[0] = a[0]-b[0]; 
+   d[1] = a[1]-b[1]; 
+   d[2] = a[2]-b[2];
+}
+static inline void v3i_sub( i32 a[3], i32 b[3], i32 d[3] )
+{
+   d[0] = a[0]-b[0]; 
+   d[1] = a[1]-b[1]; 
+   d[2] = a[2]-b[2];
+}
+static inline void v3_mul( f32 a[3], f32 b[3], f32 d[3] )
+{
+   d[0] = a[0]*b[0];
+   d[1] = a[1]*b[1];
+   d[2] = a[2]*b[2];
+}
+static inline void v3_div( f32 a[3], f32 b[3], f32 d[3] )
+{
+   d[0] = b[0]!=0.0f? a[0]/b[0]: INFINITY;
+   d[1] = b[1]!=0.0f? a[1]/b[1]: INFINITY;
+   d[2] = b[2]!=0.0f? a[2]/b[2]: INFINITY;
+}
+static inline void v3_muls( f32 a[3], f32 s, f32 d[3] )
+{
+   d[0] = a[0]*s;
+   d[1] = a[1]*s;
+   d[2] = a[2]*s;
+}
+static inline void v3_fill( f32 a[3], f32 v )
+{
+   a[0] = v;
+   a[1] = v;
+   a[2] = v;
+}
+static inline void v4_fill( f32 a[4], f32 v )
+{
+   a[0] = v;
+   a[1] = v;
+   a[2] = v;
+   a[3] = v;
+}
+static inline void v3_divs( f32 a[3], f32 s, f32 d[3] )
+{
+   if( s == 0.0f )
+      v3_fill( d, INFINITY );
+   else
+   {
+      d[0] = a[0]/s; 
+      d[1] = a[1]/s; 
+      d[2] = a[2]/s;
+   }
+}
+static inline void v3_muladds( f32 a[3], f32 b[3], f32 s, f32 d[3] )
+{
+   d[0] = a[0]+b[0]*s; 
+   d[1] = a[1]+b[1]*s; 
+   d[2] = a[2]+b[2]*s;
+}
+static inline void v3_muladd( f32 a[3], f32 b[3], f32 s[3], f32 d[3] )
+{
+   d[0] = a[0]+b[0]*s[0]; 
+   d[1] = a[1]+b[1]*s[1];
+   d[2] = a[2]+b[2]*s[2];
+}
+static inline f32 v3_dot( f32 a[3], f32 b[3] )
+{
+   return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
+}
+static inline void v3_cross( f32 a[3], f32 b[3], f32 dest[3] )
+{
+   f32 d[3];
+   d[0] = a[1]*b[2] - a[2]*b[1];
+   d[1] = a[2]*b[0] - a[0]*b[2];
+   d[2] = a[0]*b[1] - a[1]*b[0];
+   v3_copy( d, dest );
+}
+static inline f32 v3_length2( f32 a[3] )
+{
+   return v3_dot( a, a );
+}
+static inline f32 v3_length( f32 a[3] )
+{
+   return sqrtf( v3_length2( a ) );
+}
+static inline f32 v3_dist2( f32 a[3], f32 b[3] )
+{
+   f32 delta[3];
+   v3_sub( a, b, delta );
+   return v3_length2( delta );
+}
+static inline f32 v3_dist( f32 a[3], f32 b[3] )
+{
+   return sqrtf( v3_dist2( a, b ) );
+}
+static inline void v3_normalize( f32 a[3] )
+{
+   v3_muls( a, 1.f / v3_length( a ), a );
+}
+static inline void v3_lerp( f32 a[3], f32 b[3], f32 t, f32 d[3] )
+{
+   d[0] = a[0] + t*(b[0]-a[0]);
+   d[1] = a[1] + t*(b[1]-a[1]);
+   d[2] = a[2] + t*(b[2]-a[2]);
+}
+static inline void v3_min( f32 a[3], f32 b[3], f32 d[3] )
+{
+   d[0] = f32_min(a[0], b[0]);
+   d[1] = f32_min(a[1], b[1]);
+   d[2] = f32_min(a[2], b[2]);
+}
+static inline void v3_max( f32 a[3], f32 b[3], f32 d[3] )
+{
+   d[0] = f32_max(a[0], b[0]);
+   d[1] = f32_max(a[1], b[1]);
+   d[2] = f32_max(a[2], b[2]);
+}
+static inline f32 v3_min_element( f32 a[3] )
+{
+   return f32_min( f32_min( a[0], a[1] ), a[2] );
+}
+static inline f32 v3_max_element( f32 a[3] )
+{
+   return f32_max( f32_max( a[0], a[1] ), a[2] );
+}
+static inline void v3_floor( f32 a[3], f32 b[3] )
+{
+   b[0] = floorf( a[0] );
+   b[1] = floorf( a[1] );
+   b[2] = floorf( a[2] );
+}
+static inline void v3_ceil( f32 a[3], f32 b[3] )
+{
+   b[0] = ceilf( a[0] );
+   b[1] = ceilf( a[1] );
+   b[2] = ceilf( a[2] );
+}
+static inline void v3_negate( f32 a[3], f32 b[3] )
+{
+   b[0] = -a[0];
+   b[1] = -a[1];
+   b[2] = -a[2];
+}
+static inline void v3_rotate( f32 v[3], f32 angle, f32 axis[3], f32 d[3] ) 
+{
+  f32 v1[3], v2[3], k[3], c, s;
+  c = cosf( angle );
+  s = sinf( angle );
+  v3_copy( axis, k );
+  v3_normalize( k );
+  v3_muls( v, c, v1 );
+  v3_cross( k, v, v2 );
+  v3_muls( v2, s, v2 );
+  v3_add( v1, v2, v1 );
+  v3_muls( k, v3_dot(k, v) * (1.0f - c), v2);
+  v3_add( v1, v2, d );
+}
+static inline void v3_tangent_basis( f32 n[3], f32 out_tx[3], f32 out_ty[3] )
+{
+   /* Compute tangent basis (box2d) */
+   if( fabsf( n[0] ) >= 0.57735027f )
+   {
+      out_tx[0] =  n[1];
+      out_tx[1] = -n[0];
+      out_tx[2] =  0.0f;
+   }
+   else
+   {
+      out_tx[0] =  0.0f;
+      out_tx[1] =  n[2];
+      out_tx[2] = -n[1];
+   }
+   v3_normalize( out_tx );
+   v3_cross( n, out_tx, out_ty );
+}
+static inline void v3_vector_to_angles( f32 v[3], f32 out_angles[3] )
+{
+   f32 yaw = atan2f( v[0], -v[2] ),
+     pitch = atan2f( -v[1], sqrtf( v[0]*v[0] + v[2]*v[2] ) );
+   out_angles[0] = yaw;
+   out_angles[1] = pitch;
+   out_angles[2] = 0.0f;
+}
+static inline void v3_angles_to_vector( f32 angles[3], f32 out_vector[3] )
+{
+   out_vector[0] =  sinf( angles[0] ) * cosf( angles[1] );
+   out_vector[1] = -sinf( angles[1] );
+   out_vector[2] = -cosf( angles[0] ) * cosf( angles[1] );
+}
+
+/* f32[4] ----------------------------------------------------------------------------------------------------------- */
+static inline void v4_copy( f32 a[4], f32 d[4] )
+{
+   d[0] = a[0]; 
+   d[1] = a[1]; 
+   d[2] = a[2]; 
+   d[3] = a[3];
+}
+static inline void v4_add( f32 a[4], f32 b[3], f32 d[4] )
+{
+   d[0] = a[0]+b[0]; 
+   d[1] = a[1]+b[1];
+   d[2] = a[2]+b[2];
+   d[3] = a[3]+b[3];
+}
+static inline void v4_muls( f32 a[4], f32 s, f32 d[4] )
+{
+   d[0] = a[0]*s; 
+   d[1] = a[1]*s;
+   d[2] = a[2]*s;
+   d[3] = a[3]*s;
+}
+static inline void v4_muladds( f32 a[4], f32 b[4], f32 s, f32 d[4] )
+{
+   d[0] = a[0]+b[0]*s; 
+   d[1] = a[1]+b[1]*s;
+   d[2] = a[2]+b[2]*s;
+   d[3] = a[3]+b[3]*s;
+}
+static inline void v4_lerp( f32 a[4], f32 b[4], f32 t, f32 d[4] )
+{
+   d[0] = a[0] + t*(b[0]-a[0]);
+   d[1] = a[1] + t*(b[1]-a[1]);
+   d[2] = a[2] + t*(b[2]-a[2]);
+   d[3] = a[3] + t*(b[3]-a[3]);
+}
+static inline f32 v4_dot( f32 a[4], f32 b[4] )
+{
+   return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3];
+}
+static inline f32 v4_length( f32 a[4] )
+{
+   return sqrtf( v4_dot(a,a) );
+}
+
+/* f32[4] ----------------------------------------------------------------------------------------------------------- */
+static inline void q_identity( f32 q[4] )
+{
+   q[0] = 0.0f; 
+   q[1] = 0.0f; 
+   q[2] = 0.0f; 
+   q[3] = 1.0f;
+}
+static inline void q_axis_angle( f32 q[4], f32 axis[3], f32 angle )
+{
+   f32 a = angle*0.5f,
+       c = cosf(a),
+       s = sinf(a);
+   q[0] = s*axis[0];
+   q[1] = s*axis[1];
+   q[2] = s*axis[2];
+   q[3] = c;
+}
+static inline void q_mul( f32 q[4], f32 q1[4], f32 d[4] )
+{
+   f32 t[4];
+   t[0] = q[3]*q1[0] + q[0]*q1[3] + q[1]*q1[2] - q[2]*q1[1];
+   t[1] = q[3]*q1[1] - q[0]*q1[2] + q[1]*q1[3] + q[2]*q1[0];
+   t[2] = q[3]*q1[2] + q[0]*q1[1] - q[1]*q1[0] + q[2]*q1[3];
+   t[3] = q[3]*q1[3] - q[0]*q1[0] - q[1]*q1[1] - q[2]*q1[2];
+   v4_copy( t, d );
+}
+static inline void q_normalize( f32 q[4] )
+{
+   f32 l2 = v4_dot(q,q);
+   if( l2 < 0.00001f ) 
+      q_identity( q );
+   else 
+   {
+      f32 s = 1.0f/sqrtf(l2);
+      q[0] *= s;
+      q[1] *= s;
+      q[2] *= s;
+      q[3] *= s;
+   }
+}
+static inline void q_inv( f32 q[4], f32 d[4] )
+{
+   f32 s = 1.0f / v4_dot(q,q);
+   d[0] = -q[0]*s;
+   d[1] = -q[1]*s;
+   d[2] = -q[2]*s;
+   d[3] =  q[3]*s;
+}
+static inline void q_nlerp( f32 a[4], f32 b[4], f32 t, f32 d[4] )
+{
+   if( v4_dot(a,b) < 0.0f )
+   {
+      f32 c[4];
+      v4_muls( b, -1.0f, c );
+      v4_lerp( a, c, t, d );
+   }
+   else
+      v4_lerp( a, b, t, d );
+   q_normalize( d );
+}
+static inline void q_m3x3( f32 q[4], f32 d[3][3] )
+{
+   f32 l = v4_length(q),
+       s = l > 0.0f? 2.0f/l: 0.0f,
+      xx = s*q[0]*q[0], xy = s*q[0]*q[1], wx = s*q[3]*q[0],
+      yy = s*q[1]*q[1], yz = s*q[1]*q[2], wy = s*q[3]*q[1],
+      zz = s*q[2]*q[2], xz = s*q[0]*q[2], wz = s*q[3]*q[2];
+   d[0][0] = 1.0f - yy - zz;
+   d[1][1] = 1.0f - xx - zz;
+   d[2][2] = 1.0f - xx - yy;
+   d[0][1] = xy + wz;
+   d[1][2] = yz + wx;
+   d[2][0] = xz + wy;
+   d[1][0] = xy - wz;
+   d[2][1] = yz - wx;
+   d[0][2] = xz - wy;
+}
+static inline void q_mulv( f32 q[4], f32 v[3], f32 d[3] )
+{
+   f32 v1[3], v2[3];
+   v3_muls( q, 2.0f*v3_dot(q,v), v1 );
+   v3_muls( v, q[3]*q[3] - v3_dot(q,q), v2 );
+   v3_add( v1, v2, v1 );
+   v3_cross( q, v, v2 );
+   v3_muls( v2, 2.0f*q[3], v2 );
+   v3_add( v1, v2, d );
+}
+static inline f32 q_dist( f32 q0[4], f32 q1[4] )
+{
+   return acosf( 2.0f * v4_dot(q0,q1) -1.0f );
+}
+static inline void q_copy( f32 a[4], f32 b[4] )
+{
+   b[0] = a[0]; 
+   b[1] = a[1]; 
+   b[2] = a[2]; 
+   b[3] = a[3];
+}
+static inline void m2x2_copy( f32 a[2][2], f32 b[2][2] )
+{
+   v2_copy( a[0], b[0] );
+   v2_copy( a[1], b[1] );
+}
+static inline void m2x2_identity( f32 a[2][2] )
+{
+   m2x2_copy( (f32[2][2]){{1,0},
+                          {0,1}}, a );
+}
+static inline void m2x2_create_rotation( f32 a[2][2], f32 theta )
+{
+   f32 s = sinf( theta ),
+       c = cosf( theta );
+   a[0][0] =  c;
+   a[0][1] = -s;
+   a[1][0] =  s;
+   a[1][1] =  c;
+}
+static inline void m2x2_mulv( f32 m[2][2], f32 v[2], f32 d[2] )
+{
+   f32 res[2];
+   res[0] = m[0][0]*v[0] + m[1][0]*v[1];
+   res[1] = m[0][1]*v[0] + m[1][1]*v[1];
+   v2_copy( res, d );
+}
+
+/* f32[3][3] -------------------------------------------------------------------------------------------------------- */
+static inline void euler_m3x3( f32 angles[3], f32 d[3][3] )
+{
+   f32 cosY = cosf( angles[0] ),
+       sinY = sinf( angles[0] ),
+       cosP = cosf( angles[1] ),
+       sinP = sinf( angles[1] ),
+       cosR = cosf( angles[2] ),
+       sinR = sinf( angles[2] );
+   d[2][0] = -sinY * cosP;
+   d[2][1] =  sinP;
+   d[2][2] =  cosY * cosP;
+   d[0][0] =  cosY * cosR;
+   d[0][1] =  sinR;
+   d[0][2] =  sinY * cosR;
+   v3_cross( d[0], d[2], d[1] );
+}
+static inline void m3x3_q( f32 m[3][3], f32 q[4] )
+{
+   f32 diag, r, rinv;
+   diag = m[0][0] + m[1][1] + m[2][2];
+   if( diag >= 0.0f )
+   {
+      r    = sqrtf( 1.0f + diag );
+      rinv = 0.5f / r;
+      q[0] = rinv * (m[1][2] - m[2][1]);
+      q[1] = rinv * (m[2][0] - m[0][2]);
+      q[2] = rinv * (m[0][1] - m[1][0]);
+      q[3] = r    * 0.5f;
+   } 
+   else if( m[0][0] >= m[1][1] && m[0][0] >= m[2][2] )
+   {
+      r    = sqrtf( 1.0f - m[1][1] - m[2][2] + m[0][0] );
+      rinv = 0.5f / r;
+      q[0] = r    * 0.5f;
+      q[1] = rinv * (m[0][1] + m[1][0]);
+      q[2] = rinv * (m[0][2] + m[2][0]);
+      q[3] = rinv * (m[1][2] - m[2][1]);
+   } 
+   else if( m[1][1] >= m[2][2] )
+   {
+      r    = sqrtf( 1.0f - m[0][0] - m[2][2] + m[1][1] );
+      rinv = 0.5f / r;
+      q[0] = rinv * (m[0][1] + m[1][0]);
+      q[1] = r    * 0.5f;
+      q[2] = rinv * (m[1][2] + m[2][1]);
+      q[3] = rinv * (m[2][0] - m[0][2]);
+   } 
+   else 
+   {
+      r    = sqrtf( 1.0f - m[0][0] - m[1][1] + m[2][2] );
+      rinv = 0.5f / r;
+      q[0] = rinv * (m[0][2] + m[2][0]);
+      q[1] = rinv * (m[1][2] + m[2][1]);
+      q[2] = r    * 0.5f;
+      q[3] = rinv * (m[0][1] - m[1][0]);
+   }
+}
+/* a X b == [b]T a == ...*/
+static inline void m3x3_skew_symetric( f32 a[3][3], f32 v[3] )
+{
+   a[0][0] =  0.0f;
+   a[0][1] =  v[2];
+   a[0][2] = -v[1];
+   a[1][0] = -v[2];
+   a[1][1] =  0.0f;
+   a[1][2] =  v[0];
+   a[2][0] =  v[1];
+   a[2][1] = -v[0];
+   a[2][2] =  0.0f;
+}
+/* aka kronecker product */
+static inline void m3x3_outer_product( f32 out_m[3][3], f32 a[3], f32 b[3] )
+{
+   out_m[0][0] = a[0]*b[0];
+   out_m[0][1] = a[0]*b[1];
+   out_m[0][2] = a[0]*b[2];
+   out_m[1][0] = a[1]*b[0];
+   out_m[1][1] = a[1]*b[1];
+   out_m[1][2] = a[1]*b[2];
+   out_m[2][0] = a[2]*b[0];
+   out_m[2][1] = a[2]*b[1];
+   out_m[2][2] = a[2]*b[2];
+}
+static inline void m3x3_add( f32 a[3][3], f32 b[3][3], f32 d[3][3] )
+{
+   v3_add( a[0], b[0], d[0] );
+   v3_add( a[1], b[1], d[1] );
+   v3_add( a[2], b[2], d[2] );
+}
+static inline void m3x3_sub( f32 a[3][3], f32 b[3][3], f32 d[3][3] )
+{
+   v3_sub( a[0], b[0], d[0] );
+   v3_sub( a[1], b[1], d[1] );
+   v3_sub( a[2], b[2], d[2] );
+}
+static inline void m3x3_copy( f32 a[3][3], f32 b[3][3] )
+{
+   v3_copy( a[0], b[0] );
+   v3_copy( a[1], b[1] );
+   v3_copy( a[2], b[2] );
+}
+static inline void m3x3_identity( f32 a[3][3] )
+{
+   m3x3_copy( (f32[3][3]){{1,0,0},
+                          {0,1,0},
+                          {0,0,1}}, a );
+}
+static inline void m3x3_zero( f32 a[3][3] )
+{
+   m3x3_copy( (f32[3][3]){{0,0,0},
+                          {0,0,0},
+                          {0,0,0}}, a );
+}
+static inline void m3x3_diagonal( f32 out_a[3][3], f32 t )
+{
+   m3x3_identity( out_a );
+   out_a[0][0] = t;
+   out_a[1][1] = t;
+   out_a[2][2] = t;
+}
+static inline void m3x3_set_diagonal_v3( f32 a[3][3], f32 v[3] )
+{
+   a[0][0] = v[0];
+   a[1][1] = v[1];
+   a[2][2] = v[2];
+}
+static inline void m3x3_inv( f32 src[3][3], f32 dest[3][3] )
+{
+   f32 a = src[0][0], b = src[0][1], c = src[0][2],
+       d = src[1][0], e = src[1][1], f = src[1][2],
+       g = src[2][0], h = src[2][1], i = src[2][2],
+     det = 1.f / (+a*(e*i-h*f)
+                  -b*(d*i-f*g)
+                  +c*(d*h-e*g));
+   dest[0][0] =  (e*i-h*f)*det;
+   dest[0][1] = -(b*i-c*h)*det;
+   dest[0][2] =  (b*f-c*e)*det;
+   dest[1][0] = -(d*i-f*g)*det;
+   dest[1][1] =  (a*i-c*g)*det;
+   dest[1][2] = -(a*f-d*c)*det;
+   dest[2][0] =  (d*h-g*e)*det;
+   dest[2][1] = -(a*h-g*b)*det;
+   dest[2][2] =  (a*e-d*b)*det;
+}
+static inline f32 m3x3_det( f32 m[3][3] )
+{
+   return   m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
+          - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])
+          + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
+}
+static inline void m3x3_transpose( f32 src[3][3], f32 dest[3][3] )
+{
+   f32 a = src[0][0], b = src[0][1], c = src[0][2],
+       d = src[1][0], e = src[1][1], f = src[1][2],
+       g = src[2][0], h = src[2][1], i = src[2][2];
+   dest[0][0] = a;
+   dest[0][1] = d;
+   dest[0][2] = g;
+   dest[1][0] = b;
+   dest[1][1] = e;
+   dest[1][2] = h;
+   dest[2][0] = c;
+   dest[2][1] = f;
+   dest[2][2] = i;
+}
+static inline void m3x3_mul( f32 a[3][3], f32 b[3][3], f32 d[3][3] )
+{
+   f32 a00 = a[0][0], a01 = a[0][1], a02 = a[0][2],
+       a10 = a[1][0], a11 = a[1][1], a12 = a[1][2],
+       a20 = a[2][0], a21 = a[2][1], a22 = a[2][2],
+       b00 = b[0][0], b01 = b[0][1], b02 = b[0][2],
+       b10 = b[1][0], b11 = b[1][1], b12 = b[1][2],
+       b20 = b[2][0], b21 = b[2][1], b22 = b[2][2];
+   d[0][0] = a00*b00 + a10*b01 + a20*b02;
+   d[0][1] = a01*b00 + a11*b01 + a21*b02;
+   d[0][2] = a02*b00 + a12*b01 + a22*b02;
+   d[1][0] = a00*b10 + a10*b11 + a20*b12;
+   d[1][1] = a01*b10 + a11*b11 + a21*b12;
+   d[1][2] = a02*b10 + a12*b11 + a22*b12;
+   d[2][0] = a00*b20 + a10*b21 + a20*b22;
+   d[2][1] = a01*b20 + a11*b21 + a21*b22;
+   d[2][2] = a02*b20 + a12*b21 + a22*b22;
+}
+static inline void m3x3_mulv( f32 m[3][3], f32 v[3], f32 d[3] )
+{
+   f32 res[3];
+   res[0] = m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2];
+   res[1] = m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2];
+   res[2] = m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2];
+   v3_copy( res, d );
+}
+static inline void m3x3_projection( f32 d[3][3], f32 left, f32 right, f32 bottom, f32 top )
+{
+   f32 rl, tb;
+   m3x3_zero( d );
+   rl = 1.0f / (right - left);
+   tb = 1.0f / (top   - bottom);
+   d[0][0] = 2.0f * rl;
+   d[1][1] = 2.0f * tb;
+   d[2][2] = 1.0f;
+}
+static inline void m3x3_translate( f32 m[3][3], f32 v[3] )
+{
+   m[2][0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0];
+   m[2][1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1];
+   m[2][2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2];
+}
+static inline void m3x3_scale( f32 m[3][3], f32 v[3] )
+{
+   v3_muls( m[0], v[0], m[0] );
+   v3_muls( m[1], v[1], m[1] );
+   v3_muls( m[2], v[2], m[2] );
+}
+static inline void m3x3_scalef( f32 m[3][3], f32 f )
+{
+   f32 v[3];
+   v3_fill( v, f );
+   m3x3_scale( m, v );
+}
+static inline void m3x3_rotate( f32 m[3][3], f32 angle )
+{
+   f32 m00 = m[0][0], m10 = m[1][0],
+       m01 = m[0][1], m11 = m[1][1],
+       m02 = m[0][2], m12 = m[1][2], c, s;
+   s = sinf( angle );
+   c = cosf( angle );
+   m[0][0] = m00 * c + m10 * s;
+   m[0][1] = m01 * c + m11 * s;
+   m[0][2] = m02 * c + m12 * s;
+   m[1][0] = m00 * -s + m10 * c;
+   m[1][1] = m01 * -s + m11 * c;
+   m[1][2] = m02 * -s + m12 * c;
+}
+
+/* f32[4][3] -------------------------------------------------------------------------------------------------------- */
+static inline void m4x3_to_3x3( f32 a[4][3], f32 b[3][3] )
+{
+   v3_copy( a[0], b[0] );
+   v3_copy( a[1], b[1] );
+   v3_copy( a[2], b[2] );
+}
+static inline void m4x3_invert_affine( f32 a[4][3], f32 b[4][3] )
+{
+   m3x3_transpose( a, b );
+   m3x3_mulv( b, a[3], b[3] );
+   v3_negate( b[3], b[3] );
+}
+static inline void m4x3_invert_full( f32 src[4][3], f32 dst[4][3] )
+{
+  f32 t2, t4, t5,
+      det,
+      a = src[0][0], b = src[0][1], c = src[0][2],
+      e = src[1][0], f = src[1][1], g = src[1][2],
+      i = src[2][0], j = src[2][1], k = src[2][2],
+      m = src[3][0], n = src[3][1], o = src[3][2];
+   t2 = j*o - n*k;
+   t4 = i*o - m*k;
+   t5 = i*n - m*j;
+   
+   dst[0][0] =  f*k - g*j;
+   dst[1][0] =-(e*k - g*i);
+   dst[2][0] =  e*j - f*i;
+   dst[3][0] =-(e*t2 - f*t4 + g*t5);
+   
+   dst[0][1] =-(b*k - c*j);
+   dst[1][1] =  a*k - c*i;
+   dst[2][1] =-(a*j - b*i);
+   dst[3][1] =  a*t2 - b*t4 + c*t5;
+   
+   t2 = f*o - n*g;
+   t4 = e*o - m*g; 
+   t5 = e*n - m*f;
+   
+   dst[0][2] =  b*g - c*f ;
+   dst[1][2] =-(a*g - c*e );
+   dst[2][2] =  a*f - b*e ;
+   dst[3][2] =-(a*t2 - b*t4 + c * t5);
+
+   det = 1.0f / (a * dst[0][0] + b * dst[1][0] + c * dst[2][0]);
+   v3_muls( dst[0], det, dst[0] );
+   v3_muls( dst[1], det, dst[1] );
+   v3_muls( dst[2], det, dst[2] );
+   v3_muls( dst[3], det, dst[3] );
+}
+static inline void m4x3_copy( f32 a[4][3], f32 b[4][3] )
+{
+   v3_copy( a[0], b[0] );
+   v3_copy( a[1], b[1] );
+   v3_copy( a[2], b[2] );
+   v3_copy( a[3], b[3] );
+}
+static inline void m4x3_identity( f32 a[4][3] )
+{
+   m4x3_copy( (f32[4][3]){{1,0,0},
+                          {0,1,0},
+                          {0,0,1},
+                          {0,0,0}}, a );
+}
+static inline void m4x3_mul( f32 a[4][3], f32 b[4][3], f32 d[4][3] )
+{
+   f32 a00 = a[0][0], a01 = a[0][1], a02 = a[0][2],
+       a10 = a[1][0], a11 = a[1][1], a12 = a[1][2],
+       a20 = a[2][0], a21 = a[2][1], a22 = a[2][2],
+       a30 = a[3][0], a31 = a[3][1], a32 = a[3][2],
+       b00 = b[0][0], b01 = b[0][1], b02 = b[0][2],
+       b10 = b[1][0], b11 = b[1][1], b12 = b[1][2],
+       b20 = b[2][0], b21 = b[2][1], b22 = b[2][2],
+       b30 = b[3][0], b31 = b[3][1], b32 = b[3][2];
+   d[0][0] = a00*b00 + a10*b01 + a20*b02;
+   d[0][1] = a01*b00 + a11*b01 + a21*b02;
+   d[0][2] = a02*b00 + a12*b01 + a22*b02;
+   d[1][0] = a00*b10 + a10*b11 + a20*b12;
+   d[1][1] = a01*b10 + a11*b11 + a21*b12;
+   d[1][2] = a02*b10 + a12*b11 + a22*b12;
+   d[2][0] = a00*b20 + a10*b21 + a20*b22;
+   d[2][1] = a01*b20 + a11*b21 + a21*b22;
+   d[2][2] = a02*b20 + a12*b21 + a22*b22;
+   d[3][0] = a00*b30 + a10*b31 + a20*b32 + a30;
+   d[3][1] = a01*b30 + a11*b31 + a21*b32 + a31;
+   d[3][2] = a02*b30 + a12*b31 + a22*b32 + a32;
+}
+static inline void m4x3_mulv( f32 m[4][3], f32 v[3], f32 d[3] )
+{
+   f32 result[3];
+   result[0] = m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2] + m[3][0];
+   result[1] = m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2] + m[3][1];
+   result[2] = m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2] + m[3][2];
+   v3_copy( result, d );
+}
+static inline void m4x3_mulp( f32 m[4][3], f32 p[4], f32 d[4] )
+{
+   f32 o[3];
+   v3_muls( p, p[3], o );
+   m4x3_mulv( m, o, o );
+   m3x3_mulv( m, p, d );
+   d[3] = v3_dot( o, d );
+}
+static inline void m4x3_translate( f32 m[4][3], f32 v[3] )
+{
+   v3_muladds( m[3], m[0], v[0], m[3] );
+   v3_muladds( m[3], m[1], v[1], m[3] );
+   v3_muladds( m[3], m[2], v[2], m[3] );
+}
+static inline void m4x3_rotate_x( f32 m[4][3], f32 angle )
+{
+   f32 t[4][3],
+       c = cosf( angle ),
+       s = sinf( angle );
+   m4x3_identity( t );
+   t[1][1] =  c;
+   t[1][2] =  s;
+   t[2][1] = -s;
+   t[2][2] =  c;
+   m4x3_mul( m, t, m );
+}
+static inline void m4x3_rotate_y( f32 m[4][3], f32 angle )
+{
+   f32 t[4][3],
+       c = cosf( angle ),
+       s = sinf( angle );
+   m4x3_identity( t );
+   t[0][0] =  c;
+   t[0][2] = -s;
+   t[2][0] =  s;
+   t[2][2] =  c;
+   m4x3_mul( m, t, m );
+}
+static inline void m4x3_rotate_z( f32 m[4][3], f32 angle )
+{
+   f32 t[4][3],
+       c = cosf( angle ),
+       s = sinf( angle );
+   m4x3_identity( t );
+   t[0][0] =  c;
+   t[0][1] =  s;
+   t[1][0] = -s;
+   t[1][1] =  c;
+   m4x3_mul( m, t, m );
+}
+static inline void m4x3_expand( f32 m[4][3], f32 d[4][4] )
+{
+   v3_copy( m[0], d[0] );
+   v3_copy( m[1], d[1] );
+   v3_copy( m[2], d[2] );
+   v3_copy( m[3], d[3] );
+   d[0][3] = 0.0f;
+   d[1][3] = 0.0f;
+   d[2][3] = 0.0f;
+   d[3][3] = 1.0f;
+}
+static inline void m4x3_decompose( f32 m[4][3], f32 co[3], f32 q[4], f32 s[3] )
+{
+   v3_copy( m[3], co );
+   s[0] = v3_length(m[0]);
+   s[1] = v3_length(m[1]);
+   s[2] = v3_length(m[2]);
+   f32 rot[3][3];
+   v3_divs( m[0], s[0], rot[0] );
+   v3_divs( m[1], s[1], rot[1] );
+   v3_divs( m[2], s[2], rot[2] );
+   m3x3_q( rot, q );
+}
+static inline void m4x3_expand_aabb_point( f32 m[4][3], f32 box[2][3], f32 point[3] )
+{
+   f32 v[3];
+   m4x3_mulv( m, point, v );
+   v3_min( box[0], v, box[0] );
+   v3_max( box[1], v, box[1] );
+}
+static inline void m4x3_expand_aabb_aabb( f32 m[4][3], f32 boxa[2][3], f32 boxb[2][3] )
+{
+   f32 a[3]; f32 b[3];
+   v3_copy( boxb[0], a );
+   v3_copy( boxb[1], b );
+   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ a[0], a[1], a[2] } );
+   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ a[0], b[1], a[2] } );
+   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ b[0], b[1], a[2] } );
+   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ b[0], a[1], a[2] } );
+   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ a[0], a[1], b[2] } );
+   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ a[0], b[1], b[2] } );
+   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ b[0], b[1], b[2] } );
+   m4x3_expand_aabb_point( m, boxa, (f32 [3]){ b[0], a[1], b[2] } );
+}
+static inline void m4x3_lookat( f32 m[4][3], f32 pos[3], f32 target[3], f32 up[3] )
+{
+   f32 dir[3];
+   v3_sub( target, pos, dir );
+   v3_normalize( dir );
+   v3_copy( dir, m[2] );
+   v3_cross( up, m[2], m[0] );
+   v3_normalize( m[0] );
+   v3_cross( m[2], m[0], m[1] );
+   v3_copy( pos, m[3] );
+}
+
+/* f32[4][4] -------------------------------------------------------------------------------------------------------- */
+static inline void m4x4_projection( f32 m[4][4], f32 angle, f32 ratio, f32 fnear, f32 ffar )
+{
+   f32 scale = tanf( angle * 0.5f * VG_PIf / 180.0f ) * fnear,
+         r = ratio * scale,
+         l = -r,
+         t = scale,
+         b = -t;
+   m[0][0] =  2.0f * fnear / (r - l);
+   m[0][1] =  0.0f;
+   m[0][2] =  0.0f;
+   m[0][3] =  0.0f;
+   m[1][0] =  0.0f;
+   m[1][1] =  2.0f * fnear / (t - b);
+   m[1][2] =  0.0f;
+   m[1][3] =  0.0f;
+   m[2][0] =  (r + l) / (r - l);
+   m[2][1] =  (t + b) / (t - b);
+   m[2][2] = -(ffar + fnear) / (ffar - fnear);
+   m[2][3] = -1.0f;
+   m[3][0] =  0.0f;
+   m[3][1] =  0.0f;
+   m[3][2] = -2.0f * ffar * fnear / (ffar - fnear);
+   m[3][3] =  0.0f;
+} 
+static inline void m4x4_translate( f32 m[4][4], f32 v[3] )
+{
+   v4_muladds( m[3], m[0], v[0], m[3] );
+   v4_muladds( m[3], m[1], v[1], m[3] );
+   v4_muladds( m[3], m[2], v[2], m[3] );
+}
+static inline void m4x4_copy( f32 a[4][4], f32 b[4][4] )
+{
+   v4_copy( a[0], b[0] );
+   v4_copy( a[1], b[1] );
+   v4_copy( a[2], b[2] );
+   v4_copy( a[3], b[3] );
+}
+static inline void m4x4_identity( f32 a[4][4] )
+{
+   m4x4_copy( (f32[4][4]){{1,0,0,0},
+                          {0,1,0,0},
+                          {0,0,1,0},
+                          {0,0,0,1}}, a );
+}
+static inline void m4x4_zero( f32 a[4][4] )
+{
+   m4x4_copy( (f32[4][4]){{0,0,0,0},
+                          {0,0,0,0},
+                          {0,0,0,0},
+                          {0,0,0,0}}, a );
+}
+static inline void m4x4_mul( f32 a[4][4], f32 b[4][4], f32 d[4][4] )
+{
+   f32 a00 = a[0][0], a01 = a[0][1], a02 = a[0][2], a03 = a[0][3],
+       a10 = a[1][0], a11 = a[1][1], a12 = a[1][2], a13 = a[1][3],
+       a20 = a[2][0], a21 = a[2][1], a22 = a[2][2], a23 = a[2][3],
+       a30 = a[3][0], a31 = a[3][1], a32 = a[3][2], a33 = a[3][3],
+       b00 = b[0][0], b01 = b[0][1], b02 = b[0][2], b03 = b[0][3],
+       b10 = b[1][0], b11 = b[1][1], b12 = b[1][2], b13 = b[1][3],
+       b20 = b[2][0], b21 = b[2][1], b22 = b[2][2], b23 = b[2][3],
+       b30 = b[3][0], b31 = b[3][1], b32 = b[3][2], b33 = b[3][3];
+  d[0][0] = a00*b00 + a10*b01 + a20*b02 + a30*b03;
+  d[0][1] = a01*b00 + a11*b01 + a21*b02 + a31*b03;
+  d[0][2] = a02*b00 + a12*b01 + a22*b02 + a32*b03;
+  d[0][3] = a03*b00 + a13*b01 + a23*b02 + a33*b03;
+  d[1][0] = a00*b10 + a10*b11 + a20*b12 + a30*b13;
+  d[1][1] = a01*b10 + a11*b11 + a21*b12 + a31*b13;
+  d[1][2] = a02*b10 + a12*b11 + a22*b12 + a32*b13;
+  d[1][3] = a03*b10 + a13*b11 + a23*b12 + a33*b13;
+  d[2][0] = a00*b20 + a10*b21 + a20*b22 + a30*b23;
+  d[2][1] = a01*b20 + a11*b21 + a21*b22 + a31*b23;
+  d[2][2] = a02*b20 + a12*b21 + a22*b22 + a32*b23;
+  d[2][3] = a03*b20 + a13*b21 + a23*b22 + a33*b23;
+  d[3][0] = a00*b30 + a10*b31 + a20*b32 + a30*b33;
+  d[3][1] = a01*b30 + a11*b31 + a21*b32 + a31*b33;
+  d[3][2] = a02*b30 + a12*b31 + a22*b32 + a32*b33;
+  d[3][3] = a03*b30 + a13*b31 + a23*b32 + a33*b33;
+}
+static inline void m4x4_mulv( f32 m[4][4], f32 v[4], f32 d[4] )
+{
+   f32 res[4];
+   res[0] = m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2] + m[3][0]*v[3];
+   res[1] = m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2] + m[3][1]*v[3];
+   res[2] = m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2] + m[3][2]*v[3];
+   res[3] = m[0][3]*v[0] + m[1][3]*v[1] + m[2][3]*v[2] + m[3][3]*v[3];
+   v4_copy( res, d );
+}
+static inline void m4x4_inv( f32 a[4][4], f32 d[4][4] )
+{
+   f32 a00 = a[0][0], a01 = a[0][1], a02 = a[0][2], a03 = a[0][3],
+       a10 = a[1][0], a11 = a[1][1], a12 = a[1][2], a13 = a[1][3],
+       a20 = a[2][0], a21 = a[2][1], a22 = a[2][2], a23 = a[2][3],
+       a30 = a[3][0], a31 = a[3][1], a32 = a[3][2], a33 = a[3][3],
+       det,
+       t[6];
+
+   t[0] = a22*a33 - a32*a23;
+   t[1] = a21*a33 - a31*a23;
+   t[2] = a21*a32 - a31*a22;
+   t[3] = a20*a33 - a30*a23;
+   t[4] = a20*a32 - a30*a22;
+   t[5] = a20*a31 - a30*a21;
+
+   d[0][0] =  a11*t[0] - a12*t[1] + a13*t[2];
+   d[1][0] =-(a10*t[0] - a12*t[3] + a13*t[4]);
+   d[2][0] =  a10*t[1] - a11*t[3] + a13*t[5];
+   d[3][0] =-(a10*t[2] - a11*t[4] + a12*t[5]);
+
+   d[0][1] =-(a01*t[0] - a02*t[1] + a03*t[2]);
+   d[1][1] =  a00*t[0] - a02*t[3] + a03*t[4];
+   d[2][1] =-(a00*t[1] - a01*t[3] + a03*t[5]);
+   d[3][1] =  a00*t[2] - a01*t[4] + a02*t[5];
+
+   t[0] = a12*a33 - a32*a13;
+   t[1] = a11*a33 - a31*a13;
+   t[2] = a11*a32 - a31*a12;
+   t[3] = a10*a33 - a30*a13;
+   t[4] = a10*a32 - a30*a12;
+   t[5] = a10*a31 - a30*a11;
+
+   d[0][2] =  a01*t[0] - a02*t[1] + a03*t[2];
+   d[1][2] =-(a00*t[0] - a02*t[3] + a03*t[4]);
+   d[2][2] =  a00*t[1] - a01*t[3] + a03*t[5];
+   d[3][2] =-(a00*t[2] - a01*t[4] + a02*t[5]);
+
+   t[0] = a12*a23 - a22*a13;
+   t[1] = a11*a23 - a21*a13;
+   t[2] = a11*a22 - a21*a12;
+   t[3] = a10*a23 - a20*a13;
+   t[4] = a10*a22 - a20*a12;
+   t[5] = a10*a21 - a20*a11;
+
+   d[0][3] =-(a01*t[0] - a02*t[1] + a03*t[2]);
+   d[1][3] =  a00*t[0] - a02*t[3] + a03*t[4];
+   d[2][3] =-(a00*t[1] - a01*t[3] + a03*t[5]);
+   d[3][3] =  a00*t[2] - a01*t[4] + a02*t[5];
+   
+   det = 1.0f / (a00*d[0][0] + a01*d[1][0] + a02*d[2][0] + a03*d[3][0]);
+   v4_muls( d[0], det, d[0] );
+   v4_muls( d[1], det, d[1] );
+   v4_muls( d[2], det, d[2] );
+   v4_muls( d[3], det, d[3] );
+}
+/* 
+ * http://www.terathon.com/lengyel/Lengyel-Oblique.pdf 
+ */
+static inline void m4x4_clip_projection( f32 mat[4][4], f32 plane[4] )
+{
+   f32 c[4] = 
+   {
+      (f32_sign(plane[0]) + mat[2][0]) / mat[0][0],
+      (f32_sign(plane[1]) + mat[2][1]) / mat[1][1],
+      -1.0f,
+      (1.0f + mat[2][2]) / mat[3][2]
+   };
+   v4_muls( plane, 2.0f / v4_dot(plane,c), c );
+   mat[0][2] = c[0];
+   mat[1][2] = c[1];
+   mat[2][2] = c[2] + 1.0f;
+   mat[3][2] = c[3];
+}
+static inline void m4x4_reset_clipping( f32 mat[4][4], f32 far, f32 near )
+{
+   mat[0][2] = 0.0f; 
+   mat[1][2] = 0.0f; 
+   mat[2][2] = -(far + near) / (far - near); 
+   mat[3][2] = -2.0f * far * near / (far - near); 
+}
+
+/* box -------------------------------------------------------------------------------------------------------------- */
+static inline void box_addpt( f32 a[2][3], f32 pt[3] )
+{
+   v3_min( a[0], pt, a[0] );
+   v3_max( a[1], pt, a[1] );
+}
+static inline void box_concat( f32 a[2][3], f32 b[2][3] )
+{
+   v3_min( a[0], b[0], a[0] );
+   v3_max( a[1], b[1], a[1] );
+}
+static inline void box_copy( f32 a[2][3], f32 b[2][3] )
+{
+   v3_copy( a[0], b[0] );
+   v3_copy( a[1], b[1] );
+}
+static inline b8 box_overlap( f32 a[2][3], f32 b[2][3] )
+{
+   return ( a[0][0] <= b[1][0] && a[1][0] >= b[0][0] ) &&
+          ( a[0][1] <= b[1][1] && a[1][1] >= b[0][1] ) &&
+          ( a[0][2] <= b[1][2] && a[1][2] >= b[0][2] );
+}
+static inline b8 box_within_pt( f32 box[2][3], f32 pt[3] )
+{
+   return (pt[0] >= box[0][0]) && (pt[1] >= box[0][1]) && (pt[2] >= box[0][2]) &&
+          (pt[0] <= box[1][0]) && (pt[1] <= box[1][1]) && (pt[2] <= box[1][2]);
+}
+static inline b8 box_within( f32 greater[2][3], f32 lesser[2][3] )
+{
+   f32 a[3], b[3];
+   v3_sub( lesser[0], greater[0], a );
+   v3_sub( lesser[1], greater[1], b );
+   return (a[0] >= 0.0f) && (a[1] >= 0.0f) && (a[2] >= 0.0f) &&
+          (b[0] <= 0.0f) && (b[1] <= 0.0f) && (b[2] <= 0.0f);
+}
+static inline void box_init_inf( f32 box[2][3] )
+{
+   v3_fill( box[0],  INFINITY );
+   v3_fill( box[1], -INFINITY );
+}
+
+/* planes ----------------------------------------------------------------------------------------------------------- */
+static inline void tri_to_plane( f64 a[3], f64 b[3], f64 c[3], f64 p[4] )
+{
+   f64 edge0[3];
+   f64 edge1[3];
+   f64 l;
+   
+   edge0[0] = b[0] - a[0];
+   edge0[1] = b[1] - a[1];
+   edge0[2] = b[2] - a[2];
+   
+   edge1[0] = c[0] - a[0];
+   edge1[1] = c[1] - a[1];
+   edge1[2] = c[2] - a[2];
+   
+   p[0] = edge0[1] * edge1[2] - edge0[2] * edge1[1];
+   p[1] = edge0[2] * edge1[0] - edge0[0] * edge1[2];
+   p[2] = edge0[0] * edge1[1] - edge0[1] * edge1[0];
+   
+   l = sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
+   p[3] = (p[0] * a[0] + p[1] * a[1] + p[2] * a[2]) / l;
+   
+   p[0] = p[0] / l;
+   p[1] = p[1] / l;
+   p[2] = p[2] / l;
+}
+
+// TODO: What is going on here?
+static inline b8 plane_intersect3( f32 a[4], f32 b[4], f32 c[4], f32 p[3] )
+{
+   f32 const epsilon = 1e-6f;
+   f32 x[3];
+   v3_cross( a, b, x );
+   f32 d = v3_dot( x, c );
+   if( (d < epsilon) && (d > -epsilon) ) 
+      return 0;
+
+   f32 v0[3], v1[3], v2[3];
+   v3_cross( b, c, v0 );
+   v3_cross( c, a, v1 );
+   v3_cross( a, b, v2 );
+
+   v3_muls(       v0, a[3], p );
+   v3_muladds( p, v1, b[3], p );
+   v3_muladds( p, v2, c[3], p );
+   v3_divs( p, d, p );
+   return 1;
+}
+static inline b8 plane_intersect2( f32 a[4], f32 b[4], f32 p[3], f32 n[3] )
+{
+   f32 const epsilon = 1e-6f;
+   f32 c[3];
+   v3_cross( a, b, c );
+   f32 d = v3_length2( c );
+   if( (d < epsilon) && (d > -epsilon) ) 
+      return 0;
+
+   f32 v0[3], v1[3], vx[3];
+   v3_cross( c, b, v0 );
+   v3_cross( a, c, v1 );
+
+   v3_muls( v0, a[3], vx );
+   v3_muladds( vx, v1, b[3], vx );
+   v3_divs( vx, d, p );
+   v3_copy( c, n );
+   return 1;
+}
+static inline b8 plane_segment( f32 plane[4], f32 a[3], f32 b[3], f32 co[3] )
+{
+   f32 d0 = v3_dot( a, plane ) - plane[3],
+       d1 = v3_dot( b, plane ) - plane[3];
+   if( d0*d1 < 0.0f )
+   {
+      f32 tot = 1.0f/( fabsf(d0)+fabsf(d1) );
+      v3_muls( a, fabsf(d1) * tot, co );
+      v3_muladds( co, b, fabsf(d0) * tot, co );
+      return 1;
+   }
+   else return 0;
+}
+static inline f64 plane_polarity( f64 p[4], f64 a[3] )
+{
+   return (a[0] * p[0] + a[1] * p[1] + a[2] * p[2])
+          -(p[0]*p[3] * p[0] + p[1]*p[3] * p[1] + p[2]*p[3] * p[2]);
+}
+static inline f32 ray_plane( f32 plane[4], f32 co[3], f32 dir[3] )
+{
+   f32 d = v3_dot( plane, dir );
+   if( fabsf(d) > 1e-6f )
+   {
+      f32 v0[3];
+      v3_muls( plane, plane[3], v0 );
+      v3_sub( v0, co, v0 );
+      return v3_dot( v0, plane ) / d;
+   }
+   else return INFINITY;
+}
+
+
+
+/* 
+
+ Time of collision (out t) of line a0 -> a0+b , vs b0->b1 
+
+ a0: line start
+ b : line trace direction ( non-normalized )
+ b0: test segment start
+ b1: test segment end
+ bOneSided: One way or double sided colliders
+ Returns 1/0 hit
+
+*/
+static inline b8 segment_segment_time_2d( f32 a0[2], f32 b[2], f32 b0[2], f32 d[2], f32 *t, b8 one_sided )
+{
+       const f32 k_epsilon = 0.00001f;
+       
+       f32 c[2];
+       f32 det, u;
+       
+       /* Interior test */
+       det = v2_cross( b, d );
+       if( det <= 0 && one_sided ) 
+      return 0;
+       
+   v2_sub( b0, a0, c );
+       
+       /* Second edge */
+       u = v2_cross( c, b ) / det;
+       if( u < -k_epsilon || u > 1.0f+k_epsilon ) 
+      return 0;
+       
+       /* First edge */
+       *t = v2_cross( c, d ) / det;
+       if( *t < -k_epsilon || *t > 1.0f+k_epsilon ) 
+      return 0;
+       
+       return 1;
+}
+
+/* 
+
+ Positional collision HELPER. Dont use if in a loop, calculate b/d outside 
+
+ a0: line start
+ a1: line end
+ b0: line start 2
+ b1: line end 2
+ dest: collision location
+ bOneSided: One way or double sided colliders
+ returns: 1/0 Hit
+
+*/
+static inline b8 segment_segment_intersect_2d( f32 a0[2], f32 a1[2], f32 b0[2], f32 b1[2], f32 dest[2], b8 one_sided )
+{
+       f32 b[2];
+       f32 d[2];
+       f32 t;
+       
+       /* Create trace vectors */
+       v2_sub( a1, a0, b );
+       v2_sub( b1, b0, d );
+       
+       /* Find time */
+       if( !segment_segment_time_2d( a0, b, b0, d, &t, one_sided ) ) 
+      return 0;
+       
+       /* Calculate position */
+       v2_muls( b, t, dest );
+       v2_add( a0, dest, dest );
+
+       return 1;
+}
+
diff --git a/source/maths/maths.kv b/source/maths/maths.kv
new file mode 100644 (file)
index 0000000..49e0b1b
--- /dev/null
@@ -0,0 +1,2 @@
+include ""
+add random.c
diff --git a/source/maths/random.c b/source/maths/random.c
new file mode 100644 (file)
index 0000000..250a596
--- /dev/null
@@ -0,0 +1,112 @@
+#include "foundation.h"
+#include "common_maths.h"
+#include "random.h"
+
+/* An implementation of the MT19937 Algorithm for the Mersenne Twister
+ * by Evan Sultanik.  Based upon the pseudocode in: M. Matsumoto and
+ * T. Nishimura, "Mersenne Twister: A 623-dimensionally
+ * equidistributed uniform pseudorandom number generator," ACM
+ * Transactions on Modeling and Computer Simulation Vol. 8, No. 1,
+ * January pp.3-30 1998.
+ *
+ * http://www.sultanik.com/Mersenne_twister
+ * https://github.com/ESultanik/mtwister/blob/master/mtwister.c
+ */
+
+void mt_random_seed( struct mt_random *rand, u32 seed ) 
+{
+   /* set initial seeds to mt[STATE_VECTOR_LENGTH] using the generator
+    * from Line 25 of Table 1 in: Donald Knuth, "The Art of Computer
+    * Programming," Vol. 2 (2nd Ed.) pp.102.
+    */
+   rand->mt[0] = seed & 0xffffffff;
+   for( rand->index=1; rand->index<MT_STATE_VECTOR_LENGTH; rand->index++)
+      rand->mt[rand->index] = (6069 * rand->mt[rand->index-1]) & 0xffffffff;
+}
+
+/*
+ * Generates a pseudo-randomly generated long.
+ */
+u32 mt_random_u32( struct mt_random *rand ) 
+{
+   u32 y;
+   /* mag[x] = x * 0x9908b0df for x = 0,1 */
+   static u32 mag[2] = {0x0, 0x9908b0df}; 
+   if( rand->index >= MT_STATE_VECTOR_LENGTH || rand->index < 0 )
+   {
+      /* generate STATE_VECTOR_LENGTH words at a time */
+      int kk;
+      if( rand->index >= MT_STATE_VECTOR_LENGTH+1 || rand->index < 0 )
+         mt_random_seed( rand, 4357 );
+      for( kk=0; kk<MT_STATE_VECTOR_LENGTH-MT_STATE_VECTOR_M; kk++ )
+      {
+         y = (rand->mt[kk] & MT_UPPER_MASK) | (rand->mt[kk+1] & MT_LOWER_MASK);
+         rand->mt[kk] = rand->mt[kk+MT_STATE_VECTOR_M] ^ (y>>1) ^ mag[y & 0x1];
+      }
+      for( ; kk<MT_STATE_VECTOR_LENGTH-1; kk++ )
+      {
+         y = (rand->mt[kk] & MT_UPPER_MASK) | (rand->mt[kk+1] & MT_LOWER_MASK);
+         rand->mt[kk] = rand->mt[ kk+(MT_STATE_VECTOR_M-MT_STATE_VECTOR_LENGTH)] ^ (y >> 1) ^ mag[y & 0x1];
+      }
+      y = (rand->mt[MT_STATE_VECTOR_LENGTH-1] & MT_UPPER_MASK) | (rand->mt[0] & MT_LOWER_MASK);
+      rand->mt[MT_STATE_VECTOR_LENGTH-1] = rand->mt[MT_STATE_VECTOR_M-1] ^ (y >> 1) ^ mag[y & 0x1];
+      rand->index = 0;
+   }
+   y = rand->mt[rand->index++];
+   y ^= (y >> 11);
+   y ^= (y << 7) & MT_TEMPERING_MASK_B;
+   y ^= (y << 15) & MT_TEMPERING_MASK_C;
+   y ^= (y >> 18);
+   return y;
+}
+
+/*
+ * Generates a pseudo-randomly generated f64 in the range [0..1].
+ */
+f64 mt_random_f64( struct mt_random *rand )
+{
+   return (f64)mt_random_u32(rand)/(f64)0xffffffff;
+}
+
+f64 mt_random_f64_range( struct mt_random *rand, f64 min, f64 max )
+{
+   return f32_lerp( min, max, (f64)mt_random_f64(rand) );
+}
+
+void mt_random_dir( struct mt_random *rand, f32 dir[3] )
+{
+   dir[0] = mt_random_f64(rand);
+   dir[1] = mt_random_f64(rand);
+   dir[2] = mt_random_f64(rand);
+
+   /* warning: *could* be 0 length.
+    * very unlikely.. 1 in (2^32)^3. but its mathematically wrong. */
+
+   v3_muls( dir, 2.0f, dir );
+   v3_sub( dir, (f32[3]){1.0f,1.0f,1.0f}, dir );
+   v3_normalize( dir );
+}
+
+void mt_random_sphere( struct mt_random *rand, f32 co[3] )
+{
+   mt_random_dir(rand,co);
+   v3_muls( co, cbrtf( mt_random_f64(rand) ), co );
+}
+
+void mt_random_disc( struct mt_random *rand, f32 co[2] )
+{
+   f32 a = mt_random_f64(rand) * VG_TAUf;
+   co[0] = sinf(a);
+   co[1] = cosf(a); 
+   v2_muls( co, sqrtf( mt_random_f64(rand) ), co );
+}
+
+void vg_rand_cone( struct mt_random *rand, f32 out_dir[3], f32 angle )
+{
+   f32 r = sqrtf(mt_random_f64(rand)) * angle * 0.5f,
+       a = mt_random_f64(rand) * VG_TAUf;
+   out_dir[0] = sinf(a) * sinf(r);
+   out_dir[1] = cosf(a) * sinf(r);
+   out_dir[2] = cosf(r);
+}
+
diff --git a/source/maths/random.h b/source/maths/random.h
new file mode 100644 (file)
index 0000000..bce7abf
--- /dev/null
@@ -0,0 +1,37 @@
+#pragma once
+
+/* An implementation of the MT19937 Algorithm for the Mersenne Twister
+ * by Evan Sultanik.  Based upon the pseudocode in: M. Matsumoto and
+ * T. Nishimura, "Mersenne Twister: A 623-dimensionally
+ * equidistributed uniform pseudorandom number generator," ACM
+ * Transactions on Modeling and Computer Simulation Vol. 8, No. 1,
+ * January pp.3-30 1998.
+ *
+ * http://www.sultanik.com/Mersenne_twister
+ * https://github.com/ESultanik/mtwister/blob/master/mtwister.c
+ */
+
+#define MT_UPPER_MASK         0x80000000
+#define MT_LOWER_MASK         0x7fffffff
+#define MT_TEMPERING_MASK_B   0x9d2c5680
+#define MT_TEMPERING_MASK_C   0xefc60000
+
+#define MT_STATE_VECTOR_LENGTH 624
+
+/* changes to STATE_VECTOR_LENGTH also require changes to this */
+#define MT_STATE_VECTOR_M      397 
+
+struct mt_random 
+{
+  u32 mt[MT_STATE_VECTOR_LENGTH];
+  i32 index;
+};
+
+void mt_random_seed( struct mt_random *rand, u32 seed );
+u32 mt_random_u32( struct mt_random *rand );
+f64 mt_random_f64( struct mt_random *rand );
+f64 mt_random_f64_range( struct mt_random *rand, f64 min, f64 max );
+void mt_random_dir( struct mt_random *rand, f32 dir[3] );
+void mt_random_sphere( struct mt_random *rand, f32 co[3] );
+void mt_random_disc( struct mt_random *rand, f32 co[2] );
+void vg_rand_cone( struct mt_random *rand, f32 out_dir[3], f32 angle );
diff --git a/source/maths/rigidbody.h b/source/maths/rigidbody.h
new file mode 100644 (file)
index 0000000..10bc05a
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2021-2025 Mt.ZERO Software - All Rights Reserved
+ */
+
+#define k_friction         0.4f
+#define k_damp_linear      0.1f               /* scale velocity 1/(1+x) */
+#define k_damp_angular     0.1f               /* scale angular  1/(1+x) */
+#define k_penetration_slop 0.01f
+#define k_rb_inertia_scale 4.0f
+#define k_phys_baumgarte   0.2f
+//#define k_gravity          9.6f
+#define k_rb_density       8.0f
+
+extern f32 k_gravity;
+
+enum rb_shape {
+   k_rb_shape_none    = 0,
+   k_rb_shape_box     = 1,
+   k_rb_shape_sphere  = 2,
+   k_rb_shape_capsule = 3,
+};
+
+typedef struct rigidbody rigidbody;
+typedef struct rb_capsule rb_capsule;
+
+struct rb_capsule
+{
+   f32 h, r; 
+};
+
+struct rigidbody
+{
+   v3f co, v, w;
+   v4f q;
+
+   f32 inv_mass;
+
+   m3x3f iI, iIw; /* inertia model and inverse world tensor */
+   m4x3f to_world, to_local;
+};
+
+VG_API void _vg_rigidbody_register(void);
+
+/* 
+ * Initialize rigidbody inverse mass and inertia tensor with some common shapes
+ */
+void rb_setbody_capsule( rigidbody *rb, f32 r, f32 h, f32 density, f32 inertia_scale );
+void rb_setbody_box( rigidbody *rb, boxf box, f32 density, f32 inertia_scale );
+void rb_setbody_sphere( rigidbody *rb, f32 r, f32 density, f32 inertia_scale );
+
+/*
+ * Update ALL matrices and tensors on rigidbody
+ */
+void rb_update_matrices( rigidbody *rb );
+
+/* 
+ * Extrapolate rigidbody into a transform based on vg accumulator.
+ * Useful for rendering
+ */
+void rb_extrapolate( rigidbody *rb, v3f co, v4f q );
+void rb_iter( rigidbody *rb );
+void rb_solve_gyroscopic( rigidbody *rb, m3x3f I, f32 h );
+
+/*
+ * Creates relative contact velocity vector
+ */
+void rb_rcv( rigidbody *rba, rigidbody *rbb, v3f ra, v3f rb, v3f rv );
+
+/*
+ * Apply impulse to object
+ */
+void rb_linear_impulse( rigidbody *rb, v3f delta, v3f impulse );
+
+/* 
+ * Effectors
+ */
+void rb_effect_simple_bouyency( rigidbody *ra, v4f plane, f32 amt, f32 drag );
+void rb_effect_spring_target_vector( rigidbody *rba, v3f ra, v3f rt, f32 spring, f32 dampening, f32 timestep );
+
+
+
+/* TODO: Get rid of this! */
+#define VG_MAX_CONTACTS 256
+
+typedef struct rb_ct rb_ct;
+struct rb_ct
+{
+   rigidbody *rba, *rbb;
+   v3f co, n;
+   v3f t[2];
+   float p, bias, norm_impulse, tangent_impulse[2],
+         normal_mass, tangent_mass[2];
+
+   u32 element_id;
+
+   enum contact_type type;
+}
+extern rb_contact_buffer[VG_MAX_CONTACTS];
+extern int rb_contact_count;
+
+int rb_capsule__sphere( m4x3f mtxA, rb_capsule *ca, v3f coB, f32 rb, rb_ct *buf );
+int rb_capsule__capsule( m4x3f mtxA, rb_capsule *ca, m4x3f mtxB, rb_capsule *cb, rb_ct *buf );
+int rb_capsule__box( m4x3f mtxA, rb_capsule *ca, m4x3f mtxB, m4x3f mtxB_inverse, boxf box, rb_ct *buf );
+int rb_sphere__box( v3f coA, f32 ra, m4x3f mtxB, m4x3f mtxB_inverse, boxf box, rb_ct *buf );
+int rb_sphere__sphere( v3f coA, f32 ra, v3f coB, f32 rb, rb_ct *buf );
+int rb_sphere__triangle( m4x3f mtxA, f32 r, v3f tri[3], rb_ct *buf );
+int rb_capsule__triangle( m4x3f mtxA, rb_capsule *c, v3f tri[3], rb_ct *buf );
+int rb_global_has_space( void );
+rb_ct *rb_global_buffer( void );
+int rb_manifold_apply_filtered( rb_ct *man, int len );
+int rb_box_triangle_sat( v3f extent, v3f center, m4x3f to_local, v3f tri_src[3] );
+
+/*
+ * Merge two contacts if they are within radius(r) of eachother
+ */
+void rb_manifold_contact_weld( rb_ct *ci, rb_ct *cj, float r );
+void rb_manifold_filter_joint_edges( rb_ct *man, int len, float r );
+
+/*
+ * Resolve overlapping pairs
+ */
+void rb_manifold_filter_pairs( rb_ct *man, int len, float r );
+
+/* 
+ * Remove contacts that are facing away from A
+ */
+void rb_manifold_filter_backface( rb_ct *man, int len );
+
+/*
+ * Filter out duplicate coplanar results. Good for spheres.
+ */
+void rb_manifold_filter_coplanar( rb_ct *man, int len, float w );
+
+void rb_debug_contact( rb_ct *ct );
+void rb_solver_reset(void);
+rb_ct *rb_global_ct(void);
+void rb_prepare_contact( rb_ct *ct, f32 dt );
+void rb_depenetrate( rb_ct *manifold, int len, v3f dt );
+void rb_presolve_contacts( rb_ct *buffer, f32 dt, int len );
+void rb_contact_restitution( rb_ct *ct, float cr );
+void rb_solve_contacts( rb_ct *buf, int len );
+
+
+
+typedef struct rb_constr_pos rb_constr_pos;
+typedef struct rb_constr_swingtwist rb_constr_swingtwist;
+
+struct rb_constr_pos
+{
+   rigidbody *rba, *rbb;
+   v3f lca, lcb;
+};
+
+struct rb_constr_swingtwist
+{
+   rigidbody *rba, *rbb;
+
+   v4f conevx, conevy; /* relative to rba */
+   v3f view_offset,    /* relative to rba */
+       coneva, conevxb;/* relative to rbb */
+
+   int tangent_violation, axis_violation;
+   v3f axis, tangent_axis, tangent_target, axis_target;
+
+   float conet;
+   float tangent_mass, axis_mass;
+
+   f32 conv_tangent, conv_axis;
+};
+
+void rb_debug_position_constraints( rb_constr_pos *buffer, int len );
+void rb_presolve_swingtwist_constraints( rb_constr_swingtwist *buf, int len );
+void rb_debug_swingtwist_constraints( rb_constr_swingtwist *buf, int len );
+   
+/*
+ * Solve a list of positional constraints
+ */
+void rb_solve_position_constraints( rb_constr_pos *buf, int len );
+void rb_solve_swingtwist_constraints( rb_constr_swingtwist *buf, int len );
+void rb_postsolve_swingtwist_constraints( rb_constr_swingtwist *buf, u32 len );
+void rb_solve_constr_angle( rigidbody *rba, rigidbody *rbb, v3f ra, v3f rb );
+
+/*
+ * Correct position constraint drift errors
+ * [ 0.0 <= amt <= 1.0 ]: the correction amount
+ */
+void rb_correct_position_constraints( rb_constr_pos *buf, int len, f32 amt );
+void rb_correct_swingtwist_constraints( rb_constr_swingtwist *buf, int len, float amt );
diff --git a/source/thread/primative_sdl2.c b/source/thread/primative_sdl2.c
deleted file mode 100644 (file)
index b7498e9..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-struct semaphore *semaphore_new( struct stack_allocator *stack, u32 initial_value )
-{
-   return SDL_CreateSemaphore( initial_value );
-}
-
-void semaphore_delete( struct semaphore *s );
-void semaphore_post( struct semaphore *s );
-void semaphore_wait( struct semaphore *s );
-
-struct mutex *mutex_new( struct stack_allocator *stack );
-void mutex_delete( struct mutex *m );
-void mutex_lock( struct mutex *m );
-void mutex_unlock( struct mutex *m );
index 002a947fdc8ab8597a40be9f518cb5797db33bc5..16baa93b73578fb3e8340a21143710011ea05047 100644 (file)
@@ -1,16 +1,16 @@
 #include <stdlib.h>
-#include "common_api.h"
+#include "foundation.h"
 
 struct stream folder_string;
 struct stream tripple_string;
-bool _setup_done = 0;
+b8 _setup_done = 0;
 
 const c8 *target_file_path = NULL;
 
-bool use_tsan = 0;
-bool use_asan = 0;
-bool shared = 0;
-bool no_pdb = 0;
+b8 use_tsan = 0;
+b8 use_asan = 0;
+b8 shared = 0;
+b8 no_pdb = 0;
 
 u32 optimise = 0;
 
@@ -75,7 +75,7 @@ static _metacompiler;
 
 struct
 {
-   bool using;
+   b8 using;
    struct stream layer_enums;
    struct stream input_enums, input_structures;
 }
@@ -83,7 +83,7 @@ static _input;
 
 struct
 {
-   bool using;
+   b8 using;
    struct stream enums, structures;
 }
 static _threads;
@@ -100,14 +100,14 @@ struct hook_user
 };
 struct
 {
-   bool using;
+   b8 using;
    struct stretchy_allocator events;
 }
 static _hooks;
 
 struct
 {
-   bool using;
+   b8 using;
    struct stream cvar_header, cvar_definitions, command_prototypes, command_definitions;
    u32 command_count;
 }
@@ -135,7 +135,7 @@ struct shader
 };
 struct
 {
-   bool using;
+   b8 using;
    struct stream shader_enum, uniform_enum, uniform_aliases, uniform_func_protos, uniform_funcs;
    u32 uniform_count;
    struct stretchy_allocator shaders;
@@ -419,7 +419,7 @@ void _parse_kv_block( struct keyvalues *kvs, u32 block, struct block_context con
             i32 affinity = 0;
             keyvalues_read_i32s( kvs, block, "affinity", NULL, &affinity, 1 );
 
-            bool found = 0;
+            b8 found = 0;
 
             for( u32 i=0; i<stretchy_count( &_hooks.events ); i ++ )
             {
@@ -526,14 +526,14 @@ void _parse_kv_block( struct keyvalues *kvs, u32 block, struct block_context con
 
       if( compare_buffers( key, 0, "if", 0 ) )
       {
-         bool invert = 0;
+         b8 invert = 0;
          if( value[0] == '!' )
          {
             value ++;
             invert = 1;
          }
 
-         bool result = 0 ^ invert;
+         b8 result = 0 ^ invert;
          for( u32 i=0; i<context.feature_count; i ++ )
             if( compare_buffers( _metacompiler.enabled_features[i], 0, value, 0 ) )
                result = 1 ^ invert;
@@ -578,6 +578,11 @@ void _parse_kv_block( struct keyvalues *kvs, u32 block, struct block_context con
 
          if( compare_buffers( key, 0, "define", 0 ) )
             $v_string( &_metacompiler.include_path_list, {"      -D"}, {value}, {" \\\n"} );
+
+         if( compare_buffers( key, 0, "link", 0 ) )
+            $v_string( &_metacompiler.include_path_list, {"      -l"}, {value}, {" \\\n"} );
+         if( compare_buffers( key, 0, "link_folder", 0 ) )
+            $v_string( &_metacompiler.include_path_list, {"      -L"}, {value}, {" \\\n"} );
       }
       else if( context.target == k_target_subshader )
       {
@@ -815,7 +820,7 @@ i32 main( i32 argc, const c8 *argv[] )
          }
          $v_string( &source, {"\n"} );
 
-         bool sorted = 0;
+         b8 sorted = 0;
          while( !sorted )
          {
             sorted = 1;
@@ -915,8 +920,8 @@ i32 main( i32 argc, const c8 *argv[] )
       struct stream command_string;
       stream_open_stack( &command_string, _temporary_stack_allocator(), k_stream_null_terminate );
       $v_string( &command_string, {"taskset -c 0-"}, $unsigned(processors), 
-                                  {" zig cc -Wall -Wno-unused-function -std=c11 -D_DEFAULT_SOURCE -lm \\\n"}, 
-                                  {"   -include \"types.h\" \\\n"} );
+                                  {" zig cc -Wall -Wno-unused-function -std=gnu99 -D_REENTRANT -lm \\\n"}, 
+                                  {"   -include \"source/types.h\" \\\n"} );
 
 #if 0
       if( use_tsan ) $v_string( &command_string, {"   -fsanitize=thread -lasan -L/lib/x86_64-linux-gnu \\\n"} );
@@ -929,9 +934,10 @@ i32 main( i32 argc, const c8 *argv[] )
       if( optimise == 0 ) $v_string( &command_string, { " -ggdb" } );
       $v_string( &command_string, {" \\\n"} );
 
-      $v_string( &command_string, {"   -target "}, {string_get(&tripple_string)}, {shared? " -shared -fPIC ": " "} );
+      $v_string( &command_string, {shared? " -shared -fPIC ": " "} );
       if( platform == k_platform_windows )
       {
+         $v_string( &command_string, {"   -target "}, {string_get(&tripple_string)}, {" \\\n"} );
          if( !shared )
             $v_string( &command_string, {"-Wl,--subsystem=windows "} );
 
diff --git a/source/types.h b/source/types.h
new file mode 100644 (file)
index 0000000..7b8142e
--- /dev/null
@@ -0,0 +1,13 @@
+typedef unsigned        char u8;
+typedef char                 c8;
+typedef unsigned short   int u16;
+typedef unsigned         int u32;
+typedef unsigned long    int u64;
+typedef                 char i8;
+typedef signed   short   int i16;
+typedef signed           int i32;
+typedef signed   long    int i64;
+typedef                float f32;
+typedef               double f64;
+typedef unsigned        char b8;
+#define NULL 0
diff --git a/source/vg_camera/vg_camera.c b/source/vg_camera/vg_camera.c
deleted file mode 100644 (file)
index 10ac96a..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#include "common_api.h"
-#include "maths/common_maths.h"
-#include "vg_camera.h"
-
-void vg_camera_lerp_angles( f32 a[3], f32 b[3], f32 t, f32 d[3] )
-{
-   d[0] = f32_radian_lerp( a[0], b[0], t );
-   d[1] = f32_lerp(  a[1], b[1], t );
-   d[2] = f32_lerp(  a[2], b[2], t );
-}
-
-/* lerp position, fov, and angles */
-void vg_camera_lerp( struct vg_camera *a, struct vg_camera *b, f32 t, struct vg_camera *d )
-{
-   v3_lerp( a->pos, b->pos, t, d->pos );
-   vg_camera_lerp_angles( a->angles, b->angles, t, d->angles );
-   d->fov = f32_lerp( a->fov, b->fov, t );
-}
-
-void vg_camera_copy( struct vg_camera *a, struct vg_camera *d )
-{
-   v3_copy( a->pos, d->pos );
-   v3_copy( a->angles, d->angles );
-   d->fov = a->fov;
-}
-
-void vg_m4x3_transform_camera( f32 m[4][3], struct vg_camera *cam )
-{
-   m4x3_mulv( m, cam->pos, cam->pos );
-
-   f32 v0[3];
-   v3_angles_to_vector( cam->angles, v0 );
-   m3x3_mulv( m, v0, v0 );
-   v3_normalize( v0 );
-   v3_vector_to_angles( v0, cam->angles );
-}
-
-/*
- * 1) [angles, pos] -> transform 
- */
-void vg_camera_update_transform( struct vg_camera *cam )
-{
-   f32 qyaw[4], qpitch[4], qroll[4], qcam[4];
-   q_axis_angle( qyaw,   (f32[]){ 0.0f, 1.0f, 0.0f }, -cam->angles[0] );
-   q_axis_angle( qpitch, (f32[]){ 1.0f, 0.0f, 0.0f }, -cam->angles[1] );
-   q_axis_angle( qroll,  (f32[]){ 0.0f, 0.0f, 1.0f }, -cam->angles[2] );
-
-   q_mul( qyaw, qpitch, qcam );
-   q_mul( qcam, qroll, qcam );
-   q_m3x3( qcam, cam->transform );
-   v3_copy( cam->pos, cam->transform[3] );
-}
-
-/*
- * 2) [transform] -> transform_inverse, view matrix
- */
-void vg_camera_update_view( struct vg_camera *cam )
-{
-   m4x4_copy( cam->mtx.v,  cam->mtx_prev.v );
-   m4x3_invert_affine( cam->transform, cam->transform_inverse );
-   m4x3_expand( cam->transform_inverse, cam->mtx.v );
-}
-
-/*
- * 3) [fov,nearz,farz] -> projection matrix
- */
-void vg_camera_update_projection( struct vg_camera *cam, f32 vw, f32 vh )
-{
-   m4x4_copy( cam->mtx.p,  cam->mtx_prev.p );
-   m4x4_projection( cam->mtx.p, cam->fov, (f32)vw / (f32)vh, cam->nearz, cam->farz );
-}
-
-/*
- * 4) [projection matrix, view matrix] -> previous pv, new pv 
- */
-void vg_camera_finalize( struct vg_camera *cam )
-{
-   m4x4_copy( cam->mtx.pv, cam->mtx_prev.pv );
-   m4x4_mul( cam->mtx.p, cam->mtx.v, cam->mtx.pv );
-}
diff --git a/source/vg_camera/vg_camera.h b/source/vg_camera/vg_camera.h
deleted file mode 100644 (file)
index b8ab30f..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#pragma once
-
-struct vg_camera
-{
-   /* Input */
-   f32 angles[3];
-   f32 pos[3];
-   f32 fov, nearz, farz;
-
-   /* Output */
-   f32 transform[4][3],
-       transform_inverse[4][3];
-
-   struct vg_camera_mtx
-   {
-      f32 p[4][4],
-          v[4][4],
-         pv[4][4];
-   }
-   mtx,
-   mtx_prev;
-};
-
-void vg_camera_lerp_angles( f32 a[3], f32 b[3], f32 t, f32 d[3] );
-
-/* lerp position, fov, and angles */
-void vg_camera_lerp( struct vg_camera *a, struct vg_camera *b, f32 t, struct vg_camera *d );
-void vg_camera_copy( struct vg_camera *a, struct vg_camera *d );
-void vg_m4x3_transform_camera( f32 m[4][3], struct vg_camera *cam );
-
-/*
- * 1) [angles, pos] -> transform 
- */
-void vg_camera_update_transform( struct vg_camera *cam );
-
-/*
- * 2) [transform] -> transform_inverse, view matrix
- */
-void vg_camera_update_view( struct vg_camera *cam );
-
-/*
- * 3) [fov,nearz,farz] -> projection matrix
- */
-void vg_camera_update_projection( struct vg_camera *cam, f32 vw, f32 vh );
-
-/*
- * 4) [projection matrix, view matrix] -> previous pv, new pv 
- */
-void vg_camera_finalize( struct vg_camera *cam );
diff --git a/source/vg_camera/vg_camera.kv b/source/vg_camera/vg_camera.kv
deleted file mode 100644 (file)
index 6462840..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-add vg_camera.c
-include ""
diff --git a/source/vg_lines/debug_lines.fs b/source/vg_lines/debug_lines.fs
deleted file mode 100644 (file)
index 1fcc7ad..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-out vec4 FragColor;
-
-in vec4 s_colour;
-
-void main()
-{
-   FragColor = s_colour;
-}
diff --git a/source/vg_lines/debug_lines.vs b/source/vg_lines/debug_lines.vs
deleted file mode 100644 (file)
index b2f8e17..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-layout (location=0) in vec3 a_co;
-layout (location=1) in vec4 a_colour;
-
-out vec4 s_colour;
-
-void main()
-{
-   vec4 vert_pos = uPv * vec4( a_co, 1.0 );
-   s_colour = a_colour;
-   gl_Position = vert_pos;
-}
diff --git a/source/vg_lines/vg_lines.c b/source/vg_lines/vg_lines.c
deleted file mode 100644 (file)
index ad89747..0000000
+++ /dev/null
@@ -1,289 +0,0 @@
-#include "opengl.h"
-#include "common_api.h"
-#include "common_thread_api.h"
-#include "maths/common_maths.h"
-#include <stddef.h>
-
-struct vg_lines
-{
-   struct stack_allocator vertex_stack;
-   struct vg_lines_vert
-   {
-      f32 co[3];
-      u32 colour;
-   } 
-   *vertex_buffer;
-   u32 vertex_count;
-
-       GLuint vao, vbo;
-}
-static _vg_lines;
-
-#define VG_LINES_MAX_VERTS 50000
-
-void _vg_lines_init(void)
-{
-   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_OPENGL ) );
-
-   _vg_lines.vertex_buffer = _heap_allocate( VG_LINES_MAX_VERTS*sizeof(struct vg_lines_vert) );
-   glGenVertexArrays( 1, &_vg_lines.vao );
-   glGenBuffers( 1, &_vg_lines.vbo );
-   glBindVertexArray( _vg_lines.vao );
-   glBindBuffer( GL_ARRAY_BUFFER, _vg_lines.vbo );
-   glBufferData( GL_ARRAY_BUFFER, VG_LINES_MAX_VERTS*sizeof(struct vg_lines_vert), NULL, GL_DYNAMIC_DRAW );
-   glBindVertexArray( _vg_lines.vao );
-
-   /* Pointers */
-   glVertexAttribPointer( 
-      0, 
-      3,
-      GL_FLOAT, 
-      GL_FALSE, 
-      sizeof( struct vg_lines_vert ), 
-      (void *)0 
-   );
-   glEnableVertexAttribArray( 0 );
-   
-   glVertexAttribPointer( 
-      1, 
-      4, 
-      GL_UNSIGNED_BYTE, 
-      GL_TRUE, 
-      sizeof( struct vg_lines_vert ), 
-      (void*)(offsetof( struct vg_lines_vert, colour ))
-   );
-   glEnableVertexAttribArray( 1 );
-}
-
-void vg_lines_draw( f32 pv[4][4] )
-{
-   _shader_bind( k_shader_debug_lines );
-   _shader_debug_lines_uPv( pv );
-
-       glBindVertexArray( _vg_lines.vao );
-       glBindBuffer( GL_ARRAY_BUFFER, _vg_lines.vbo );
-       glBufferSubData( GL_ARRAY_BUFFER, 0, _vg_lines.vertex_count*sizeof(struct vg_lines_vert), _vg_lines.vertex_buffer );
-
-       glEnable( GL_BLEND );
-       glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
-       glBlendEquation( GL_FUNC_ADD );
-   glDrawArrays( GL_LINES, 0, _vg_lines.vertex_count );
-       glDisable( GL_BLEND );
-}
-
-void vg_lines_clear(void)
-{
-   _vg_lines.vertex_count = 0;
-}
-
-void vg_line2( f32 from[3], f32 to[3], u32 fc, u32 tc )
-{
-   if( _vg_lines.vertex_count < VG_LINES_MAX_VERTS )
-   {
-      struct vg_lines_vert *v = &_vg_lines.vertex_buffer[ _vg_lines.vertex_count ];
-
-      v3_copy( from, v[0].co );
-      v3_copy( to, v[1].co );
-
-      v[0].colour = fc;
-      v[1].colour = tc;
-
-      _vg_lines.vertex_count += 2;
-   }
-}
-
-void vg_line( f32 from[3], f32 to[3], u32 colour )
-{
-       vg_line2( from, to, colour, colour );
-}
-
-void vg_line_arrow( f32 co[3], f32 dir[3], f32 size, u32 colour )
-{
-   f32 p1[3], tx[3], ty[3], p2[3], p3[3];
-   v3_muladds( co, dir, size, p1 );
-   v3_tangent_basis( dir, tx, ty );
-
-   v3_muladds( p1, dir, -size * 0.125f, p2 );
-   v3_muladds( p2, ty,  size * 0.125f, p3 );
-   v3_muladds( p2, ty, -size * 0.125f, p2 );
-
-   vg_line( co, p1, colour );
-   vg_line( p1, p2, colour );
-   vg_line( p1, p3, colour );
-}
-
-void vg_line_box_verts( f32 box[2][3], f32 verts[8][3] )
-{
-   for( u32 i=0; i<8; i++ )
-      for( u32 j=0; j<3; j++ )
-         verts[i][j] = i&(0x1<<j)? box[1][j]: box[0][j];
-}
-
-void vg_line_mesh( f32 verts[][3], u32 indices[][2], u32 indice_count, u32 colour )
-{
-   for( u32 i=0; i<indice_count; i++ )
-      vg_line( verts[indices[i][0]], verts[indices[i][1]], colour );
-}
-
-void vg_line_boxf( f32 box[2][3], u32 colour )
-{
-   f32 verts[8][3];
-   vg_line_box_verts( box, verts );
-   u32 indices[][2] = {{0,1},{1,3},{3,2},{2,0},
-                       {4,5},{5,7},{7,6},{6,4},
-                       {4,0},{5,1},{6,2},{7,3}};
-
-   vg_line_mesh( verts, indices, ARRAY_COUNT(indices), colour );
-}
-
-void vg_line_boxf_transformed( f32 m[4][3], f32 box[2][3], u32 colour )
-{
-   f32 verts[8][3];
-   vg_line_box_verts( box, verts );
-
-   for( u32 i=0; i<8; i++ )
-      m4x3_mulv( m, verts[i], verts[i] );
-
-   u32 indices[][2] = {{0,1},{1,3},{3,2},{2,0},
-                       {4,5},{5,7},{7,6},{6,4},
-                       {4,0},{5,1},{6,2},{7,3}};
-   vg_line_mesh( verts, indices, ARRAY_COUNT(indices), colour );
-}
-
-void vg_line_cross( f32 pos[3], u32 colour, f32 scale )
-{
-   f32 p0[3], p1[3];
-   v3_add( (f32[]){ scale,0.0f,0.0f}, pos, p0 );
-   v3_add( (f32[]){-scale,0.0f,0.0f}, pos, p1 );
-   vg_line( p0, p1, colour );
-   v3_add( (f32[]){0.0f, scale,0.0f}, pos, p0 );
-   v3_add( (f32[]){0.0f,-scale,0.0f}, pos, p1 );
-   vg_line( p0, p1, colour );
-   v3_add( (f32[]){0.0f,0.0f, scale}, pos, p0 );
-   v3_add( (f32[]){0.0f,0.0f,-scale}, pos, p1 );
-   vg_line( p0, p1, colour );
-}
-
-void vg_line_point( f32 pt[3], f32 size, u32 colour )
-{
-   f32 box[][3] =
-   {
-      { pt[0]-size, pt[1]-size, pt[2]-size },
-      { pt[0]+size, pt[1]+size, pt[2]+size }
-   };
-
-   vg_line_boxf( box, colour );
-}
-
-
-void vg_line_sphere( f32 m[4][3], f32 radius, u32 colour )
-{
-   f32 ly[3] = { 0.0f, 0.0f, radius },
-       lx[3] = { 0.0f, radius, 0.0f },
-       lz[3] = { 0.0f, 0.0f, radius };
-   
-   for( int i=0; i<16; i++ )
-   {
-      f32 t = ((f32)(i+1) * (1.0f/16.0f)) * VG_PIf * 2.0f,
-          s = sinf(t),
-          c = cosf(t);
-
-      f32 py[3] = { s*radius, 0.0f, c*radius },
-          px[3] = { s*radius, c*radius, 0.0f },
-          pz[3] = { 0.0f, s*radius, c*radius };
-
-      f32 p0[3], p1[3], p2[3], p3[3], p4[3], p5[3];
-      m4x3_mulv( m, py, p0 );
-      m4x3_mulv( m, ly, p1 );
-      m4x3_mulv( m, px, p2 );
-      m4x3_mulv( m, lx, p3 );
-      m4x3_mulv( m, pz, p4 );
-      m4x3_mulv( m, lz, p5 );
-
-      vg_line( p0, p1, colour == 0x00? 0xff00ff00: colour );
-      vg_line( p2, p3, colour == 0x00? 0xff0000ff: colour );
-      vg_line( p4, p5, colour == 0x00? 0xffff0000: colour );
-
-      v3_copy( py, ly );
-      v3_copy( px, lx );
-      v3_copy( pz, lz );
-   }
-}
-
-void vg_line_capsule( f32 m[4][3], f32 radius, f32 h, u32 colour )
-{
-   f32 s0 = sinf(0.0f)*radius,
-       c0 = cosf(0.0f)*radius;
-
-   f32 p0[3], p1[3], up[3], right[3], forward[3];
-   m3x3_mulv( m, (f32[]){0.0f,1.0f,0.0f}, up );
-   m3x3_mulv( m, (f32[]){1.0f,0.0f,0.0f}, right );
-   m3x3_mulv( m, (f32[]){0.0f,0.0f,-1.0f}, forward );
-   v3_muladds( m[3], up, -h*0.5f+radius, p0 );
-   v3_muladds( m[3], up,  h*0.5f-radius, p1 );
-
-   f32 a0[3], a1[3], b0[3], b1[3];
-   v3_muladds( p0, right, radius, a0 );
-   v3_muladds( p1, right, radius, a1 );
-   v3_muladds( p0, forward, radius, b0 );
-   v3_muladds( p1, forward, radius, b1 );
-   vg_line( a0, a1, colour );
-   vg_line( b0, b1, colour );
-
-   v3_muladds( p0, right, -radius, a0 );
-   v3_muladds( p1, right, -radius, a1 );
-   v3_muladds( p0, forward, -radius, b0 );
-   v3_muladds( p1, forward, -radius, b1 );
-   vg_line( a0, a1, colour );
-   vg_line( b0, b1, colour );
-   
-   for( i32 i=0; i<16; i++ )
-   {
-      f32 t = ((f32)(i+1) * (1.0f/16.0f)) * VG_PIf * 2.0f,
-          s1 = sinf(t)*radius,
-          c1 = cosf(t)*radius;
-
-      f32 e0[3] = { s0, 0.0f, c0 },
-          e1[3] = { s1, 0.0f, c1 },
-          e2[3] = { s0, c0, 0.0f },
-          e3[3] = { s1, c1, 0.0f },
-          e4[3] = { 0.0f, c0, s0 },
-          e5[3] = { 0.0f, c1, s1 };
-
-      m3x3_mulv( m, e0, e0 );
-      m3x3_mulv( m, e1, e1 );
-      m3x3_mulv( m, e2, e2 );
-      m3x3_mulv( m, e3, e3 );
-      m3x3_mulv( m, e4, e4 );
-      m3x3_mulv( m, e5, e5 );
-
-      v3_add( p0, e0, a0 );
-      v3_add( p0, e1, a1 );
-      v3_add( p1, e0, b0 );
-      v3_add( p1, e1, b1 );
-
-      vg_line( a0, a1, colour );
-      vg_line( b0, b1, colour );
-
-      if( c0 < 0.0f )
-      {
-         v3_add( p0, e2, a0 );
-         v3_add( p0, e3, a1 );
-         v3_add( p0, e4, b0 );
-         v3_add( p0, e5, b1 );
-      }
-      else
-      {
-         v3_add( p1, e2, a0 );
-         v3_add( p1, e3, a1 );
-         v3_add( p1, e4, b0 );
-         v3_add( p1, e5, b1 );
-      }
-
-      vg_line( a0, a1, colour );
-      vg_line( b0, b1, colour );
-
-      s0 = s1;
-      c0 = c1;
-   }
-}
diff --git a/source/vg_lines/vg_lines.h b/source/vg_lines/vg_lines.h
deleted file mode 100644 (file)
index 62fb781..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#define LINE_RED   0xff0000ff
-#define LINE_GREEN 0xff00ff00
-#define LINE_BLUE  0xffff0000
-#define LINE_WHITE 0xffffffff
-#define LINE_BLACK 0xff000000
-#define LINE_CLEAR 0x00ffffff
-#define LINE_PINK  0xffff00ff
-#define LINE_YELOW 0xff00ffff
-#define LINE_CYAN  0xffffff00
-#define LINE_NONE  0x00000000
-
-void vg_lines_clear(void);
-void vg_lines_draw( f32 pv[4][4] );
-
-void vg_line_capsule( f32 m[4][3], f32 radius, f32 h, u32 colour );
-void vg_line_sphere( f32 m[4][3], f32 radius, u32 colour );
-void vg_line_point( f32 pt[3], f32 size, u32 colour );
-void vg_line_boxf_transformed( f32 m[4][3], f32 box[2][3], u32 colour );
-void vg_line_boxf( f32 box[2][3], u32 colour );
-void vg_line_mesh( f32 verts[][3], u32 indices[][2], u32 indice_count, u32 colour );
-void vg_line_box_verts( f32 box[2][3], f32 verts[8][3] );
-void vg_line_cross( f32 pos[3], u32 colour, f32 scale );
-void vg_line_arrow( f32 co[3], f32 dir[3], f32 size, u32 colour );
-void vg_line( f32 from[3], f32 to[3], u32 colour );
-void vg_line2( f32 from[3], f32 to[3], u32 fc, u32 tc );
diff --git a/source/vg_lines/vg_lines.kv b/source/vg_lines/vg_lines.kv
deleted file mode 100644 (file)
index f07fb61..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-add vg_lines.c
-include ""
-
-hook
-{
-   event START
-   function _vg_lines_init
-}
-
-cvar
-{
-   name debug_lines
-   type u32
-   default 0
-   cheat 1
-   description "Show line debuggers"
-}
-
-shader
-{
-   name debug_lines
-   
-   subshader
-   {
-      type vertex
-      add debug_lines.vs
-
-      uniform
-      {
-         type mat4
-         alias uPv
-      }
-   }
-
-   subshader
-   {
-      type fragment
-      add debug_lines.fs
-   }
-}
diff --git a/source/vg_model/array_file.c b/source/vg_model/array_file.c
deleted file mode 100644 (file)
index 415b833..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-#include "common_api.h"
-#include "array_file.h"
-
-u32 af_str_hash( const void *packed_strings, u32 pstr )
-{
-   if( pstr & 0x3 )
-   {
-      $log( $fatal, {"ALIGNMENT ERROR, PSTR INDEX: "}, $unsigned(pstr) );
-   }
-   return *((u32 *)(packed_strings + pstr));
-}
-
-const c8 *af_str( const void *packed_strings, u32 pstr )
-{
-   return packed_strings + pstr + 4;
-}
-
-bool af_str_eq( const void *packed_strings, u32 pstr, const c8 *str, u32 str_hash )
-{
-   if( af_str_hash( packed_strings, pstr ) == str_hash )
-      if( compare_buffers( str, 0, af_str( packed_strings, pstr ), 0 ))
-         return 1;
-   return 0;
-}
-
-void af_load_array_file_buffer( struct array_file_context *ctx, struct array_file_meta *arr, void *buffer, u32 stride )
-{
-   if( arr->item_count )
-   {
-      zero_buffer( buffer, stride*arr->item_count );
-      u32 read_size = u32_min( stride, arr->item_size );
-      for( u32 i=0; i<arr->item_count; i++ )
-      {
-         stream_seek( ctx->stream, arr->file_offset + i*arr->item_size );
-         stream_read( ctx->stream, buffer+i*stride, read_size );
-      }
-   }
-}
-
-void af_load_array_file( struct array_file_context *ctx, struct array_file_ptr *out_ptr, 
-                         struct array_file_meta *arr, struct stack_allocator *stack, u32 stride )
-{
-   if( arr->item_count )
-   {
-      u32 size = stride*arr->item_count;
-      out_ptr->data = stack_allocate( stack, size, 8, NULL );
-      af_load_array_file_buffer( ctx, arr, out_ptr->data, stride );
-   }
-   else
-      out_ptr->data = NULL;
-   
-   out_ptr->stride = stride;
-   out_ptr->count = arr->item_count;
-}
-
-void *af_arritm( struct array_file_ptr *arr, u32 index )
-{
-   if( index >= arr->count )
-   {
-      $log( $fatal, {"Index out of range"}, $unsigned(index), {" >= "}, $unsigned( arr->count ) );
-      _fatal_exit();
-   }
-
-   return ((u8 *)arr->data) + index*arr->stride;
-}
-
-u32 af_arrcount( struct array_file_ptr *arr )
-{
-   return arr->count;
-}
-
-struct array_file_meta *af_find_array( struct array_file_context *ctx, const c8 *name )
-{
-   for( u32 i=0; i<af_arrcount(&ctx->index); i++ )
-   {
-      struct array_file_meta *arr = af_arritm( &ctx->index, i );
-      if( compare_buffers( arr->name, 0, name, 0 ) )
-         return arr;
-   }
-
-   return NULL;
-}
-
-bool af_load_array( struct array_file_context *ctx, struct array_file_ptr *ptr, const c8 *name, 
-                   struct stack_allocator *stack, u32 stride )
-{
-   struct array_file_meta *arr = af_find_array( ctx, name );
-
-   if( arr )
-   {
-      af_load_array_file( ctx, ptr, arr, stack, stride );
-      return 1;
-   }
-   else
-   {
-      ptr->data = NULL;
-      ptr->count = 0;
-      ptr->stride = 0;
-      return 0;
-   }
-}
-
-bool af_open_stream( struct array_file_context *afc, struct stream *stream, u32 min_version, u32 max_version, 
-                     struct stack_allocator *stack )
-{
-   afc->stream = stream;
-   if( stream_read( stream, &afc->header, sizeof(struct array_file_header)) != sizeof(struct array_file_header) )
-   {
-      $log( $error, {"Array file not large enough to contain header."} );
-      return 0;
-   }
-
-   if( (afc->header.version < min_version) || (afc->header.version > max_version) )
-   {
-      $log( $error, {"Array file version is out of range ("}, $unsigned( afc->header.version ),
-                    {"\nAccepted: "}, $unsigned( min_version ), {" -> "}, $unsigned( max_version ) );
-      return 0;
-   }
-
-   af_load_array_file( afc, &afc->index, &afc->header.index, stack, sizeof(struct array_file_meta) );
-   return 1;
-}
-
-/* compiler
- * ---------------------------------------------------------------------- */
-struct af_compiler_iter
-{
-   u32 i, j;
-   struct af_compiler_index *index;
-   struct af_compiler_item *current_item;
-   void *data;
-};
-
-static void af_init_iterator( struct af_compiler_iter *iter, struct af_compiler_index *index )
-{
-   iter->i = 0;
-   iter->j = 0;
-   iter->index = index;
-   iter->current_item = NULL;
-   iter->data = NULL;
-}
-
-static bool af_next( struct af_compiler_iter *iter )
-{
-   if( iter->i == 0 )
-   {
-      if( iter->index->first == NULL )
-         return 0;
-      iter->current_item = iter->index->first;
-   }
-
-   if( iter->j >= iter->current_item->count )
-   {
-      if( iter->current_item->next == NULL )
-         return 0;
-
-      iter->current_item = iter->current_item->next;
-      iter->j = 0;
-   }
-
-   iter->data = iter->current_item->data + (iter->j * iter->index->element_size);
-   iter->j ++;
-   iter->i ++;
-   return 1;
-}
-
-struct af_compiler_item *af_compiler_allocate_items( struct af_compiler *compiler, struct af_compiler_index *index, u32 count )
-{
-   struct af_compiler_item *entry = stack_allocate( compiler->stack, sizeof(struct af_compiler_item), 1, "Compiler item" );
-   entry->next = NULL;
-
-   u32 data_size = count * index->element_size;
-   index->element_count += count;
-
-   entry->data = stack_allocate( compiler->stack, data_size, 8, NULL );
-   entry->count = count;
-
-   for( u32 i=0; i<data_size; i ++ )
-      ((u8 *)entry->data)[i] = 0xab;
-
-   if( index->last )
-      index->last->next = entry;
-   index->last = entry;
-
-   if( !index->first )
-      index->first = entry;
-
-   return entry;
-}
-
-static void af_compiler_init_index( struct af_compiler_index *index, const c8 *alias, u32 element_size )
-{
-   ASSERT_CRITICAL( element_size );
-   if( !buffer_copy( alias, 0, index->name, sizeof(index->name) ) )
-   {
-      $log( $fatal, {"Index name overflowed: "}, {alias} );
-      _fatal_exit();
-   }
-   index->element_size = element_size;
-   index->element_count = 0;
-   index->first = NULL;
-   index->last = NULL;
-}
-
-struct af_compiler_index *af_compiler_create_index( struct af_compiler *compiler, const c8 *alias, u32 element_size )
-{
-   struct af_compiler_item *item = af_compiler_allocate_items( compiler, &compiler->index, 1 );
-   struct af_compiler_index *index = item->data;
-   af_compiler_init_index( index, alias, element_size );
-   return index;
-}
-
-u32 af_compile_string( struct af_compiler *compiler, const c8 *string )
-{
-   u32 string_hash = buffer_djb2( string, 0 );
-
-   // TODO: Hash table against existing strings (low priority)
-   u32 offset = offset = compiler->strings_index->element_count;
-   u32 bytes = PAD_TO_4( buffer_first_index( string, 0,0 )+1 + 4 );
-   struct af_compiler_item *item = af_compiler_allocate_items( compiler, compiler->strings_index, bytes );
-   *((u32 *)item->data) = string_hash;
-   buffer_copy( string, 0, item->data+4, 0 );
-   return offset;
-}
-
-void af_compiler_init( struct af_compiler *compiler, struct stack_allocator *stack )
-{
-   compiler->stack = stack;
-   af_compiler_init_index( &compiler->index, "index", sizeof(struct af_compiler_index) );
-   compiler->strings_index = af_compiler_create_index( compiler, "strings", 1 );
-   af_compile_string( compiler, "nul" );
-}
-
-static void af_write_bin( struct af_compiler *compiler, void *data, u32 data_len, u32 padding )
-{
-   if( data )
-   {
-      stream_write( &compiler->stream, data, data_len );
-      compiler->file_offset += data_len;
-   }
-
-   if( padding )
-   {
-      while( compiler->file_offset % padding )
-      {
-         const u8 pad_byte = 0xac;
-         stream_write( &compiler->stream, &pad_byte, 1 );
-         compiler->file_offset ++;
-      }
-   }
-}
-
-struct af_compiler_index *af_get_or_make_index( struct af_compiler *compiler, const c8 *alias, u32 element_size )
-{
-   struct af_compiler_iter iter;
-   af_init_iterator( &iter, &compiler->index );
-   while( af_next( &iter ) )
-   {
-      struct af_compiler_index *index = iter.data;
-      if( compare_buffers( index->name, 0, alias, 0 ) )
-         return index;
-   }
-   
-   return af_compiler_create_index( compiler, alias, element_size );
-}
-
-bool af_write( struct af_compiler *compiler, const c8 *path, u32 version )
-{
-   u32 indices_to_write = 0;
-   
-   struct af_compiler_iter iter;
-   af_init_iterator( &iter, &compiler->index );
-   while( af_next( &iter ) )
-   {
-      struct af_compiler_index *index = iter.data;
-      if( index->element_count )
-         indices_to_write ++;
-   }
-
-   u32 header_size = PAD_TO_8( sizeof( struct array_file_header ) );
-   u32 index_size = PAD_TO_8( sizeof( struct array_file_meta ) * indices_to_write );
-   
-   if( !stream_open_file( &compiler->stream, path, k_stream_write ) )
-      return 0;
-
-   compiler->file_offset = 0;
-
-   struct array_file_header header;
-   header.version = version;
-   header.index.file_offset = header_size;
-   header.index.item_count = indices_to_write;
-   header.index.item_size = sizeof(struct array_file_meta);
-   buffer_copy( "index", 0, header.index.name, sizeof(header.index.name) );
-   af_write_bin( compiler, &header, sizeof(struct array_file_header), 8 );
-
-   /* write index */
-   u32 file_offset = header_size + index_size;
-   af_init_iterator( &iter, &compiler->index );
-   while( af_next( &iter ) )
-   {
-      struct af_compiler_index *index = iter.data;
-      if( index->element_count )
-      {
-         struct array_file_meta meta;
-         buffer_copy( index->name, sizeof(index->name), meta.name, sizeof(meta.name) );
-         meta.item_count = index->element_count;
-         meta.item_size = index->element_size;
-         meta.file_offset = file_offset;
-         file_offset += PAD_TO_8( meta.item_size*meta.item_count );
-         af_write_bin( compiler, &meta, sizeof(struct array_file_meta), 0 );
-      }
-   }
-   af_write_bin( compiler, NULL, 0, 8 );
-
-   af_init_iterator( &iter, &compiler->index );
-   while( af_next( &iter ) )
-   {
-      struct af_compiler_index *index = iter.data;
-
-      if( index->element_count )
-      {
-         struct af_compiler_iter item_iter;
-         af_init_iterator( &item_iter, index );
-
-         while( af_next( &item_iter ) )
-            af_write_bin( compiler, item_iter.data, index->element_size, 0 );
-         af_write_bin( compiler, NULL, 0, 8 );
-      }
-   }
-
-   stream_close( &compiler->stream );
-   return 1;
-}
diff --git a/source/vg_model/array_file.h b/source/vg_model/array_file.h
deleted file mode 100644 (file)
index 75b6113..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#pragma once
-
-struct array_file_ptr
-{
-   void *data;
-   u32 count, stride;
-};
-
-struct array_file_meta
-{
-   u32 file_offset,
-       item_count,
-       item_size;
-
-   c8 name[16];
-};
-
-struct array_file_header
-{
-   u32 version;
-   struct array_file_meta index;
-};
-
-struct array_file_context
-{
-   struct stream *stream;
-   struct array_file_header header;
-   struct array_file_ptr index;
-};
-
-/* array loading */
-struct array_file_meta *af_find_array( struct array_file_context *ctx, const c8 *name );
-
-void af_load_array_file( struct array_file_context *ctx, struct array_file_ptr *out_ptr, 
-                         struct array_file_meta *arr, struct stack_allocator *stack, u32 stride );
-
-bool af_load_array( struct array_file_context *ctx, struct array_file_ptr *ptr, const c8 *name, 
-                    struct stack_allocator *stack, u32 stride );
-void af_load_array_file_buffer( struct array_file_context *ctx, struct array_file_meta *arr, void *buffer, u32 stride );
-
-/* array access */
-void *af_arritm( struct array_file_ptr *arr, u32 index );
-u32 af_arrcount( struct array_file_ptr *arr );
-
-/* packed string buffer access (with djb2 hash prefix) */
-const c8 *af_str( const void *packed_strings, u32 pstr );
-u32 af_str_hash( const void *packed_strings, u32 pstr );
-bool af_str_eq( const void *packed_strings, u32 pstr, const char *str, u32 str_hash );
-
-#define AF_STR_EQ( CTX, PSTR, CONSTR ) \
-   af_str_eq( CTX, PSTR, CONSTR, vg_strdjb2( CONSTR ) )
-
-/* COmpiler 
- * ------------------------------------ */
-
-struct af_compiler_item
-{
-   void *data;
-   u32 count;
-   struct af_compiler_item *next;
-};
-
-struct af_compiler_index
-{
-   c8 name[16];
-   u32 element_size, element_count;
-   struct af_compiler_item *first, *last;
-};
-
-struct af_compiler
-{
-   struct stack_allocator *stack;
-   struct af_compiler_index index,
-                           *strings_index;
-   struct af_compiler_item *most_recent_item;
-
-   struct stream stream;
-   u32 file_offset;
-};
-
-void af_compiler_init( struct af_compiler *compiler, struct stack_allocator *stack );
-struct af_compiler_item *af_compiler_allocate_items( struct af_compiler *compiler, struct af_compiler_index *index, u32 count );
-struct af_compiler_index *af_compiler_create_index( struct af_compiler *compiler, const c8 *alias, u32 element_size );
-bool af_write( struct af_compiler *compiler, const c8 *path, u32 version );
-struct af_compiler_index *af_get_or_make_index( struct af_compiler *compiler, const c8 *alias, u32 element_size );
-u32 af_compile_string( struct af_compiler *compiler, const c8 *string );
-
-bool af_open_stream( struct array_file_context *afc, struct stream *stream, u32 min_version, u32 max_version, 
-                     struct stack_allocator *stack );
diff --git a/source/vg_model/compiler.c b/source/vg_model/compiler.c
deleted file mode 100644 (file)
index 7760939..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-#include "compiler.h"
-
-void mdl_compiler_init( struct mdl_compiler *compiler )
-{
-   stack_init( &compiler->stack, NULL, VG_MB(10), "MDL Compiler" );
-   af_compiler_init( &compiler->af, &compiler->stack );
-   compiler->meshes = af_compiler_create_index( &compiler->af, "mdl_mesh", sizeof(struct mdl_mesh) );
-   compiler->submeshes = af_compiler_create_index( &compiler->af, "mdl_submesh", sizeof(struct mdl_submesh) );
-   compiler->vertices = af_compiler_create_index( &compiler->af, "mdl_vert", sizeof(struct mdl_vert) );
-   compiler->indices = af_compiler_create_index( &compiler->af, "mdl_indice", sizeof(u32) );
-   compiler->bones = af_compiler_create_index( &compiler->af, "mdl_bone", sizeof(struct mdl_bone) );
-   compiler->materials = af_compiler_create_index( &compiler->af, "mdl_material", sizeof(struct mdl_material) );
-   compiler->shader_data = af_compiler_create_index( &compiler->af, "shader_data", 1 );
-   compiler->armatures = af_compiler_create_index( &compiler->af, "mdl_armature", sizeof(struct mdl_armature) );
-   compiler->textures = af_compiler_create_index( &compiler->af, "mdl_texture", sizeof(struct mdl_texture) );
-   compiler->pack_data = af_compiler_create_index( &compiler->af, "pack", 1 );
-}
-
-u32 mdl_compiler_push_entity( struct mdl_compiler *compiler, u32 entity_type, const c8 *entity_type_string,
-                              void *data, u32 data_size )
-{
-   struct af_compiler_index *index = af_get_or_make_index( &compiler->af, entity_type_string, data_size );
-
-   u32 index_part = index->element_count,
-       id = (entity_type & 0xfffff)<<16 | (index_part & 0xfffff); //TODO
-
-   struct af_compiler_item *item = af_compiler_allocate_items( &compiler->af, index, 1 );
-   buffer_copy( item->data, data_size, data, data_size );
-   return jd;
-}
-
-void mdl_compiler_start_mesh( struct mdl_compiler *compiler, const c8 *name, u32 associated_entity, u32 associated_armature )
-{
-   struct mdl_mesh *mesh = af_compiler_allocate_items( &compiler->af, compiler->meshes, 1 )->data;
-   mdl_transform_identity( &mesh->transform );
-   mesh->submesh_start = compiler->submeshes->element_count;
-   mesh->submesh_count = 0;
-   mesh->pstr_name = af_compile_string( &compiler->af, name );
-   mesh->entity_id = associated_entity;
-   mesh->armature_id = associated_armature;
-}
-
-void mdl_compiler_start_submesh( struct mdl_compiler *compiler, u32 material_id, u32 flags )
-{
-   struct mdl_mesh *current_mesh = compiler->meshes->last->data;
-   current_mesh->submesh_count ++;
-
-   struct mdl_submesh *sm = af_compiler_allocate_items( &compiler->af, compiler->submeshes, 1 )->data;
-   sm->indice_start = compiler->indices->element_count;
-   sm->vertex_start = compiler->vertices->element_count;
-   sm->indice_count = 0;
-   sm->vertex_count = 0;
-   sm->material_id = material_id;
-   sm->flags = flags;
-   box_init_inf( sm->bbx );
-}
-
-void mdl_compiler_push_meshdata( struct mdl_compiler *compiler, struct mdl_vert *vertex_buffer, u32 vertex_count,
-                                 u32 *indice_buffer, u32 indice_count )
-{
-   struct mdl_submesh *current_submesh = compiler->submeshes->last->data;
-   current_submesh->vertex_count += vertex_count;
-   current_submesh->indice_count += indice_count;
-
-   struct mdl_vert *dest_verts = af_compiler_allocate_items( &compiler->af, compiler->vertices, vertex_count )->data;
-   u32 *dest_indices = af_compiler_allocate_items( &compiler->af, compiler->indices, indice_count )->data;
-
-   for( u32 i=0; i<vertex_count; i ++ )
-      box_addpt( current_submesh->bbx, vertex_buffer[i].co );
-
-   u32 vert_size = vertex_count * sizeof(mdl_vert);
-   buffer_copy( vertex_buffer, vert_size, dest_verts, vert_size );
-
-   u32 indice_size = indice_count * sizeof(u32);
-   buffer_copy( indice_buffer, indice_size, dest_indices, indice_size );
-}
-
-static void mdl_compiler_pack_data( struct mdl_compiler *compiler, const c8 *path, void *data, 
-                                    u32 data_length, struct mdl_file *out_file )
-{
-   out_file->pstr_path = af_compile_string( &compiler->af, path );
-   out_file->pack_offset = compiler->pack_data->element_count;
-   out_file->pack_size = data_length;
-   void *dest = af_compiler_allocate_items( &compiler->af, compiler->pack_data, vg_align16(data_length) )->data;
-   buffer_copy( data, data_length, dest, data_length );
-}
-
-u32 mdl_compiler_start_material( struct mdl_compiler *compiler, const c8 *name )
-{
-   u32 material_id = compiler->materials->element_count + 1;
-
-   struct mdl_material *material = af_compiler_allocate_items( &compiler->af, compiler->materials, 1 )->data;
-   material->pstr_name = af_compile_string( &compiler->af, name );
-   material->shader = 0;
-   material->flags = 0;
-   material->surface_prop = 0;
-   material->props.kvs.offset = 0;
-   material->props.kvs.size = 0;
-   return material_id;
-}
-
-void mdl_compiler_set_surface_info( struct mdl_compiler *compiler, u32 flags, u32 surface_prop )
-{
-   struct mdl_material *current_material = compiler->materials->last->data;
-   current_material->flags = flags;
-   current_material->surface_prop = surface_prop;
-}
-
-u32 mdl_compiler_compile_texture_qoi( struct mdl_compiler *compiler, const c8 *name, void *data, u32 data_len )
-{
-   u32 texture_id = compiler->textures->element_count + 1;
-   struct mdl_texture *texture = af_compiler_allocate_items( &compiler->af, compiler->textures, 1 )->data;
-   mdl_compiler_pack_data( compiler, name, data, data_len, &texture->file );
-   return texture_id;
-}
-
-void mdl_compiler_push_shaderdata( struct mdl_compiler *compiler, u32 shader_id, struct keyvalues *shader_kvs )
-{
-   struct mdl_material *current_material = compiler->materials->last->data;
-   current_material->shader = shader_id;
-   current_material->props.kvs.offset = compiler->shader_data->element_count;
-
-   // FIXME FIXME FIXME FIXME FIXME NEED TO FLATTEN OUT THE KVS INTO STRINGYMABOB!!!!!!!!!!!!!!!!
-#if 0
-   current_material->props.kvs.size = shader_kvs->cur.co;
-
-   void *dest = af_compiler_allocate_items( &compiler->af, compiler->shader_data, vg_align8(shader_kvs->cur.co) )->data;
-   memcpy( dest, shader_kvs->buf, shader_kvs->cur.co );
-#endif
-}
-
-void mdl_compiler_free( struct mdl_compiler *compiler )
-{
-   stack_free( &compiler->stack );
-}
diff --git a/source/vg_model/compiler.h b/source/vg_model/compiler.h
deleted file mode 100644 (file)
index 7435de4..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-struct mdl_compiler
-{
-   struct af_compiler af;
-   struct af_compiler_index *meshes,
-                            *submeshes,
-                            *vertices,
-                            *indices,
-                            *bones,
-                            *materials,
-                            *shader_data,
-                            *armatures,
-                            *textures,
-                            *pack_data;
-   struct stack_allocator stack;
-};
diff --git a/source/vg_model/entity.h b/source/vg_model/entity.h
deleted file mode 100644 (file)
index 65e26af..0000000
+++ /dev/null
@@ -1,751 +0,0 @@
-#pragma once
-#include "model.h"
-
-enum entity_alias
-{
-   k_ent_none        = 0,
-   k_ent_gate        = 1,
-   k_ent_spawn       = 2,
-   k_ent_route_node  = 3,
-   k_ent_route       = 4,
-   k_ent_water       = 5,
-   k_ent_volume      = 6,
-   k_ent_audio       = 7,
-   k_ent_marker      = 8,
-   k_ent_font        = 9,
-   k_ent_font_variant= 10,
-   k_ent_traffic     = 11,
-   k_ent_skateshop   = 12,
-   k_ent_camera      = 13,
-   k_ent_swspreview  = 14,
-   k_ent_deleted1    = 15, //k_ent_menuitem    = 15,
-   k_ent_worldinfo   = 16,
-   k_ent_ccmd        = 17,
-   k_ent_objective   = 18,
-   k_ent_challenge   = 19,
-   k_ent_deleted0    = 20, //k_ent_relay       = 20,
-   k_ent_cubemap     = 21,
-   k_ent_miniworld   = 22,
-   k_ent_prop        = 23,
-   k_ent_list        = 24,
-   k_ent_region      = 25,
-   k_ent_glider      = 26,
-   k_ent_npc         = 27,
-   k_ent_armature    = 28,
-   k_ent_script      = 29, 
-   k_ent_atom        = 30,
-   k_ent_cutscene    = 31,
-   k_ent_light       = 32,
-   k_mdl_mesh        = 33,
-   k_editer_property = 34,
-   k_editer_item     = 35,
-   k_ent_max
-};
-
-const char *_entity_alias_str[] =
-{
-   [k_ent_none] = "none/null",
-   [k_ent_gate] = "ent_gate",
-   [k_ent_spawn] = "ent_spawn",
-   [k_ent_route_node] = "ent_route_node",
-   [k_ent_route] = "ent_route",
-   [k_ent_water] = "ent_water",
-   [k_ent_volume] = "ent_volume",
-   [k_ent_audio] = "ent_audio",
-   [k_ent_marker] = "ent_marker",
-   [k_ent_font] = "ent_font",
-   [k_ent_font_variant] = "ent_font_variant",
-   [k_ent_traffic] = "ent_traffic",
-   [k_ent_skateshop] = "ent_skateshop",
-   [k_ent_camera] = "ent_camera",
-   [k_ent_swspreview] = "ent_swspreview",
-   //[k_ent_menuitem] = "ent_menuitem",
-   [k_ent_worldinfo] = "ent_worldinfo",
-   [k_ent_ccmd] = "ent_ccmd",
-   [k_ent_objective] = "ent_objective",
-   [k_ent_challenge] = "ent_challenge",
-   //[k_ent_relay] = "ent_relay",
-   [k_ent_cubemap] = "ent_cubemap",
-   [k_ent_miniworld] = "ent_miniworld",
-   [k_ent_prop] = "ent_prop",
-   [k_ent_region] = "ent_region",
-   [k_ent_glider] = "ent_glider",
-   [k_ent_npc] = "ent_npc",
-   [k_ent_armature] = "mdl_armature",
-   [k_ent_script] = "ent_script",
-   [k_ent_atom] = "ent_atom",
-   [k_ent_cutscene] = "ent_cutscene",
-   [k_ent_light] = "ent_light"
-};
-
-static inline u32 mdl_entity_id_type( u32 entity_id )
-{
-   return (entity_id & 0x0fff0000) >> 16;
-}
-
-static inline u32 mdl_entity_id_id( u32 entity_id )
-{
-   return entity_id & 0x0000ffff;
-}
-
-static inline u32 mdl_entity_id( u32 type, u32 index )
-{
-   return (type & 0xfffff)<<16 | (index & 0xfffff);
-}
-
-enum ent_spawn_flag
-{
-   k_ent_spawn_flag_locked = 0x1,
-   k_ent_spawn_flag_group_1 = 0x10,
-   k_ent_spawn_flag_group_2 = 0x20,
-   k_ent_spawn_flag_group_3 = 0x40,
-   k_ent_spawn_flag_group_4 = 0x80
-};
-
-struct ent_spawn
-{
-   struct mdl_transform transform;
-   u32 pstr_name;
-   u32 flags;
-};
-
-enum light_type
-{
-   k_light_type_point = 0,
-   k_light_type_spot = 1
-};
-
-struct ent_light
-{
-   struct mdl_transform transform;
-   u32 daytime,
-       type;
-   f32 colour[4];
-   f32 angle,
-       range;
-   f32 inverse_world[4][3];
-   f32 angle_sin_cos[2];
-};
-
-/* v101 */
-#if 0
-enum gate_type{
-   k_gate_type_unlinked = 0,
-   k_gate_type_teleport = 1,
-   k_gate_type_nonlocal_unlinked = 2,
-   k_gate_type_nonlocel = 3
-};
-#endif
-
-enum list_alias_type
-{
-   k_list_alias_none = 0,
-   k_list_alias_string = 1
-};
-
-struct ent_list
-{
-   u16 entity_ref_start, entity_ref_count;
-   u8  alias_type, none0, none1, none2;
-   u32 pstr_alias;
-};
-
-struct file_entity_ref
-{
-   u32 entity_id, pstr_alias;
-};
-
-/* v102+ */
-enum ent_gate_flag
-{
-   k_ent_gate_linked      = 0x1, /* this is a working portal */
-   k_ent_gate_nonlocal    = 0x2, /* use the key string to link this portal.
-                                       NOTE: if set, it adds the flip flag. */
-   k_ent_gate_flip        = 0x4, /* flip direction 180* for exiting portal */
-   k_ent_gate_custom_mesh = 0x8, /* use a custom submesh instead of default */
-   k_ent_gate_locked      = 0x10,/* has to be unlocked to be useful */
-
-   k_ent_gate_clean_pass  = 0x20,/* player didn't rewind while getting here */
-   k_ent_gate_no_linkback = 0x40,/* NONLOCAL Recievers are not allowed to linkback through this gate */
-   k_ent_gate_passive     = 0x80
-};
-
-struct ent_gate
-{
-   u32 flags,
-       target, 
-       key;
-
-   f32 dimensions[3],
-       co[2][3];
-
-   f32 q[2][4];
-
-   /* runtime */
-   f32 to_world[4][3], transport[4][3];
-   union
-   {
-      u32 timing_version;
-      u16 remote_addon_id;
-
-      struct
-      {
-         u8 ref_count;
-      };
-   };
-
-   union
-   {
-      f64 timing_time;
-      u32 cubemap_id;
-   };
-
-   u16 routes[4];       /* routes that pass through this gate */
-   u8 route_count;
-
-   /* v102+ */
-   u32 submesh_start, submesh_count;
-};
-
-struct ent_route_node
-{
-   f32 co[3];
-   u8 ref_count, ref_total;
-};
-
-struct ent_path_index
-{
-   u16 index;
-};
-
-struct ent_checkpoint
-{
-   u16 gate_index,
-       path_start,
-       path_count;
-
-   /* EXTENSION */
-   f32 best_time;
-};
-
-enum ent_route_flag 
-{
-   k_ent_route_flag_achieve_silver = 0x1,
-   k_ent_route_flag_achieve_gold   = 0x2,
-
-   k_ent_route_flag_out_of_zone    = 0x10,
-   k_ent_region_flag_hasname       = 0x20,
-   k_ent_route_flag_target         = 0x40
-};
-
-struct ent_route
-{
-   union
-   {
-      struct mdl_transform transform;
-      u32 official_track_id;   /* TODO: remove this */
-   }
-   anon;
-
-   u32 pstr_name;
-   u16 checkpoints_start,
-       checkpoints_count;
-
-   f32 colour[4];
-
-   /* runtime */
-   u16 active_checkpoint, 
-       valid_checkpoints;
-
-   f32 factive;
-   f32 board_transform[4][3];
-   struct mdl_submesh sm;
-   f64 timing_base;
-
-   u32 id_camera; /* v103+ */
-
-   /* v104+, but always accessible */
-   u32 flags;
-   f64 best_laptime;
-   f32 ui_stopper, ui_residual;
-   i16 ui_first_block_width, ui_residual_block_w;
-};
-
-struct ent_water
-{
-   struct mdl_transform transform;
-   f32 max_dist;
-   u32 reserved0, reserved1;
-};
-
-struct ent_audio_clip
-{
-   union
-   {
-      struct mdl_file file;
-      // struct audio_clip clip; FIXME, MUST ALLOCATE THIS LATER INSTEAD OF PUTTING IT IN THE FUCKING FILE STRUCT. WHO CARES IF WE LOSE HALF A KB
-   }
-   _;
-
-   f32 probability;
-};
-
-struct volume_particles
-{
-   u32 blank, blank2;
-};
-
-struct volume_trigger
-{
-   i32 blank, blank2;
-};
-
-struct volume_interact
-{
-   i32 blank;
-   u32 pstr_text;
-};
-
-enum ent_volume_flag
-{
-   k_ent_volume_flag_particles = 0x1,
-   k_ent_volume_flag_disabled  = 0x2,
-   k_ent_volume_flag_removed0  = 0x4,
-   k_ent_volume_flag_interact  = 0x8,
-   k_ent_volume_flag_water     = 0x10,
-   k_ent_volume_flag_repeatable= 0x20
-};
-
-struct ent_volume
-{
-   struct mdl_transform transform;
-   f32 to_world[4][3];
-
-   union
-   {
-      f32 to_local[4][3];
-      f32 particle_co[3];
-   };
-
-   u32 flags;
-   u32 deleted0;
-   union
-   {
-      struct volume_trigger trigger;
-      struct volume_interact interact;
-      struct volume_particles particles;
-   };
-};
-
-struct ent_audio
-{
-   struct mdl_transform transform;
-   u32 flags,
-       clip_start,
-       clip_count;
-   f32 volume, crossfade;
-   u32 behaviour,
-       group,
-       probability_curve,
-       max_channels;
-};
-
-enum
-{
-   k_ent_marker_flag_hidden = 0x1,
-   k_ent_marker_flag_gui_icon = 0x8
-};
-
-struct ent_marker
-{
-   struct mdl_transform transform;
-   u32 pstr_alias;
-   u32 flags;
-};
-
-enum skateshop_type
-{
-   k_skateshop_type_boardshop = 0,
-   k_skateshop_type_charshop = 1,
-   k_skateshop_type_worldshop = 2,
-   k_skateshop_type_DELETED = 3,
-   k_skateshop_type_server = 4
-};
-
-struct ent_skateshop
-{
-   struct mdl_transform transform;
-   u32 type, id_camera;
-
-   union
-   {
-      struct
-      {
-         u32 id_display,
-             id_info,
-             id_rack;
-      }
-      boards;
-
-      struct
-      {
-         u32 id_display,
-             id_info,
-             id_rack;
-      }
-      character;
-
-      struct
-      {
-         u32 id_display,
-             id_info;
-      }
-      worlds;
-
-      struct
-      {
-         u32 id_lever;
-      }
-      server;
-   };
-};
-
-struct ent_swspreview
-{
-   u32 id_camera, id_display, id_display1;
-};
-
-struct ent_traffic
-{
-   struct mdl_transform transform;
-   u32 submesh_start,
-       submesh_count,
-       start_node,
-       node_count;
-   f32 speed,
-       t;
-   u32 index;     /* into the path */
-};
-
-struct ent_camera
-{
-   f32 co[3], r[3];
-   f32 fov;
-};
-
-#if (VG_MODEL_VERSION_MIN <= 107)
-struct ent_camera_v107
-{
-   struct mdl_transform transform;
-   f32 fov;
-};
-
-static inline void fix_ent_camera_v107( struct ent_camera_v107 *old, struct ent_camera *new )
-{
-   f32 dir[3] = {0.0f,-1.0f,0.0f};
-   mdl_transform_vector( &old->transform, dir, dir );
-   v3_angles( dir, new->r );
-   v3_copy( old->transform.co, new->co );
-   new->fov = old->fov;
-}
-#endif
-
-enum world_flag
-{
-   k_world_flag_fixed_time = 0x1,
-   k_world_flag_water_is_safe = 0x2,
-   k_world_flag_no_skating = 0x4,
-   k_world_flag_no_rewind = 0x8
-};
-
-struct ent_worldinfo
-{
-   u32 pstr_name, pstr_author, pstr_desc;
-   f32 timezone;
-   u32 pstr_skybox;
-   u32 flags;
-   f32 wind_scale;
-};
-
-enum channel_behaviour
-{
-   k_channel_behaviour_unlimited = 0,
-   k_channel_behaviour_discard_if_full = 1,
-   k_channel_behaviour_crossfade_if_full = 2
-};
-
-enum probability_curve{
-   k_probability_curve_constant = 0,
-   k_probability_curve_wildlife_day = 1,
-   k_probability_curve_wildlife_night = 2
-};
-
-struct ent_font
-{
-   u32 alias,
-       variant_start,
-       variant_count,
-       glyph_start,
-       glyph_count,
-       glyph_utf32_base;
-};
-
-struct ent_font_variant
-{
-   u32 name,
-       material_id;
-};
-
-struct ent_glyph
-{
-   f32 size[2];
-   u32 indice_start,
-       indice_count;
-};
-
-struct ent_ccmd
-{
-   u32 pstr_command;
-};
-
-enum ent_script_flag
-{
-   k_ent_script_flag_linked = 0x1
-};
-
-struct ent_script
-{
-   union
-   {
-      u32 pstr_script_name,  /* When its in the file */
-          script_id;         /* When its runtime */
-   };
-
-   u32 entity_list_id;
-   u32 flags;
-};
-
-enum ent_atom_flag
-{
-   k_ent_atom_global = 0x1,
-   k_ent_atom_scrap  = 0x2
-};
-
-struct ent_atom
-{
-   union
-   {
-      u32 pstr_alias;
-      i32 scrap_value;
-   };
-
-   u32 flags;
-};
-
-enum ent_cutscene_flag
-{
-   k_ent_cutscene_freeze_player = 0x1,
-};
-
-struct ent_cutscene
-{
-   u32 pstr_path,
-       flags;
-};
-
-enum ent_objective_filter{
-   k_ent_objective_filter_none            = 0x00000000,
-   k_ent_objective_filter_trick_shuvit    = 0x00000001,
-   k_ent_objective_filter_trick_kickflip  = 0x00000002,
-   k_ent_objective_filter_trick_treflip   = 0x00000004,
-   k_ent_objective_filter_trick_any       = 
-      k_ent_objective_filter_trick_shuvit|
-      k_ent_objective_filter_trick_treflip|
-      k_ent_objective_filter_trick_kickflip,
-   k_ent_objective_filter_flip_back       = 0x00000008,
-   k_ent_objective_filter_flip_front      = 0x00000010,
-   k_ent_objective_filter_flip_any        =
-      k_ent_objective_filter_flip_back|
-      k_ent_objective_filter_flip_front,
-   k_ent_objective_filter_grind_truck_any = 0x00000020,
-   k_ent_objective_filter_grind_board_any = 0x00000040,
-   k_ent_objective_filter_grind_any       =
-      k_ent_objective_filter_grind_truck_any|
-      k_ent_objective_filter_grind_board_any,
-   k_ent_objective_filter_footplant       = 0x00000080,
-   k_ent_objective_filter_passthrough     = 0x00000100,
-   k_ent_objective_filter_glider          = 0x00000200
-};
-
-enum ent_objective_flag 
-{
-   k_ent_objective_hidden = 0x1,
-   k_ent_objective_passed = 0x2,
-   k_ent_objective_failed = 0x4
-};
-
-struct ent_objective
-{
-   struct mdl_transform transform;
-   u32 submesh_start,
-       submesh_count,
-       flags,
-       id_next,
-       filter,filter2,
-       deleted0;
-   i32 deleted1;
-   f32 time_limit;
-   u32 pstr_description_ui;
-};
-
-enum ent_challenge_flag 
-{
-   k_ent_challenge_timelimit = 0x1,
-   //k_ent_challenge_is_story  = 0x2,
-   k_ent_challenge_locked    = 0x4,
-   k_ent_challenge_any_order = 0x8,
-   k_ent_challenge_target    = 0x10
-};
-
-struct ent_challenge
-{
-   struct mdl_transform transform;
-   u32 pstr_alias,
-       flags;
-
-   u32 deleted0;//on_activate_id;
-   u32 deleted1;//on_activate_event;
-   u32 deleted2;//on_complete_id;
-   i32 deleted3;//on_complete_event;
-
-   u32 first_objective_id;
-   u32 camera_id;
-
-   u32 status;
-   u32 reset_spawn_id;
-};
-
-struct ent_cubemap 
-{
-   f32 co[3];
-   u32 resolution, live, texture_id, 
-       framebuffer_id, renderbuffer_id, placeholder[2];
-};
-
-enum prop_flag
-{
-   k_prop_flag_hidden = 0x1,
-   k_prop_flag_spinning = 0x2,
-   k_prop_flag_collider = 0x4,
-   k_prop_flag_spinning_fast = 0x8,
-};
-
-struct ent_prop 
-{
-   struct mdl_transform transform;
-   u32 submesh_start, submesh_count, flags, pstr_alias;
-};
-
-enum editer_type
-{
-   k_editer_type_toggle = 0,
-   k_editer_type_slider = 1,
-   k_editer_type_selecter = 2
-};
-
-struct editer_property
-{
-   u32 pstr_alias;
-   u8  ui_type;
-
-   union 
-   {
-      u32 _u32;
-      f32 _f32;
-      u32 pstr_options;
-   }
-   max;
-};
-
-struct editer_item
-{
-   struct mdl_transform transform;
-   u32 submesh_start, submesh_count;
-
-   u32 pstr_visibility,
-       pstr_uv_x,
-       pstr_uv_y;
-   u16 discard_send,
-       discard_mask;
-};
-
-struct ent_region 
-{
-   struct mdl_transform transform;
-   u32 submesh_start, submesh_count, pstr_title, flags;
-
-   union
-   {
-      struct{ u32 zone_volume; } v105;
-      struct{ u32 id_list; } v109;
-   };
-
-   /* 105+ */
-   u32 deleted01[2];
-};
-
-enum ent_glider_flag
-{
-   k_ent_glider_flag_locked = 0x1
-};
-struct ent_glider 
-{
-   struct mdl_transform transform;
-   u32 flags;
-   f32 cooldown;
-};
-
-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
-};
-
-enum ent_event_flags
-{
-   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,
-       pstr_recieve_event,
-       source_entity_id,
-       recieve_entity_id,
-       flags;
-
-   f32 delay;
-   u32 unused0;
-
-   union
-   {
-      i32 const_i32;
-      f32 const_f32;
-      u32 const_entity_id;
-      u32 const_pstr;
-      u32 pstr_data_alias;
-   }
-   data;
-};
diff --git a/source/vg_model/metascene.c b/source/vg_model/metascene.c
deleted file mode 100644 (file)
index 7714c2e..0000000
+++ /dev/null
@@ -1,499 +0,0 @@
-#include "common_api.h"
-#include "common_thread_api.h"
-#include "metascene.h"
-#include "maths/common_maths.h"
-#include "model.h"
-#include "entity.h"
-#include "shader_props.h"
-#include "array_file.h"
-
-void metascene_load( struct metascene *ms, const c8 *path, struct stack_allocator *stack )
-{
-   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
-   zero_buffer( ms, sizeof(struct metascene) );
-
-   struct stream stream;
-   ASSERT_CRITICAL( stream_open_file( &stream, path, k_stream_read ) );
-
-   u32 temp_frame = _start_temporary_frame();
-   {
-      struct array_file_context af;
-      ASSERT_CRITICAL( af_open_stream( &af, &stream, MS_VERSION_MIN, MS_VERSION_NR, _temporary_stack_allocator() ) );
-
-      struct array_file_ptr strings;
-      af_load_array( &af, &strings, "strings", stack, 1 );
-      ms->packed_strings = strings.data;
-      af_load_array( &af, &ms->infos, "ms_scene_info", stack, sizeof(struct ms_scene_info) );
-      af_load_array( &af, &ms->instances, "ms_instance", stack, sizeof(struct ms_instance) );
-      af_load_array( &af, &ms->overrides, "ms_override", stack, sizeof(struct ms_override) );
-      af_load_array( &af, &ms->strips, "ms_strip", stack, sizeof(struct ms_strip) );
-      af_load_array( &af, &ms->tracks, "ms_track", stack, sizeof(struct ms_track) );
-      af_load_array( &af, &ms->keyframes, "ms_keyframe", stack, sizeof(struct ms_keyframe) );
-      af_load_array( &af, &ms->cameras, "ent_camera", stack, sizeof(struct ent_camera) );
-      af_load_array( &af, &ms->audios, "ent_audio", stack, sizeof(struct ent_audio) );
-      af_load_array( &af, &ms->audio_clips, "ent_audio_clip", stack, sizeof(struct ent_audio_clip) );
-      af_load_array( &af, &ms->curves, "ms_curves", stack, sizeof(struct ms_curve_keyframe) );
-
-      ASSERT_CRITICAL( af_arrcount( &ms->infos ) );
-      struct ms_scene_info *src_inf = af_arritm( &ms->infos, 0 );
-      ms->info = *src_inf;
-
-      stream_close( &stream );
-   }
-   _end_temporary_frame( temp_frame );
-}
-
-u32 skeleton_bone_id( struct ms_skeleton *skele, const c8 *name )
-{
-   for( u32 i=1; i<skele->bone_count; i++ )
-      if( compare_buffers( skele->bones[i].name, 0, name, 0 ) )
-         return i;
-
-   $log( $fatal, {"skeleton_bone_id( *, "}, {name}, {" ); -> Bone does not exist"} );
-   _fatal_exit();
-   return 0;
-}
-
-void keyframe_copy_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, i32 num )
-{
-   for( i32 i=0; i<num; i++ )
-      kfb[i] = kfa[i];
-}
-
-
-/* apply a rotation from the perspective of root */
-void keyframe_rotate_around( struct ms_keyframe *kf, f32 origin[3], f32 offset[3], f32 q[4] )
-{
-   f32 v0[3], co[3];
-   v3_add( kf->co, offset, co );
-   v3_sub( co, origin, v0 );
-   q_mulv( q, v0, v0 );
-   v3_add( v0, origin, co );
-   v3_sub( co, offset, kf->co );
-   q_mul( q, kf->q, kf->q );
-   q_normalize( kf->q );
-}
-
-void keyframe_lerp( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd )
-{
-   v3_lerp( kfa->co, kfb->co, t, kfd->co );
-   q_nlerp( kfa->q,  kfb->q,  t, kfd->q );
-   v3_lerp( kfa->s,  kfb->s,  t, kfd->s );
-}
-
-/*
- * Lerp between two sets of keyframes and store in dest. Rotations use Nlerp.
- */
-void keyframe_lerp_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd, i32 count )
-{
-   if( t <= 0.0001f )
-   {
-      keyframe_copy_pose( kfa, kfd, count );
-      return;
-   }
-   else if( t >= 0.9999f )
-   {
-      keyframe_copy_pose( kfb, kfd, count );
-      return;
-   }
-
-   for( i32 i=0; i<count; i++ )
-      keyframe_lerp( kfa+i, kfb+i, t, kfd+i );
-}
-
-void skeleton_lerp_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd )
-{
-   keyframe_lerp_pose( kfa, kfb, t, kfd, skele->bone_count-1 );
-}
-
-void skeleton_copy_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfd )
-{
-   keyframe_copy_pose( kfa, kfd, skele->bone_count-1 );
-}
-
-/*
- * Sample animation between 2 closest frames using time value. Output is a
- * keyframe buffer that is allocated with an appropriate size
- *
- * Time is in SECONDS
- */
-void skeleton_sample_anim( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output )
-{
-   struct ms_strip *strip = anim->strip;
-   f32 animtime  = fmodf( time*anim->framerate, (f32)strip->strip.length ),
-       animframe = floorf( animtime ),
-       t = animtime - animframe;
-
-   u32 frame = (u32)animframe % strip->strip.length,
-       next  = (frame+1) % strip->strip.length;
-
-   struct ms_keyframe *base  = anim->keyframes_base + strip->strip.count*frame,
-                      *nbase = anim->keyframes_base + strip->strip.count*next;
-   skeleton_lerp_pose( skele, base, nbase, t, output );
-}
-
-/* time is in SECONDS */
-i32 skeleton_sample_anim_clamped( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output )
-{
-   struct ms_strip *strip = anim->strip;
-   f32 end = (strip->strip.length-1)/anim->framerate;
-   skeleton_sample_anim( skele, anim, f32_min( end, time ), output );
-   if( time > end ) return 0;
-   else             return 1;
-}
-
-static i32 should_apply_bone( struct ms_skeleton *skele, u32 id, enum anim_apply type )
-{
-   struct ms_skeleton_bone *sb = &skele->bones[ id ],
-                           *sp = &skele->bones[ sb->parent ];
-   if( type == k_anim_apply_defer_ik )
-   {
-      if( ((sp->flags & k_bone_flag_ik) && !(sb->flags & k_bone_flag_ik)) || sp->defer )
-      {
-         sb->defer = 1;
-         return 0;
-      }
-      else
-      {
-         sb->defer = 0;
-         return 1;
-      }
-   }
-   else if( type == k_anim_apply_deffered_only )
-   {
-      if( sb->defer )
-         return 1;
-      else
-         return 0;
-   }
-
-   return 1;
-}
-
-/*
- * Apply block of keyframes to skeletons final pose
- */
-void skeleton_apply_pose( struct ms_skeleton *skele, struct ms_keyframe *pose, enum anim_apply passtype, f32 final_mtx[][4][3] )
-{
-   if( passtype == k_anim_apply_absolute )
-   {
-      for( u32 i=1; i<skele->bone_count; i++ )
-      {
-         struct ms_keyframe *kf = &pose[i-1];
-         q_m3x3( kf->q, final_mtx[i] );
-         m3x3_scale( final_mtx[i], kf->s );
-         v3_copy( kf->co, final_mtx[i][3] );
-      }
-      return;
-   }
-
-   m4x3_identity( final_mtx[0] );
-   skele->bones[0].defer = 0;
-   skele->bones[0].flags &= ~k_bone_flag_ik;
-
-   for( u32 i=1; i<skele->bone_count; i++ )
-   {
-      struct ms_skeleton_bone *sb = &skele->bones[i];
-      if( !should_apply_bone( skele, i, passtype ) )
-         continue;
-
-      sb->defer = 0;
-
-      /* process pose */
-      f32 posemtx[4][3];
-      f32 temp_delta[3];
-      v3_sub( skele->bones[i].co, skele->bones[sb->parent].co, temp_delta );
-
-      /* pose matrix */
-      struct ms_keyframe *kf = &pose[i-1];
-      q_m3x3( kf->q, posemtx );
-      m3x3_scale( posemtx, kf->s );
-      v3_copy( kf->co, posemtx[3] );
-      v3_add( temp_delta, posemtx[3], posemtx[3] );
-
-      /* final matrix */
-      m4x3_mul( final_mtx[ sb->parent ], posemtx, final_mtx[i] );
-   }
-}
-
-/* 
- * Take the final matrices and decompose it into an absolute positioned anim
- */
-void skeleton_decompose_mtx_absolute( struct ms_skeleton *skele, struct ms_keyframe *anim, f32 final_mtx[][4][3] )
-{
-   for( u32 i=1; i<skele->bone_count; i++ )
-   {
-      struct ms_keyframe *kf = &anim[i-1];
-      m4x3_decompose( final_mtx[i], kf->co, kf->q, kf->s );
-   }
-}
-
-/* 
- * creates the reference inverse matrix for an IK bone, as it has an initial 
- * intrisic rotation based on the direction that the IK is setup..
- */
-void skeleton_inverse_for_ik( struct ms_skeleton *skele, f32 ivaxis[3], u32 id, f32 inverse[3][3] )
-{
-   v3_copy( ivaxis, inverse[0] );
-   v3_copy( skele->bones[id].end, inverse[1] );
-   v3_normalize( inverse[1] );
-   v3_cross( inverse[0], inverse[1], inverse[2] );
-   m3x3_transpose( inverse, inverse );
-}
-
-/*
- * Creates inverse rotation matrices which the IK system uses.
- */
-void skeleton_create_inverses( struct ms_skeleton *skele )
-{
-   /* IK: inverse 'plane-bone space' axis '(^axis,^bone,...)[base] */
-   for( u32 i=0; i<skele->ik_count; i++ )
-   {
-      struct ms_skeleton_ik *ik = &skele->ik[i];
-      f32 iv0[3], iv1[3], ivaxis[3];
-      v3_sub( skele->bones[ik->target].co, skele->bones[ik->lower].co, iv0 );
-      v3_sub( skele->bones[ik->pole].co,   skele->bones[ik->lower].co, iv1 );
-      v3_cross( iv0, iv1, ivaxis );
-      v3_normalize( ivaxis );
-
-      skeleton_inverse_for_ik( skele, ivaxis, ik->lower, ik->ia );
-      skeleton_inverse_for_ik( skele, ivaxis, ik->upper, ik->ib );
-   }
-}
-
-/*
- * Apply a model matrix to all bones, should be done last
- */
-void skeleton_apply_transform( struct ms_skeleton *skele, f32 transform[4][3], f32 final_mtx[][4][3] )
-{
-   for( u32 i=0; i<skele->bone_count; i++ )
-      m4x3_mul( transform, final_mtx[i], final_mtx[i] );
-}
-
-/*
- * Apply an inverse matrix to all bones which maps vertices from bind space into
- * bone relative positions
- */
-void skeleton_apply_inverses( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
-{
-   for( u32 i=0; i<skele->bone_count; i++ )
-   {
-      struct ms_skeleton_bone *sb = &skele->bones[i];
-      f32 inverse[4][3];
-      m3x3_identity( inverse );
-      v3_negate( sb->co, inverse[3] );
-      m4x3_mul( final_mtx[i], inverse, final_mtx[i] );
-   }
-}
-
-/*
- * Apply all IK modifiers (2 bone ik reference from blender is supported)
- */
-void skeleton_apply_ik_pass( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
-{
-   for( u32 i=0; i<skele->ik_count; i++ )
-   {
-      struct ms_skeleton_ik *ik = &skele->ik[i];
-      f32 v0[3], /* base -> target */
-          v1[3], /* base -> pole */
-          vaxis[3];
-      f32 co_base[3],
-          co_target[3],
-          co_pole[3];
-
-      v3_copy( final_mtx[ik->lower][3], co_base );
-      v3_copy( final_mtx[ik->target][3], co_target );
-      v3_copy( final_mtx[ik->pole][3], co_pole );
-
-      v3_sub( co_target, co_base, v0 );
-      v3_sub( co_pole, co_base, v1 );
-      v3_cross( v0, v1, vaxis );
-      v3_normalize( vaxis );
-      v3_normalize( v0 );
-      v3_cross( vaxis, v0, v1 );
-
-      /* localize problem into [x:v0,y:v1] 2d plane */
-      f32 base[2] = { v3_dot( v0, co_base   ), v3_dot( v1, co_base   ) },
-          end[2]  = { v3_dot( v0, co_target ), v3_dot( v1, co_target ) },
-          knee[2];
-
-      /* Compute angles (basic trig)*/
-      f32 delta[2];
-      v2_sub( end, base, delta );
-
-      f32 l1 = v3_length( skele->bones[ik->lower].end ),
-          l2 = v3_length( skele->bones[ik->upper].end ),
-          d = f32_clamp( v2_length(delta), fabsf(l1 - l2), l1+l2-0.00001f ),
-          c = acosf( (l1*l1 + d*d - l2*l2) / (2.0f*l1*d) ),
-          rot = atan2f( delta[1], delta[0] ) + c - VG_PIf/2.0f;
-
-      knee[0] = sinf(-rot) * l1;
-      knee[1] = cosf(-rot) * l1;
-
-      m4x3_identity( final_mtx[ik->lower] );
-      m4x3_identity( final_mtx[ik->upper] );
-
-      /* create rotation matrix */
-      f32 co_knee[3];
-      v3_muladds( co_base, v0, knee[0], co_knee );
-      v3_muladds( co_knee, v1, knee[1], co_knee );
-      // vg_line( co_base, co_knee, 0xff00ff00 );
-
-      f32 transform[4][3];
-      v3_copy( vaxis, transform[0] );
-      v3_muls( v0, knee[0], transform[1] );
-      v3_muladds( transform[1], v1, knee[1], transform[1] );
-      v3_normalize( transform[1] );
-      v3_cross( transform[0], transform[1], transform[2] );
-      v3_copy( co_base, transform[3] );
-
-      m3x3_mul( transform, ik->ia, transform );
-      m4x3_copy( transform, final_mtx[ik->lower] );
-
-      /* upper/knee bone */
-      v3_copy( vaxis, transform[0] );
-      v3_sub( co_target, co_knee, transform[1] );
-      v3_normalize( transform[1] );
-      v3_cross( transform[0], transform[1], transform[2] );
-      v3_copy( co_knee, transform[3] );
-
-      m3x3_mul( transform, ik->ib, transform );
-      m4x3_copy( transform, final_mtx[ik->upper] );
-   }
-}
-
-/*
- * Applies the typical operations that you want for an IK rig: 
- *    Pose, IK, Pose(deferred), Inverses, Transform
- */
-void skeleton_apply_standard( struct ms_skeleton *skele, struct ms_keyframe *pose, f32 transform[4][3], f32 final_mtx[][4][3] )
-{
-   skeleton_apply_pose( skele, pose, k_anim_apply_defer_ik, final_mtx );
-   skeleton_apply_ik_pass( skele, final_mtx );
-   skeleton_apply_pose( skele, pose, k_anim_apply_deffered_only, final_mtx );
-   skeleton_apply_inverses( skele, final_mtx );
-   skeleton_apply_transform( skele, transform, final_mtx );
-}
-
-void skeleton_alloc_from( struct ms_skeleton *skele, struct stack_allocator *stack, 
-                          struct vg_model *model, struct mdl_armature *armature )
-{
-   skele->bone_count     = armature->bone_count+1;
-   skele->ik_count       = 0;
-   skele->collider_count = 0;
-
-   for( u32 i=0; i<armature->bone_count; i++ )
-   {
-      struct mdl_bone *bone = &model->bones[ armature->bone_start+i ];
-      if( bone->flags & k_bone_flag_ik )
-         skele->ik_count ++;
-      if( bone->collider )
-         skele->collider_count ++;
-   }
-
-   u32 bone_size = sizeof(struct ms_skeleton_bone) * skele->bone_count,
-       ik_size   = sizeof(struct ms_skeleton_ik)   * skele->ik_count;
-
-   skele->bones = stack_allocate( stack, bone_size, 8, NULL );
-   skele->ik    = stack_allocate( stack, ik_size, 8, NULL );
-
-   zero_buffer( skele->bones, bone_size );
-   zero_buffer( skele->ik, ik_size );
-}
-
-/* Setup a skeleton from model. mdl's metadata should stick around */
-void skeleton_setup( struct ms_skeleton *skele, struct vg_model *model, u32 index, struct stack_allocator *stack )
-{
-   u32 ik_count = 0, collider_count = 0;
-   skele->bone_count = 0;
-   skele->bones = NULL;
-
-   if( !model->armature_count )
-   {
-      $log( $fatal, {"No skeleton in model"} );
-      _fatal_exit();
-   }
-
-   struct mdl_armature *armature = &model->armatures[ index ];
-   skeleton_alloc_from( skele, stack, model, armature );
-
-   for( u32 i=0; i<armature->bone_count; i++ )
-   {
-      struct mdl_bone *bone = &model->bones[ armature->bone_start+i ];
-      struct ms_skeleton_bone *sb = &skele->bones[i+1];
-
-      v3_copy( bone->co, sb->co );
-      v3_copy( bone->end, sb->end );
-
-      sb->parent = bone->parent;
-      sb->name   = af_str( model->packed_strings, bone->pstr_name );
-      sb->flags  = bone->flags;
-      sb->collider = bone->collider;
-      sb->orig_bone = bone;
-
-      if( sb->flags & k_bone_flag_ik )
-      {
-         skele->bones[ sb->parent ].flags |= k_bone_flag_ik;
-         
-         if( ik_count == skele->ik_count )
-         {
-            $log( $fatal, {"Too many ik bones, corrupt model file"} );
-            _fatal_exit();
-         }
-
-         struct ms_skeleton_ik *ik = &skele->ik[ ik_count ++ ];
-         ik->upper = i+1;
-         ik->lower = bone->parent;
-         ik->target = bone->ik_target;
-         ik->pole = bone->ik_pole;
-      }
-
-      box_copy( bone->hitbox, sb->hitbox );
-      if( bone->collider )
-      {
-         if( collider_count == skele->collider_count )
-         {
-            $log( $fatal, {"Too many collider bones"} );
-            _fatal_exit();
-         }
-         collider_count ++;
-      }
-   }
-
-   /* fill in implicit root bone */
-   v3_fill( skele->bones[0].co, 0 );
-   v3_copy( (f32[3]){0.0f,1.0f,0.0f}, skele->bones[0].end );
-   skele->bones[0].parent = 0xffffffff;
-   skele->bones[0].flags = 0;
-   skele->bones[0].name = "[root]";
-
-   skeleton_create_inverses( skele );
-   $log( $ok, {"Loaded skeleton with "}, $unsigned( skele->bone_count ), {" bones"} );
-   $log( $ok, {"                     "}, $unsigned( skele->collider_count), {" colliders"} );
-}
-
-#if 0
-void skeleton_debug( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
-{
-   for( u32 i=1; i<skele->bone_count; i ++ )
-   {
-      struct ms_skeleton_bone *sb = &skele->bones[i];
-      f32 p0[3], p1[3];
-      v3_copy( sb->co, p0 );
-      v3_add( p0, sb->end, p1 );
-
-      m4x3_mulv( final_mtx[i], p0, p0 );
-      m4x3_mulv( final_mtx[i], p1, p1 );
-
-      if( sb->flags & k_bone_flag_deform )
-      {
-         if( sb->flags & k_bone_flag_ik )
-            vg_line( p0, p1, 0xff0000ff );
-         else
-            vg_line( p0, p1, 0xffcccccc );
-      }
-      else
-         vg_line( p0, p1, 0xff00ffff );
-   }
-}
-#endif
diff --git a/source/vg_model/metascene.h b/source/vg_model/metascene.h
deleted file mode 100644 (file)
index 24a61c4..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-#pragma once
-#include "array_file.h"
-#include "model.h"
-
-#define MS_VERSION_NR 2
-#define MS_VERSION_MIN 2
-
-struct ms_scene_info
-{
-   f32 framerate;
-   u32 end_frame;
-};
-
-struct metascene
-{
-   const void *packed_strings;
-   struct ms_scene_info info;
-   struct array_file_ptr infos,
-                         instances,
-                         overrides,
-                         strips,
-                         tracks,
-                         keyframes,
-                         curves,
-
-                         audios, /* kinda temp? */
-                         audio_clips,
-                         cameras;
-};
-
-struct ms_instance
-{
-   u32 pstr_name,
-       override_start,
-       override_count;
-};
-
-struct ms_override
-{
-   u32 entity_type, pstr_name;
-   struct mdl_transform transform;
-};
-
-enum ms_strip_mode
-{
-   k_ms_strip_mode_keyframes = 0x1,
-   k_ms_strip_mode_curves = 0x2,
-   k_ms_strip_mode_animation = 0x1|0x2,
-
-   k_ms_strip_mode_camera = 0x10,
-   k_ms_strip_mode_event = 0x20,
-   k_ms_strip_mode_subtitle = 0x40,
-   k_ms_strip_mode_fadeout = 0x80,
-};
-
-struct ms_strip
-{
-   u8  mode;
-   i32 offset;
-
-   union
-   {
-      struct
-      {
-         u32 start, count, length,
-             pstr_name,
-             pstr_internal_name,
-             instance_id, object_id;
-         f32 timing_offset;
-      }
-      strip;
-
-      struct
-      {
-         u32 entity_id;
-      }
-      camera;
-
-      struct
-      {
-         u32 pstr_en, res0, res1, res2;
-         u8  character;
-      }
-      subtitle;
-
-      struct
-      {
-         u32 pstr_string;
-      }
-      event;
-   };
-};
-
-struct ms_track
-{
-   u32 keyframe_start,
-       keyframe_count,
-       pstr_datapath,
-       semantic_type;
-};
-
-struct ms_curve_keyframe
-{
-   f32 co[2], l[2], r[2];
-};
-
-struct ms_keyframe
-{
-   f32 co[3], s[3];
-   f32 q[4];
-};
-
-/* skeletons 
- * ------------------------------------------------------------------------------------------------------------------ */
-
-struct ms_skeleton
-{
-   struct ms_skeleton_bone
-   {
-      f32 co[3], end[3];
-      u32 parent;
-
-      u32 flags;
-      int defer;
-
-      //ms_keyframe kf;
-      struct mdl_bone *orig_bone;
-      u32 collider;     // TODO: SOA
-      f32 hitbox[2][3]; // TODO: SOA
-      const char *name; // TODO: SOA
-   }
-   *bones;
-   u32 bone_count;
-
-   struct ms_skeleton_ik
-   {
-      u32 lower, upper, target, pole;
-      f32 ia[3][3], ib[3][3];
-   }
-   *ik;
-   u32 ik_count;
-   u32 collider_count,
-       bindable_count;
-};
-
-void metascene_load( struct metascene *ms, const c8 *path, struct stack_allocator *stack );
-void keyframe_copy_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, i32 num );
-void keyframe_rotate_around( struct ms_keyframe *kf, f32 origin[3], f32 offset[3], f32 q[4] );
-void keyframe_lerp( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd );
-void keyframe_lerp_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd, i32 count );
-void skeleton_alloc_from( struct ms_skeleton *skele, struct stack_allocator *stack, 
-                          struct vg_model *model, struct mdl_armature *armature );
-u32 skeleton_bone_id( struct ms_skeleton *skele, const c8 *name );
-void skeleton_lerp_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd );
-void skeleton_copy_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfd );
-
-struct ms_skeletal_animation
-{
-   struct ms_strip *strip;
-   struct ms_keyframe *keyframes_base;
-   f32 framerate;
-};
-
-void skeleton_sample_anim( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output );
-int skeleton_sample_anim_clamped( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output );
-
-enum anim_apply
-{
-   k_anim_apply_always,
-   k_anim_apply_defer_ik,
-   k_anim_apply_deffered_only,
-   k_anim_apply_absolute
-}
-anim_apply;
-
-void skeleton_apply_pose( struct ms_skeleton *skele, struct ms_keyframe *pose, enum anim_apply passtype, f32 final_mtx[][4][3] );
-void skeleton_decompose_mtx_absolute( struct ms_skeleton *skele, struct ms_keyframe *anim, f32 final_mtx[][4][3] );
-void skeleton_inverse_for_ik( struct ms_skeleton *skele, f32 ivaxis[3], u32 id, f32 inverse[3][3] );
-void skeleton_create_inverses( struct ms_skeleton *skele );
-void skeleton_apply_transform( struct ms_skeleton *skele, f32 transform[4][3], f32 final_mtx[][4][3] );
-void skeleton_apply_inverses( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
-void skeleton_apply_ik_pass( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
-void skeleton_apply_standard( struct ms_skeleton *skele, struct ms_keyframe *pose, f32 transform[4][3], f32 final_mtx[][4][3] );
-void skeleton_setup( struct ms_skeleton *skele, struct vg_model *model, u32 index, struct stack_allocator *stack );
-void skeleton_debug( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
diff --git a/source/vg_model/model.c b/source/vg_model/model.c
deleted file mode 100644 (file)
index 7393871..0000000
+++ /dev/null
@@ -1,549 +0,0 @@
-#include "common_api.h"
-#include "common_thread_api.h"
-#include "maths/common_maths.h"
-
-#include "model.h"
-#include "array_file.h"
-#include "opengl.h"
-#include "shader_props.h"
-#include <stddef.h>
-
-struct stream *vg_model_stream_pack_stream( struct vg_model_stream_context *ctx, struct mdl_file *file )
-{
-   if( !file->pack_size )
-   {
-      $log( $fatal, {"Packed file is only a header; it is not packed\n"},
-                    {"Path: "}, {af_str( &ctx->model->packed_strings, file->pstr_path )} );
-      _fatal_exit();
-   }
-
-   stream_seek( &ctx->stream, ctx->model->pack_base_offset + file->pack_offset );
-
-   // WE dont ever read backwards so this just sets us up an uppper bound to read against
-   ctx->stream.buffer_length = ctx->model->pack_base_offset + file->pack_offset + file->pack_size;
-   return &ctx->stream;
-}
-
-/* This also compiles them */
-static void vg_model_stream_materials( struct vg_model_stream_context *ctx, struct stack_allocator *stack )
-{
-   struct array_file_ptr mats_ptr;
-   af_load_array( &ctx->af, &mats_ptr, "mdl_material", stack, sizeof(struct mdl_material) );
-   ctx->model->materials = mats_ptr.data;
-   ctx->model->material_count = mats_ptr.count;
-
-   u32 size = sizeof(union shader_props) * mats_ptr.count;
-   ctx->model->shader_props = stack_allocate( stack, size, 8, "Compiled shader properties" );
-
-   /* 
-    * Step 0:
-    *  Acquiring the data source
-    *
-    * Step 1:
-    *  Converting into formal KV structure
-    *  We have 3 different modes;
-    *    v101+: old simple binary structures, requires 'generating' correct kvs
-    *    v106+: deprecated 'vg_msg' similar to kvs, only requires conversion
-    *    v110+: text KV's, direct parsing into vg_kvs
-    *
-    * Step 2:
-    *  Formal KV structure is then compiled into the binary union
-    */
-
-   /* step0 ----------------------------- */
-   u32 temp_frame = _start_temporary_frame();
-   {
-#if (VG_MODEL_VERSION_MIN <= 101)
-      struct array_file_ptr v101_materials;
-#endif
-#if (VG_MODEL_VERSION_MIN <= 106)
-      struct array_file_ptr v106_data;
-#endif
-      struct array_file_ptr v110_data;
-
-      if( ctx->model->version <= 105 )
-#if (VG_MODEL_VERSION_MIN <= 105)
-         af_load_array( &ctx->af, &v101_materials, "mdl_material", _temporary_stack_allocator(), sizeof(struct mdl_material_v101) );
-#else
-      {
-         $log( $fatal, {"Unsupported model version: "}, $unsigned( ctx->model->version ) );
-      }
-#endif
-#if (VG_MODEL_VERSION_MIN <= 109)
-      else if( ctx->model->version <= 109 )
-         af_load_array( &ctx->af, &v106_data, "shader_data", _temporary_stack_allocator(), 1 );
-#endif
-      else
-         af_load_array( &ctx->af, &v110_data, "shader_props", _temporary_stack_allocator(), 1 );
-
-      struct keyvalues kvs;
-      keyvalues_init( &kvs, _temporary_stack_allocator() );
-
-      /* step1 ----------------------------- */
-      if( ctx->model->version <= 105 )
-      {
-#if (VG_MODEL_VERSION_MIN <= 105)
-         for( u32 i=0; i<ctx->model->material_count; i ++ )
-         {
-            struct mdl_material *mat = &ctx->model->materials[ i ];
-            struct mdl_material_v101 *old = af_arritm( &v101_materials, i );
-
-            mat->props.kv_root = keyvalues_append_frame( &kvs, 0, NULL );
-
-            keyvalues_append_string( &kvs, mat->props.kv_root, "version", "101" );
-            keyvalues_append_u32s( &kvs, mat->props.kv_root, "tex_diffuse", &old->tex_diffuse, 1 );
-
-            if( mat->shader == k_shader_cubemap )
-            {
-               keyvalues_append_u32s( &kvs, mat->props.kv_root, "cubemap", &old->tex_none0, 1 );
-               keyvalues_append_f32s( &kvs, mat->props.kv_root, "tint", old->colour, 4 );
-            }
-            else if( mat->shader == k_shader_terrain_blend )
-            {
-               keyvalues_append_f32s( &kvs, mat->props.kv_root, "sand_colour", old->colour, 4 );
-               keyvalues_append_f32s( &kvs, mat->props.kv_root, "blend_offset", old->colour1, 2 );
-            }
-            else if( mat->shader == k_shader_standard_vertex_blend )
-            {
-               keyvalues_append_f32s( &kvs, mat->props.kv_root, "blend_offset", old->colour1, 2 );
-            }
-            else if( mat->shader == k_shader_water )
-            {
-               keyvalues_append_f32s( &kvs, mat->props.kv_root, "shore_colour", old->colour, 4 );
-               keyvalues_append_f32s( &kvs, mat->props.kv_root, "deep_colour", old->colour1, 4 );
-            }
-         }
-#else
-         ASSERT_CRITICAL( 0 );
-#endif
-      }
-      else if( ctx->model->version <= 109 )
-      {
-#if (VG_MODEL_VERSION_MIN <= 109)
-         for( u32 i=0; i<ctx->model->material_count; i ++ )
-         {
-            struct mdl_material *mat = &ctx->model->materials[ i ];
-            u32 root = keyvalues_append_frame( &kvs, 0, NULL );
-            keyvalues_append_string( &kvs, root, "version", "106" );
-
-            void *buffer = NULL;
-            if( v106_data.data )
-               buffer = v106_data.data + mat->props.kvs.offset;
-
-            vg_kvs_append_from_legacy_msg2( &kvs, root, buffer, mat->props.kvs.size );
-            mat->props.kv_root = root;
-         }
-#else
-         ASSERT_CRITICAL( 0 );
-#endif
-      }
-      else
-      {
-         for( u32 i=0; i<ctx->model->material_count; i ++ )
-         {
-            struct mdl_material *mat = &ctx->model->materials[ i ];
-            u32 root = keyvalues_append_frame( &kvs, 0, NULL );
-            keyvalues_append_string( &kvs, root, "version", "110" );
-
-            const c8 *buffer = NULL;
-            if( v110_data.data )
-            {
-               buffer = v110_data.data + mat->props.kvs.offset;
-               struct stream kv_stream;
-               stream_open_buffer_read( &kv_stream, buffer, mat->props.kvs.size, 0 );
-               keyvalues_parse_stream( &kvs, root, &kv_stream );
-            }
-            mat->props.kv_root = root;
-         }
-      }
-
-      /* step2 ----------------------------- */
-      for( u32 i=0; i<ctx->model->material_count; i ++ )
-      {
-         struct mdl_material *mat = &ctx->model->materials[ i ];
-         u32 root = mat->props.kv_root;
-         union shader_props *props = &ctx->model->shader_props[ i ];
-
-         if( mat->shader == k_shader_standard || 
-             mat->shader == k_shader_standard_cutout || 
-             mat->shader == k_shader_foliage ||
-             mat->shader == k_shader_fxglow )
-         {
-            keyvalues_read_u32s( &kvs, root, "tex_diffuse", (u32[]){0}, &props->standard.tex_diffuse, 1 );
-            keyvalues_read_u32s( &kvs, root, "render_flags", (u32[]){0}, &props->standard.render_flags, 1 );
-         }
-         else if( mat->shader == k_shader_standard_vertex_blend )
-         {
-            keyvalues_read_u32s( &kvs, root, "tex_diffuse", (u32[]){0}, &props->vertex_blend.tex_diffuse, 1 );
-            keyvalues_read_f32s( &kvs, root, "blend_offset", (f32[]){ 0.5, 0.0 }, props->vertex_blend.blend_offset, 2 );
-         }
-         else if( mat->shader == k_shader_cubemap )
-         {
-            keyvalues_read_u32s( &kvs, root, "tex_diffuse", (u32[]){0}, &props->cubemapped.tex_diffuse, 1 );
-            keyvalues_read_u32s( &kvs, root, "cubemap_entity", (u32[]){0}, &props->cubemapped.cubemap_entity, 1 );
-            keyvalues_read_f32s( &kvs, root, "tint", (f32[]){1.0,1.0,1.0,1.0}, props->cubemapped.tint, 4 );
-         }
-         else if( mat->shader == k_shader_terrain_blend )
-         {
-            keyvalues_read_u32s( &kvs, root, "tex_diffuse", (u32[]){0}, &props->terrain.tex_diffuse, 1 );
-            keyvalues_read_f32s( &kvs, root, "sand_colour", (f32[]){ 0.79, 0.63, 0.48, 1.0 }, props->terrain.sand_colour, 4 );
-            keyvalues_read_f32s( &kvs, root, "blend_offset", (f32[]){ 0.5, 0.0 }, props->terrain.blend_offset, 2 );
-         }
-         else if( mat->shader == k_shader_water )
-         {
-            keyvalues_read_f32s( &kvs, root, "shore_colour", (f32[]){0.03,0.32,0.61,1.0}, props->water.shore_colour, 4 );
-            keyvalues_read_f32s( &kvs, root, "deep_colour", (f32[]){0.0,0.006,0.03,1.0}, props->water.deep_colour, 4 );
-            keyvalues_read_f32s( &kvs, root, "fog_scale", (f32[]){0.04}, &props->water.fog_scale, 1 );
-            keyvalues_read_f32s( &kvs, root, "fresnel", (f32[]){5.0}, &props->water.fresnel, 1 );
-            keyvalues_read_f32s( &kvs, root, "water_scale", (f32[]){ 0.008 }, &props->water.water_sale, 1 );
-            keyvalues_read_f32s( &kvs, root, "wave_speed", (f32[]){0.008,0.006,0.003,0.03}, props->water.wave_speed, 4 );
-         }
-         else if( mat->shader == k_shader_workshop )
-         {
-            const c8 *_shader_prop_workshop_keys[] =
-            {
-               [k_workshop_shader_part_truck1  ] = "truck1",
-               [k_workshop_shader_part_truck2  ] = "truck2",
-               [k_workshop_shader_part_wheel1  ] = "wheel1",
-               [k_workshop_shader_part_wheel2  ] = "wheel2",
-               [k_workshop_shader_part_wheel3  ] = "wheel3",
-               [k_workshop_shader_part_wheel4  ] = "wheel4",
-               [k_workshop_shader_part_edge    ] = "edge",
-               [k_workshop_shader_part_griptape] = "griptape",
-               [k_workshop_shader_part_deck    ] = "deck"
-            };
-
-            for( u32 j=0; j<k_workshop_shader_part_max; j ++ )
-               keyvalues_read_u32s( &kvs, root, _shader_prop_workshop_keys[j], (u32[]){0}, &props->workshop.tex_all[j], 1 );
-         }
-      }
-   }
-   _end_temporary_frame( temp_frame );
-}
-
-void vg_model_stream_metadata( struct vg_model_stream_context *ctx, struct stack_allocator *stack )
-{
-   struct array_file_ptr strings;
-   af_load_array( &ctx->af, &strings, "strings", stack, 1 );
-   ctx->model->packed_strings = strings.data;
-   ctx->model->flags |= VG_MODEL_CPU_METADATA;
-
-   struct array_file_meta *pack = af_find_array( &ctx->af, "pack" );
-   if( pack ) ctx->model->pack_base_offset = pack->file_offset;
-   else       ctx->model->pack_base_offset = 0;
-
-   struct array_file_ptr ptr;
-   af_load_array( &ctx->af, &ptr, "mdl_mesh", stack, sizeof(struct mdl_mesh) );
-   ctx->model->meshes = ptr.data;
-   ctx->model->mesh_count = ptr.count;
-
-   af_load_array( &ctx->af, &ptr, "mdl_submesh", stack, sizeof(struct mdl_submesh) );
-   ctx->model->submeshes = ptr.data;
-   ctx->model->submesh_count = ptr.count;
-
-   af_load_array( &ctx->af, &ptr, "mdl_texture", stack, sizeof(union mdl_texture) );
-   ctx->model->textures = ptr.data;
-   ctx->model->texture_count = ptr.count;
-
-   af_load_array( &ctx->af, &ptr, "mdl_armature", stack, sizeof(struct mdl_armature) );
-   ctx->model->armatures = ptr.data;
-   ctx->model->armature_count = ptr.count;
-
-   af_load_array( &ctx->af, &ptr, "mdl_bone", stack, sizeof(struct mdl_bone) );
-   ctx->model->bones = ptr.data;
-   ctx->model->bone_count = ptr.count;
-
-   vg_model_stream_materials( ctx, stack );
-}
-
-void vg_model_stream_meshes_cpu( struct vg_model_stream_context *ctx, struct stack_allocator *stack )
-{
-   ASSERT_CRITICAL( ctx->model->flags & VG_MODEL_CPU_METADATA );
-   ctx->model->flags |= VG_MODEL_CPU_MESHES;
-
-   struct array_file_ptr ptr;
-   af_load_array( &ctx->af, &ptr, "mdl_vert", stack, sizeof(struct mdl_vert) );
-   ctx->model->verts = ptr.data;
-   ctx->model->vert_count = ptr.count;
-
-   af_load_array( &ctx->af, &ptr, "mdl_indice", stack, sizeof(u32) );
-   ctx->model->indices = ptr.data;
-   ctx->model->indice_count = ptr.count;
-}
-
-struct model_upload_task
-{
-   struct vg_model *model;
-   struct mdl_vert *vert_buffer;
-   u32 *indice_buffer;
-};
-
-static void vg_model_upload_task( struct task *task )
-{
-   struct model_upload_task *in_args = task_buffer(task);
-   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_MAIN ) );
-
-   glGenVertexArrays( 1, &in_args->model->vao );
-   glBindVertexArray( in_args->model->vao );
-   glGenBuffers( 1, &in_args->model->vbo );
-   glGenBuffers( 1, &in_args->model->ebo );
-
-   u32 stride = sizeof(struct mdl_vert);
-   glBindBuffer( GL_ARRAY_BUFFER, in_args->model->vbo );
-   glBufferData( GL_ARRAY_BUFFER, in_args->model->vert_count*stride, in_args->vert_buffer, GL_STATIC_DRAW );
-
-   glBindVertexArray( in_args->model->vao );
-   glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, in_args->model->ebo );
-   glBufferData( GL_ELEMENT_ARRAY_BUFFER, in_args->model->indice_count*sizeof(u32), 
-                                          in_args->indice_buffer, GL_STATIC_DRAW );
-   
-   /* 0: coordinates */
-   glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0 );
-   glEnableVertexAttribArray( 0 );
-
-   /* 1: normal */
-   glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, stride, (void *)offsetof(struct mdl_vert, norm) );
-   glEnableVertexAttribArray( 1 );
-
-   /* 2: uv */
-   glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE, stride, (void *)offsetof(struct mdl_vert, uv) );
-   glEnableVertexAttribArray( 2 );
-
-   /* 3: colour */
-   glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void *)offsetof(struct mdl_vert, colour) );
-   glEnableVertexAttribArray( 3 );
-
-   /* 4: weights */
-   glVertexAttribPointer( 4, 4, GL_UNSIGNED_SHORT, GL_TRUE, stride, (void *)offsetof(struct mdl_vert, weights) );
-   glEnableVertexAttribArray( 4 );
-
-   /* 5: groups */
-   glVertexAttribIPointer( 5, 4, GL_UNSIGNED_BYTE, stride, (void *)offsetof(struct mdl_vert, groups) );
-   glEnableVertexAttribArray( 5 );
-}
-
-void vg_model_stream_meshes_gpu( struct vg_model_stream_context *ctx, u32 *fixup_table )
-{
-   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
-   ASSERT_CRITICAL( ctx->model->flags & VG_MODEL_CPU_METADATA );
-   ctx->model->flags |= VG_MODEL_GPU_MESHES;
-
-   /* NOTE: We could check here if we already have CPU meshes and use those buffers. 
-    *       In very rare instances (1 time) we use both paths. */
-
-   struct array_file_meta *arr_vertices = af_find_array( &ctx->af, "mdl_vert" );
-   struct array_file_meta *arr_indices = af_find_array( &ctx->af, "mdl_indice" );
-
-   if( arr_vertices && arr_indices )
-   {
-      u32 size_verts   = PAD_TO_8(sizeof(struct mdl_vert)*arr_vertices->item_count),
-          size_indices = PAD_TO_8(sizeof(u32)*arr_indices->item_count),
-          size_hdr     = PAD_TO_8(sizeof(struct model_upload_task)),
-          total        = size_hdr + size_verts + size_indices;
-
-      struct task *upload_task = _task_new( k_thread_main, total, 0, "Model upload to GPU task" );
-      struct model_upload_task *args = task_buffer( upload_task );
-
-      args->model = ctx->model;
-      args->vert_buffer   = ((void *)args) + size_hdr;
-      args->indice_buffer = ((void *)args) + size_hdr + size_verts;
-      ctx->model->vert_count = arr_vertices->item_count;
-      ctx->model->indice_count = arr_indices->item_count;
-
-      af_load_array_file_buffer( &ctx->af, arr_vertices, args->vert_buffer, sizeof(struct mdl_vert) );
-      af_load_array_file_buffer( &ctx->af, arr_indices, args->indice_buffer, sizeof(u32) );
-
-      if( fixup_table )
-      {
-         for( u32 i=0; i<ctx->model->vert_count; i ++ )
-         {
-            struct mdl_vert *vert = &args->vert_buffer[i];
-            for( u32 j=0; j<4; j++ )
-               vert->groups[j] = fixup_table[vert->groups[j]];
-         }
-      }
-
-      /*
-       * Unpack the indices (if there are meshes)
-       * ---------------------------------------------------------
-       */
-      if( ctx->model->submesh_count )
-      {
-         struct mdl_submesh *sm = &ctx->model->submeshes[ 0 ];
-         u32 offset = sm->vertex_count;
-
-         for( u32 i=1; i<ctx->model->submesh_count; i++ )
-         {
-            struct mdl_submesh *sm = &ctx->model->submeshes[ i ];
-            u32           *indices = args->indice_buffer + sm->indice_start;
-
-            for( u32 j=0; j<sm->indice_count; j++ )
-               indices[j] += offset;
-            offset += sm->vertex_count;
-         }
-      }
-
-      task_send( upload_task, vg_model_upload_task );
-   }
-   else
-   {
-      $log( $fatal, {"No vertex/indice data in model file"} );
-      _fatal_exit();
-   }
-}
-
-void vg_model_stream_textures_gpu( struct vg_model_stream_context *ctx )
-{
-   ASSERT_CRITICAL( ctx->model->flags & VG_MODEL_CPU_METADATA );
-   ctx->model->flags |= VG_MODEL_GPU_TEXTURES;
-   for( u32 i=0; i<ctx->model->texture_count; i ++ )
-   {
-      union mdl_texture *tex = &ctx->model->textures[ i ];
-      struct mdl_file pack_info = tex->file;
-      _vg_tex_load_stream( &tex->tex, vg_model_stream_pack_stream( ctx, &pack_info ), 
-                           VG_TEX_REPEAT | VG_TEX_NEAREST | VG_TEX_NOMIP );
-   }
-}
-
-bool vg_model_stream_open( struct vg_model_stream_context *ctx, struct vg_model *model, const c8 *path )
-{
-   zero_buffer( ctx, sizeof(struct vg_model_stream_context) );
-   zero_buffer( model, sizeof(struct vg_model) );
-   if( stream_open_file( &ctx->stream, path, k_stream_read ) )
-   {
-      ctx->model = model;
-      ctx->temp_frame = _start_temporary_frame();
-      if( !af_open_stream( &ctx->af, &ctx->stream, VG_MODEL_VERSION_MIN, VG_MODEL_VERSION_NR, _temporary_stack_allocator()) )
-      {
-         stream_close( &ctx->stream );
-         _end_temporary_frame( ctx->temp_frame );
-         return 0;
-      }
-      ctx->model->version = ctx->af.header.version;
-      return 1;
-   }
-   else return 0;
-}
-
-void vg_model_stream_close( struct vg_model_stream_context *ctx )
-{
-   stream_close( &ctx->stream );
-   _end_temporary_frame( ctx->temp_frame );
-}
-
-bool vg_model_load( struct vg_model *model, u32 model_flags, const c8 *path, struct stack_allocator *stack )
-{
-   bool success = 0;
-   model_flags |= VG_MODEL_CPU_METADATA;
-
-   struct vg_model_stream_context ctx;
-   if( vg_model_stream_open( &ctx, model, path ) )
-   {
-      if( model_flags & VG_MODEL_CPU_METADATA )
-         vg_model_stream_metadata( &ctx, stack );
-
-      if( model_flags & VG_MODEL_CPU_MESHES )
-         vg_model_stream_meshes_cpu( &ctx, stack );
-
-      ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
-      if( model_flags & VG_MODEL_GPU_MESHES )
-         vg_model_stream_meshes_gpu( &ctx, NULL );
-
-      if( model_flags & VG_MODEL_GPU_TEXTURES )
-         vg_model_stream_textures_gpu( &ctx );
-
-      vg_model_stream_close( &ctx );
-      success = 1;
-   }
-   return success;
-}
-
-void vg_model_unload_gpu( struct vg_model *model )
-{
-   if( model->flags & VG_MODEL_GPU_MESHES )
-   {
-      model->flags &= ~(u32)(VG_MODEL_GPU_MESHES);
-      glDeleteVertexArrays( 1, &model->vao );
-      glDeleteBuffers( 1, &model->ebo );
-      glDeleteBuffers( 1, &model->vbo );
-   }
-
-   if( model->flags & VG_MODEL_GPU_TEXTURES )
-      for( u32 i=0; i<model->texture_count; i ++ )
-         vg_tex_delete( &model->textures[i].tex );
-}
-
-void mdl_transform_m4x3( struct mdl_transform *transform, f32 mtx[4][3] )
-{
-   q_m3x3( transform->q, mtx );
-   v3_muls( mtx[0], transform->s[0], mtx[0] );
-   v3_muls( mtx[1], transform->s[1], mtx[1] );
-   v3_muls( mtx[2], transform->s[2], mtx[2] );
-   v3_copy( transform->co, mtx[3] );
-}
-
-void mdl_transform_identity( struct mdl_transform *transform )
-{
-   v3_fill( transform->co, 0 );
-   q_identity( transform->q );
-   v3_fill( transform->s, 1.0f );
-}
-
-void mdl_transform_vector( struct mdl_transform *transform, f32 vec[3], f32 dest[3] )
-{
-   v3_mul( transform->s, vec, dest );
-   q_mulv( transform->q, dest, dest );
-}
-
-void mdl_transform_point( struct mdl_transform *transform, f32 co[3], f32 dest[3] )
-{
-   mdl_transform_vector( transform, co, dest );
-   v3_add( transform->co, dest, dest );
-}
-
-void mdl_transform_mul( struct mdl_transform *a, struct mdl_transform *b, struct mdl_transform *d )
-{
-   mdl_transform_point( a, b->co, d->co );
-   q_mul( a->q, b->q, d->q );
-   q_normalize( d->q );
-   v3_mul( a->s, b->s, d->s );
-}
-
-void vg_model_bind_mesh( struct vg_model *model )
-{
-   glBindVertexArray( model->vao );
-}
-
-void vg_model_draw_elements( u32 start, u32 count )
-{
-   glDrawElements( GL_TRIANGLES, count, GL_UNSIGNED_INT, (void *)(start*sizeof(u32)) );
-}
-
-void vg_model_draw_submesh( struct mdl_submesh *sm )
-{
-   vg_model_draw_elements( sm->indice_start, sm->indice_count );
-}
-
-i32 vg_model_get_mesh_index( struct vg_model *model, const c8 *name )
-{
-   u32 hash = buffer_djb2( name, 0 );
-   for( u32 i=0; i<model->mesh_count; i++ )
-   {
-      struct mdl_mesh *mesh = &model->meshes[ i ];
-      if( af_str_eq( model->packed_strings, mesh->pstr_name, name, hash ) ) 
-         return i;
-   }
-   return -1;
-}
-
-i32 vg_model_get_submesh_index( struct vg_model *model, const c8 *mesh_name )
-{
-   i32 mesh_index = vg_model_get_mesh_index( model, mesh_name );
-   if( mesh_index == -1 )
-      return -1;
-
-   struct mdl_mesh *mesh = &model->meshes[ mesh_index ];
-   if( !mesh->submesh_count ) 
-      return -1;
-   return  mesh->submesh_start;
-}
diff --git a/source/vg_model/model.h b/source/vg_model/model.h
deleted file mode 100644 (file)
index bcdd387..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-#pragma once
-#include "vg_tex.h"
-#include "shader_props.h"
-#include "array_file.h"
-
-#define VG_MODEL_VERSION_MIN 110
-#define VG_MODEL_VERSION_NR 110
-
-enum mdl_shader
-{
-   k_shader_standard                = 0,
-   k_shader_standard_cutout         = 1,
-   k_shader_terrain_blend           = 2,
-   k_shader_standard_vertex_blend   = 3,
-   k_shader_water                   = 4,
-   k_shader_invisible               = 5,
-   k_shader_boundary                = 6,
-   k_shader_fxglow                  = 7,
-   k_shader_cubemap                 = 8,
-   k_shader_walking                 = 9,
-   k_shader_foliage                 = 10,
-   k_shader_workshop                = 11,
-   k_shader_override                = 30000
-};
-
-enum mdl_surface_prop
-{
-   k_surface_prop_concrete = 0,
-   k_surface_prop_wood     = 1,
-   k_surface_prop_grass    = 2,
-   k_surface_prop_tiles    = 3,
-   k_surface_prop_metal    = 4,
-   k_surface_prop_snow     = 5,
-   k_surface_prop_sand     = 6
-};
-
-enum material_flag
-{
-   k_material_flag_skate_target     = 0x0001,
-   k_material_flag_collision        = 0x0002,
-   k_material_flag_grow_grass       = 0x0004,
-   k_material_flag_grindable        = 0x0008,
-   k_material_flag_invisible        = 0x0010,
-   k_material_flag_boundary         = 0x0020,
-   k_material_flag_preview_visibile = 0x0040,
-   k_material_flag_walking          = 0x0080,
-
-   k_material_flag_ghosts      =
-      k_material_flag_boundary|
-      k_material_flag_invisible|
-      k_material_flag_walking
-};
-
-#pragma pack(push,1)
-
-/* 48 byte */
-struct mdl_vert
-{
-   f32 co[3],     /* 3*32 */
-       norm[3];   /* 3*32 */
-   f32 uv[2];     /* 2*32 */
-
-   u8  colour[4]; /* 4*8 */
-   u16 weights[4];/* 4*16 */
-   u8  groups[4]; /* 4*8 */
-};
-
-#pragma pack(pop)
-
-struct mdl_transform
-{
-   f32 co[3], s[3];
-   f32 q[4];
-};
-
-void mdl_transform_identity( struct mdl_transform *transform );
-void mdl_transform_vector( struct mdl_transform *transform, f32 vec[3], f32 dest[3] );
-void mdl_transform_point( struct mdl_transform *transform, f32 co[3], f32 dest[3] );
-void mdl_transform_mul( struct mdl_transform *a, struct mdl_transform *b, struct mdl_transform *d );
-
-#if (VG_MODEL_VERSION_MIN <= 105)
-struct mdl_material_v101
-{
-   u32 pstr_name,
-       shader,
-       flags,
-       surface_prop;
-
-   f32 colour[4],
-       colour1[4];
-
-   u32 tex_diffuse, /* Indexes start from 1. 0 if missing. */
-       tex_none0,
-       tex_none1;
-};
-#endif
-
-struct mdl_material
-{
-   u32 pstr_name,
-       shader,
-       flags,
-       surface_prop;
-
-   union 
-   {
-      struct 
-      {
-         u32 offset, size; /* indexes shader data */
-      }
-      kvs;
-      u32 kv_root; /* runtime */
-   } 
-   props;
-};
-
-struct mdl_bone
-{
-   f32 co[3], end[3];
-   u32 parent,
-       collider,
-       ik_target,
-       ik_pole,
-       flags,
-       pstr_name;
-
-   f32 hitbox[2][3];
-   f32 conevx[3], conevy[3], coneva[3];
-   f32 conet;
-};
-
-enum bone_flag
-{
-   k_bone_flag_deform               = 0x00000001,
-   k_bone_flag_ik                   = 0x00000002,
-   k_bone_flag_cone_constraint      = 0x00000004
-};
-
-enum bone_collider
-{
-   k_bone_collider_none = 0,
-   k_bone_collider_box = 1,
-   k_bone_collider_capsule = 2
-};
-         
-struct mdl_armature
-{
-   struct mdl_transform transform;
-   u32 bone_start,
-       bone_count,
-       anim_start_OBSOLETE_107, // obsolete 107+
-       anim_count_OBSOLETE_107, // .
-       pstr_name; // v107+
-};
-
-struct mdl_submesh
-{
-   u32 indice_start,
-       indice_count,
-       vertex_start,
-       vertex_count;
-
-   f32 bbx[2][3];
-   u16 material_id, flags;
-};
-
-enum esubmesh_flags
-{
-   k_submesh_flag_none     = 0x0000,
-   k_submesh_flag_consumed = 0x0001
-};
-
-struct mdl_mesh
-{
-   struct mdl_transform transform;
-   u32 submesh_start,
-       submesh_count,
-       pstr_name,
-       entity_id,    /* upper 16 bits: type, lower 16 bits: index. hgn: 11.06.2025 Is this still used??? */
-       armature_id;
-};
-
-struct mdl_file
-{
-   u32 pstr_path,
-       pack_offset,
-       pack_size;
-};
-
-union mdl_texture
-{
-   struct mdl_file file;
-   struct vg_tex tex;
-};
-
-struct vg_model
-{
-   u32 version;
-   u32 flags;
-
-   /* VG_MODEL_CPU_METADATA ---------------------- */
-   const void *packed_strings;
-   union shader_props *shader_props;
-
-   struct mdl_mesh *meshes;
-   u32 mesh_count;
-
-   struct mdl_submesh *submeshes;
-   u32 submesh_count;
-
-   struct mdl_material *materials;
-   u32 material_count;
-
-   union mdl_texture *textures;
-   u32 texture_count;
-
-   struct mdl_armature *armatures;
-   u32 armature_count;
-
-   struct mdl_bone *bones;
-   u32 bone_count;
-
-   /* VG_MODEL_CPU_MESHES ---------------------- */
-   struct mdl_vert *verts;
-   u32 vert_count;
-   u32 *indices;
-   u32 indice_count;
-
-   u32 pack_base_offset;
-
-   /* VG_MODEL_GPU_MESHES ----------------------- */
-   GLuint vao, vbo, ebo;
-};
-
-#define VG_MODEL_CPU_METADATA   0x4
-#define VG_MODEL_CPU_MESHES     0x8
-# define VG_MODEL_GPU_TEXTURES   0x1
-# define VG_MODEL_GPU_MESHES     0x2
-# define VG_MODEL_ENGINE_STANDARD (VG_MODEL_CPU_METADATA|VG_MODEL_GPU_TEXTURES|VG_MODEL_GPU_MESHES)
-# define VG_MODEL_ENGINE_PROCEDURAL_SOURCE (VG_MODEL_CPU_METADATA|VG_MODEL_GPU_TEXTURES|VG_MODEL_CPU_MESHES)
-
-bool vg_model_load( struct vg_model *model, u32 model_flags, const c8 *path, struct stack_allocator *stack );
-void vg_model_unload_gpu( struct vg_model *model );
-
-/* Sub functions for each part of the load process
- * ---------------------------------------------------------------------------------------------------------------  */
-struct vg_model_stream_context
-{
-   struct stream stream;
-   struct array_file_context af;
-   struct vg_model *model;
-   u32 temp_frame;
-};
-
-bool vg_model_stream_open( struct vg_model_stream_context *ctx, struct vg_model *model, const c8 *path );
-void vg_model_stream_close( struct vg_model_stream_context *ctx );
-
-/* Parts which you might want (currently they all require metadata to be explicitly loaded) */
-void vg_model_stream_metadata( struct vg_model_stream_context *ctx, struct stack_allocator *stack );
-void vg_model_stream_meshes_cpu( struct vg_model_stream_context *ctx, struct stack_allocator *stack );
-
-void vg_model_stream_meshes_gpu( struct vg_model_stream_context *ctx, u32 *fixup_table );
-void vg_model_stream_textures_gpu( struct vg_model_stream_context *ctx );
-
-struct stream *vg_model_stream_pack_stream( struct vg_model_stream_context *ctx, struct mdl_file *file );
-
-/* Rendering operations
- * ----------------------------------------------------------------------------------------------------------------- */
-void vg_model_bind_mesh( struct vg_model *model );
-void vg_model_draw_elements( u32 start, u32 count );
-void vg_model_draw_submesh( struct mdl_submesh *sm );
-i32  vg_model_get_mesh_index( struct vg_model *model, const c8 *name );
-i32  vg_model_get_submesh_index( struct vg_model *model, const c8 *mesh_name );
-void mdl_transform_m4x3( struct mdl_transform *transform, f32 mtx[4][3] );
diff --git a/source/vg_model/model.kv b/source/vg_model/model.kv
deleted file mode 100644 (file)
index 49a1149..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-add model.c
-add metascene.c
-add array_file.c
-include ""
diff --git a/source/vg_model/shader_props.h b/source/vg_model/shader_props.h
deleted file mode 100644 (file)
index d383c0d..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-#pragma once
-
-enum material_render_flag
-{
-   k_material_render_additive = 0x20
-};
-
-enum workshop_shader_part
-{
-   k_workshop_shader_part_truck1,
-   k_workshop_shader_part_truck2,
-   k_workshop_shader_part_wheel1,
-   k_workshop_shader_part_wheel2,
-   k_workshop_shader_part_wheel3,
-   k_workshop_shader_part_wheel4,
-   k_workshop_shader_part_edge,
-   k_workshop_shader_part_griptape,
-   k_workshop_shader_part_deck,
-   k_workshop_shader_part_max
-};
-
-union shader_props
-{
-   struct shader_props_standard
-   {
-      u32 tex_diffuse;
-      u32 render_flags;
-   }
-   standard;
-
-   struct shader_props_terrain
-   {
-      u32 tex_diffuse;
-      f32 blend_offset[2];
-      f32 sand_colour[4];
-   }
-   terrain;
-
-   struct shader_props_vertex_blend
-   {
-      u32 tex_diffuse;
-      f32 blend_offset[2];
-   }
-   vertex_blend;
-
-   struct shader_props_water
-   {
-      f32 shore_colour[4];
-      f32 deep_colour[4];
-      f32 fog_scale;
-      f32 fresnel;
-      f32 water_sale;
-      f32 wave_speed[4];
-   }
-   water;
-
-   struct shader_props_cubemapped
-   {
-      u32 tex_diffuse;
-      u32 cubemap_entity;
-      f32 tint[4];
-   }
-   cubemapped;
-
-   struct shader_props_workshop
-   {
-      u32 tex_all[k_workshop_shader_part_max];
-   }
-   workshop;
-};
-
-extern const char *_shader_prop_workshop_keys[k_workshop_shader_part_max];
diff --git a/source/vg_tex/vg_tex.c b/source/vg_tex/vg_tex.c
deleted file mode 100644 (file)
index 4253c01..0000000
+++ /dev/null
@@ -1,438 +0,0 @@
-#include "common_api.h"
-#include "common_thread_api.h"
-#include "vg_tex.h"
-
-#define QOI_OP_INDEX  0x00 /* 00xxxxxx */
-#define QOI_OP_DIFF   0x40 /* 01xxxxxx */
-#define QOI_OP_LUMA   0x80 /* 10xxxxxx */
-#define QOI_OP_RUN    0xc0 /* 11xxxxxx */
-#define QOI_OP_RGB    0xfe /* 11111110 */
-#define QOI_OP_RGBA   0xff /* 11111111 */
-#define QOI_MASK_2    0xc0 /* 11000000 */
-
-#define QOI_COLOR_HASH(C) (C.rgba[0]*3 + C.rgba[1]*5 + C.rgba[2]*7 + C.rgba[3]*11)
-#define QOI_MAGIC \
-   (((u32)'q')       | ((u32)'o') << 8 | \
-    ((u32)'i') << 16 | ((u32)'f') << 24)
-
-static const u8 qoi_padding[8] = {0,0,0,0,0,0,0,1};
-
-#define cpu_to_big32 big32_to_cpu
-static inline u32 big32_to_cpu( u32 x )
-{
-   return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | (x >> 24);
-}
-
-bool vg_qoi_validate( const struct qoi_desc *desc )
-{
-   if( (desc->width == 0)    || (desc->height == 0) ||
-       (desc->width >= 2048) || (desc->height >= 2048) )
-   {
-      $log( $error, {"QOI file is invalid; Unpermitted size: "}, $unsigned( desc->width ), {" by "}, $unsigned( desc->height ) );
-      return 0;
-   }
-
-   if( !(desc->channels == 3 || desc->channels == 4) )
-   {
-      $log( $error, {"QOI file is invalid; Only 3 or 4 channels allowed, file has: "}, $unsigned( desc->channels ) );
-      return 0;
-   }
-   return 1;
-}
-
-/* Initalize stream context and return the number of bytes required to store the final RGB/A image data. */
-u32 vg_qoi_stream_init( struct qoi_desc *desc, struct stream *stream )
-{
-   stream_read( stream, desc, sizeof(struct qoi_desc) );
-   if( desc->magic != QOI_MAGIC )
-   {
-      $log( $error, {"QOI file is invalid; Magic Number incorrect."} );
-      return 0;
-   }
-
-   desc->width = big32_to_cpu( desc->width );
-   desc->height = big32_to_cpu( desc->height );
-
-   if( vg_qoi_validate( desc ) )
-      return desc->width * desc->height * desc->channels;
-   else return 0;
-}
-
-void vg_qoi_stream_decode( struct qoi_desc *desc, struct stream *stream, u8 *pixels, bool v_flip )
-{
-   union qoi_rgba_t index[64], px;
-   zero_buffer( index, sizeof(union qoi_rgba_t)*64 );
-   zero_buffer( &px, sizeof(union qoi_rgba_t) );
-   px.rgba[3] = 255;
-
-   u32 run=0;
-   for( u32 y=0; y<desc->height; y ++ )
-   {
-      for( u32 x=0; x<desc->width; x ++ )
-      {
-         if( run > 0 )
-            run --;
-         else 
-         {
-            u8 b1;
-            stream_read( stream, &b1, 1 );
-
-            if( b1 == QOI_OP_RGB ) 
-               stream_read( stream, px.rgba, 3 );
-            else if( b1 == QOI_OP_RGBA ) 
-               stream_read( stream, px.rgba, 4 );
-            else if( (b1 & QOI_MASK_2) == QOI_OP_INDEX ) 
-               px = index[b1];
-            else if( (b1 & QOI_MASK_2) == QOI_OP_DIFF ) 
-            {
-               px.rgba[0] += (i32)((b1 >> 4) & 0x03) - 2;
-               px.rgba[1] += (i32)((b1 >> 2) & 0x03) - 2;
-               px.rgba[2] += (i32)( b1       & 0x03) - 2;
-            }
-            else if( (b1 & QOI_MASK_2) == QOI_OP_LUMA ) 
-            {
-               u8 b2;
-               stream_read( stream, &b2, 1 );
-               i32 vg = (i32)(b1 & 0x3f) - 32;
-               px.rgba[0] += vg - 8 + (i32)((b2 >> 4) & 0x0f);
-               px.rgba[1] += vg;
-               px.rgba[2] += vg - 8 + (i32)(b2 & 0x0f);
-            }
-            else if( (b1 & QOI_MASK_2) == QOI_OP_RUN ) 
-               run = (b1 & 0x3f);
-            index[ QOI_COLOR_HASH(px) % 64 ] = px;
-         }
-
-         u32 row = v_flip? desc->height-(y+1): y;
-         for( u32 i=0; i < desc->channels; i ++ )
-            pixels[ ((row*desc->width) + x)*desc->channels + i ] = px.rgba[i];
-      }
-   }
-}
-
-u32 vg_query_qoi_max_compressed_size( const struct qoi_desc *desc )
-{
-   return desc->width * desc->height * (desc->channels + 1) + sizeof(struct qoi_desc) + sizeof(qoi_padding);
-}
-
-u32 vg_qoi_stream_encode( const struct qoi_desc *desc, const u8 *pixels, struct stream *stream, bool v_flip )
-{
-   if( !vg_qoi_validate( desc ) )
-      return 0;
-
-   struct qoi_desc file_header = *desc;
-   file_header.magic  = QOI_MAGIC;
-   file_header.width  = cpu_to_big32( file_header.width );
-   file_header.height = cpu_to_big32( file_header.height );
-   stream_write( stream, &file_header, sizeof(struct qoi_desc) );
-   
-   union qoi_rgba_t index[64];
-   zero_buffer( index, sizeof(union qoi_rgba_t)*64 );
-   union qoi_rgba_t px_prev = { .rgba = { 0, 0, 0, 255 } };
-   union qoi_rgba_t px = px_prev;
-
-   u32 run = 0;
-   for( u32 y=0; y<desc->height; y ++ )
-   {
-      for( u32 x=0; x<desc->width; x ++ )
-      {
-         u32 row = v_flip? desc->height-(y+1): y;
-         for( u32 i=0; i < desc->channels; i ++ )
-            px.rgba[i] = pixels[ ((row*desc->width) + x)*desc->channels + i ];
-
-         if( px.v == px_prev.v )
-         {
-            run ++;
-            if( run == 62 || ((y+1 == desc->height) && (x+1 == desc->width)) )
-            {
-               u8 b1 = QOI_OP_RUN | (run - 1);
-               stream_write( stream, &b1, 1 );
-               run = 0;
-            }
-         }
-         else
-         {
-            if( run > 0 )
-            {
-               u8 b1 = QOI_OP_RUN | (run - 1);
-               stream_write( stream, &b1, 1 );
-               run = 0;
-            }
-
-            u32 index_pos = QOI_COLOR_HASH( px ) % 64;
-            if( index[ index_pos ].v == px.v )
-            {
-               u8 b1 = QOI_OP_INDEX | index_pos;
-               stream_write( stream, &b1, 1 );
-            }
-            else
-            {
-               index[ index_pos ] = px;
-               if( px.rgba[3] == px_prev.rgba[3] )
-               {
-                  i8 vr = px.rgba[0] - px_prev.rgba[0],
-                     vg = px.rgba[1] - px_prev.rgba[1],
-                     vb = px.rgba[2] - px_prev.rgba[2],
-                     vg_r = vr - vg,
-                     vg_b = vb - vg;
-
-                  if( vr > -3 && vr < 2 &&
-                      vg > -3 && vg < 2 &&
-                      vb > -3 && vb < 2 ) 
-                  {
-                     stream_write( stream, (u8[]){ QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2) }, 1 );
-                  }
-                  else if( vg_r >  -9 && vg_r <  8 &&
-                           vg   > -33 && vg   < 32 &&
-                           vg_b >  -9 && vg_b <  8 ) 
-                  {
-                     stream_write( stream, (u8[]){ QOI_OP_LUMA | (vg   + 32), (vg_r + 8) << 4 | (vg_b +  8) }, 2 );
-                  }
-                  else 
-                  {
-                     stream_write( stream, (u8[]){ QOI_OP_RGB }, 1 );
-                     stream_write( stream, px.rgba, 3 );
-                  }
-               }
-               else
-               {
-                  stream_write( stream, (u8 []){ QOI_OP_RGBA }, 1 );
-                  stream_write( stream, px.rgba, 4 );
-               }
-            }
-         }
-         px_prev = px;
-      }
-   }
-   stream_write( stream, qoi_padding, sizeof(qoi_padding) );
-   return 1;
-}
-
-/* VG_PART 
- * ------------------------------------------------------------------------------------------------------------------ */
-
-struct
-{
-   GLuint error2d, errorcube;
-}
-static _vg_tex;
-
-void _vg_tex_init(void)
-{
-   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_OPENGL ) );
-   $log( $info, {"[INIT] _vg_tex_init"} );
-
-   static u8 const_vg_tex2d_err[] =
-   {
-      0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff,
-      0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 
-      0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff,
-      0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff, 
-   };
-
-   glGenTextures( 1, &_vg_tex.error2d );
-   glBindTexture( GL_TEXTURE_2D, _vg_tex.error2d );
-   glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, const_vg_tex2d_err );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
-
-   glGenTextures( 1, &_vg_tex.errorcube );
-   glBindTexture( GL_TEXTURE_CUBE_MAP, _vg_tex.errorcube );
-   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
-   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
-   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
-   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
-   glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
-
-   for( u32 j=0; j<6; j ++ ) 
-   {
-      glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, 0, GL_RGBA, 4, 4, 
-                    0, GL_RGBA, GL_UNSIGNED_BYTE, const_vg_tex2d_err );
-   }
-}
-
-struct tex_upload_task 
-{
-   struct vg_tex *tex;
-   u32 width, height, channels, flags;
-   u8 image_buffer[];
-};
-
-static void _vg_tex_upload( struct task *task )
-{
-   struct tex_upload_task *in_args = task_buffer( task );
-   ASSERT_CRITICAL( _vg_tex.errorcube && _vg_tex.error2d );
-   u32 flags = in_args->flags;
-   struct vg_tex *tex = in_args->tex;
-
-   if( flags & VG_TEX_ERROR )
-   {
-      tex->name  = (flags & VG_TEX_CUBEMAP)? _vg_tex.errorcube: _vg_tex.error2d;
-      tex->flags = (flags & VG_TEX_CUBEMAP) | VG_TEX_ERROR | VG_TEX_COMPLETE;
-   }
-   else
-   {
-      u32 pixel_format = 0;
-      if( in_args->channels == 3 ) pixel_format = GL_RGB;
-      else if( in_args->channels == 4 ) pixel_format = GL_RGBA;
-      else 
-      {
-         $log( $fatal, {"Can't upload texture with "}, $unsigned( in_args->channels ), {" channels."} );
-         _fatal_exit();
-      }
-
-      glGenTextures( 1, &tex->name );
-      u32 filter_min = 0,
-          filter_mag = 0;
-      if( flags & VG_TEX_LINEAR )
-      {
-         if( flags & VG_TEX_NOMIP ) filter_min = GL_LINEAR;
-         else                       filter_min = GL_LINEAR_MIPMAP_LINEAR;
-         filter_mag = GL_LINEAR;
-      }
-      else
-      {
-         ASSERT_CRITICAL( flags & VG_TEX_NEAREST );
-         filter_min = GL_NEAREST;
-         filter_mag = GL_NEAREST;
-      }
-
-      if( flags & VG_TEX_CUBEMAP )
-      {
-         u32 w = in_args->width,
-             h = in_args->height/6;
-
-         glBindTexture(   GL_TEXTURE_CUBE_MAP, tex->name );
-         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, filter_min );
-         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, filter_mag );
-         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); /* can this be anything else? */
-         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
-         glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
-
-         for( u32 j=0; j<6; j ++ ) 
-         {
-            u32 offset = w*h*j*in_args->channels;
-            glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, 0, pixel_format,
-                           w, h,
-                           0, pixel_format, GL_UNSIGNED_BYTE, in_args->image_buffer + offset );
-         }
-
-         if( !(flags & VG_TEX_NOMIP) )
-            glGenerateMipmap( GL_TEXTURE_CUBE_MAP );
-      }
-      else
-      {
-         glBindTexture( GL_TEXTURE_2D, tex->name );
-         glTexImage2D( GL_TEXTURE_2D, 0, pixel_format, in_args->width, in_args->height,
-                        0, pixel_format, GL_UNSIGNED_BYTE, in_args->image_buffer );
-
-         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_min );
-         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_mag );
-
-         u32 wrap_s = 0,
-             wrap_t = 0;
-
-         if( flags & VG_TEX_CLAMP )
-         {
-            wrap_s = GL_CLAMP_TO_EDGE;
-            wrap_t = GL_CLAMP_TO_EDGE;
-         }
-         else
-         {
-            ASSERT_CRITICAL( flags & VG_TEX_REPEAT );
-            wrap_s = GL_REPEAT;
-            wrap_t = GL_REPEAT;
-         }
-
-         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s );
-         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t );
-
-         if( !(flags & VG_TEX_NOMIP) )
-            glGenerateMipmap( GL_TEXTURE_2D );
-      }
-      tex->flags = flags | VG_TEX_COMPLETE;
-   }
-}
-
-bool _vg_tex_load_stream( struct vg_tex *out_tex, struct stream *in_stream, u32 flags )
-{
-   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
-
-   struct qoi_desc qoi;
-   u32 size = vg_qoi_stream_init( &qoi, in_stream );
-   if( size )
-   {
-      _async_push_groups( ASYNC_GROUP_OPENGL, 0 );
-
-      struct task *upload_task = _task_new( k_thread_main, sizeof( struct tex_upload_task ) + size, 0, "Texture upload task" );
-      struct tex_upload_task *args = task_buffer( upload_task );
-      vg_qoi_stream_decode( &qoi, in_stream, args->image_buffer, flags & VG_TEX_FLIP_V? 1: 0 );
-      args->tex = out_tex;
-      args->width = qoi.width;
-      args->height = qoi.height;
-      args->channels = qoi.channels;
-      args->flags = flags;
-      task_send( upload_task, _vg_tex_upload );
-      _async_pop_groups();
-      return 1;
-   }
-   else 
-      return 0;
-}
-
-void _vg_tex_load( struct vg_tex *out_tex, const c8 *path, u32 flags )
-{
-   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
-
-   bool error = 0;
-   struct stream file;
-   if( stream_open_file( &file, path, k_stream_read ) )
-   {
-      if( !_vg_tex_load_stream( out_tex, &file, flags ) )
-         error = 1;
-      stream_close( &file );
-   }
-   else 
-      error = 1;
-
-   if( error )
-   {
-      _async_push_groups( ASYNC_GROUP_OPENGL, 0 );
-      struct task *upload_task = _task_new( k_thread_main, sizeof( struct tex_upload_task ), 0, "Texture upload task" );
-      struct tex_upload_task *args = task_buffer( upload_task );
-      args->tex = out_tex;
-      args->width = 0;
-      args->height = 0;
-      args->channels = 0;
-      args->flags = VG_TEX_ERROR;
-      task_send( upload_task, _vg_tex_upload );
-      _async_pop_groups();
-   }
-}
-
-u32 vg_tex_name( GLuint target, struct vg_tex *tex )
-{
-   if( !tex ) 
-   {
-      return (target == GL_TEXTURE_2D)? _vg_tex.error2d: _vg_tex.errorcube;
-   }
-   if( tex->flags & VG_TEX_COMPLETE ) return tex->name;
-   else                               return (target == GL_TEXTURE_2D)? _vg_tex.error2d: _vg_tex.errorcube;
-}
-
-void vg_tex_bind( GLuint target, struct vg_tex *tex, u32 slot )
-{
-   glActiveTexture( GL_TEXTURE0 + slot );
-   glBindTexture( target, vg_tex_name( target, tex ) );
-}
-
-void vg_tex_delete( struct vg_tex *tex )
-{
-   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_OPENGL ) );
-   ASSERT_CRITICAL( tex->flags & VG_TEX_COMPLETE );
-   if( !(tex->flags & VG_TEX_ERROR) )
-      glDeleteTextures( 1, &tex->name );
-   zero_buffer( tex, sizeof(struct vg_tex) );
-}
diff --git a/source/vg_tex/vg_tex.h b/source/vg_tex/vg_tex.h
deleted file mode 100644 (file)
index b528fe9..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#pragma once
-#include "opengl.h"
-#include "common_api.h"
-
-/* TODO: Include Okaypeg alongside QOI. */
-
-struct vg_tex
-{
-   u32 name;
-   u32 flags;
-};
-
-#pragma pack(push,1)
-union qoi_rgba_t
-{
-   u8 rgba[4];
-   u32 v;
-};
-
-struct qoi_desc
-{
-   u32 magic;
-   u32 width;
-   u32 height;
-   u8  channels;
-   u8  colorspace;
-};
-#pragma pack(pop)
-
-# define QOI_SRGB   0
-# define QOI_LINEAR 1
-
-/* Reading qois */
-u32 vg_qoi_stream_init( struct qoi_desc *desc, struct stream *file );
-void vg_qoi_stream_decode( struct qoi_desc *desc, struct stream *file, u8 *dest_buffer, bool v_flip );
-
-/* Writing qois */
-u32 vg_query_qoi_max_compressed_size( const struct qoi_desc *desc );
-u32 vg_qoi_stream_encode( const struct qoi_desc *desc, const u8 *pixels, struct stream *stream, bool v_flip );
-
-#  define VG_TEX_LINEAR   0x1
-#  define VG_TEX_NEAREST  0x2
-#  define VG_TEX_REPEAT   0x4
-#  define VG_TEX_CLAMP    0x8
-#  define VG_TEX_NOMIP    0x10
-#  define VG_TEX_CUBEMAP  0x20
-#  define VG_TEX_ERROR    0x40
-#  define VG_TEX_COMPLETE 0x80
-#  define VG_TEX_FLIP_V   0x100
-#  define VG_TEX_FRAMEBUFFER_ATTACHMENT 0x200
-#  define VG_TEX_PRIVATE  0x400 /* used on renderbuffers... */
-
-u32  vg_tex_name( GLuint target, struct vg_tex *tex );
-void vg_tex_bind( GLuint target, struct vg_tex *tex, u32 slot );
-void vg_tex_delete( struct vg_tex *tex );
-void _vg_tex_load( struct vg_tex *out_tex, const c8 *path, u32 flags );
-bool _vg_tex_load_stream( struct vg_tex *out_tex, struct stream *in_stream, u32 flags );
diff --git a/source/vg_tex/vg_tex.kv b/source/vg_tex/vg_tex.kv
deleted file mode 100644 (file)
index c68af35..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-add vg_tex.c
-include ""
-
-hook
-{
-   event START
-   function _vg_tex_init
-}
diff --git a/source/vg_water/vg_water.c b/source/vg_water/vg_water.c
deleted file mode 100644 (file)
index 3f6d539..0000000
+++ /dev/null
@@ -1,261 +0,0 @@
-#include "vg_water.h"
-
-static vg_tex _water_surface_tex;
-
-static void _world_water_load_content( void *_, vg_async_info *async )
-{
-   _vg_tex_load( &world_water.tex_water_surf, "textures/water_surf.qoi", VG_TEX_LINEAR|VG_TEX_REPEAT );
-}
-
-void _world_water_init(void)
-{
-   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_MAIN ) );
-   _vg_async_send( _vg_async_alloc( VG_THREAD_ASYNC_ID, 0 ), (vg_async_fn)_world_water_load_content );
-}
-
-void water_set_surface( world_instance *world, float height )
-{
-   world->water.height = height;
-   v4_copy( (v4f){ 0.0f, 1.0f, 0.0f, height }, world->water.plane );
-}
-
-/*
- * Does not write motion vectors
- */
-void render_water_texture( world_instance *world, vg_camera *cam )
-{
-   if( !world->water.enabled || (vg.quality_profile == k_quality_profile_low) )
-      return;
-
-   /* Draw reflection buffa */
-   vg_framebuffer_bind( _vg_render.fb_water_reflection, _vg_render.scale );
-   glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
-
-   /* 
-    * Create flipped view matrix. Don't care about motion vectors
-    */
-   float cam_height = cam->transform[3][1] - world->water.height;
-
-   vg_camera water_cam;
-   water_cam.farz = cam->farz;
-   water_cam.nearz = cam->nearz;
-   v3_copy( cam->transform[3], water_cam.transform[3] );
-   water_cam.transform[3][1] -= 2.0f * cam_height;
-
-   m3x3f flip;
-   m3x3_identity( flip );
-   flip[1][1] = -1.0f;
-   m3x3_mul( flip, cam->transform, water_cam.transform );
-
-   vg_camera_update_view( &water_cam );
-
-   /* 
-    * Create clipped projection 
-    */
-   v4f clippa = { 0.0f, 1.0f, 0.0f, world->water.height-0.1f };
-   m4x3_mulp( water_cam.transform_inverse, clippa, clippa );
-   clippa[3] *= -1.0f;
-
-   m4x4_copy( cam->mtx.p, water_cam.mtx.p );
-   m4x4_clip_projection( water_cam.mtx.p, clippa );
-
-   vg_camera_finalize( &water_cam );
-
-   /*
-    * Draw world
-    */
-   glEnable( GL_DEPTH_TEST );
-   glDisable( GL_BLEND );
-   glCullFace( GL_FRONT );
-   render_world( world, &water_cam, 0, 1, 0, 1, _vg_render.fb_water_reflection );
-   glCullFace( GL_BACK );
-   
-   /*
-    * Create beneath view matrix
-    */
-   vg_camera beneath_cam;
-   m4x3_copy( cam->transform, beneath_cam.transform );
-   vg_camera_update_view( &beneath_cam );
-
-   float bias = -(cam->transform[3][1]-world->water.height)*0.1f;
-
-   v4f clippb = { 0.0f, -1.0f, 0.0f, -(world->water.height) + bias };
-   m4x3_mulp( beneath_cam.transform_inverse, clippb, clippb );
-   clippb[3] *= -1.0f;
-
-   m4x4_copy( cam->mtx.p, beneath_cam.mtx.p );
-   m4x4_clip_projection( beneath_cam.mtx.p, clippb );
-   vg_camera_finalize( &beneath_cam );
-
-
-   vg_framebuffer_bind( _vg_render.fb_water_refraction, _vg_render.scale );
-   glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
-   glEnable( GL_DEPTH_TEST );
-   glDisable( GL_BLEND );
-   glCullFace( GL_BACK );
-   render_world( world, &beneath_cam, 0, 1, 0, 1, _vg_render.fb_water_refraction );
-   glCullFace( GL_BACK );
-
-
-
-
-   vg_framebuffer_bind( _vg_render.fb_water_beneath, _vg_render.scale );
-   glClearColor( 1.0f, 0.0f, 0.0f, 0.0f );
-   glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
-   glEnable( GL_DEPTH_TEST );
-   glDisable( GL_BLEND );
-   render_world_depth( world, &beneath_cam );
-}
-
-void render_water_surface( world_instance *world, vg_camera *cam )
-{
-   if( !world->water.enabled )
-      return;
-
-   if( vg.quality_profile == k_quality_profile_high )
-   {
-      /* Draw surface */
-      shader_scene_water_use();
-      
-      vg_framebuffer_bind_texture( _vg_render.fb_water_reflection, 0, 0 );
-      shader_scene_water_uTexMain( 0 );
-   
-      vg_tex_bind( GL_TEXTURE_2D, &world_water.tex_water_surf, 1 );
-      shader_scene_water_uTexDudv( 1 );
-      
-      shader_scene_water_uInvRes( (v2f){ 1.0f / (f32)_vg_window.w, 1.0f / (f32)_vg_window.h });
-      WORLD_LINK_LIGHTING( world, scene_water );
-
-      vg_framebuffer_bind_texture( _vg_render.fb_water_beneath, 0, 5 );
-      shader_scene_water_uTexBack( 5 );
-      shader_scene_water_uTime( _world.time );
-      shader_scene_water_uCamera( cam->transform[3] );
-      shader_scene_water_uSurfaceY( world->water.height );
-
-      vg_framebuffer_bind_texture( _vg_render.fb_water_refraction, 0, 6 );
-      shader_scene_water_uTexRefraction( 6 );
-
-      shader_scene_water_uPv( cam->mtx.pv );
-      shader_scene_water_uPvmPrev( cam->mtx_prev.pv );
-
-      m4x3f full;
-      m4x3_identity( full );
-      shader_scene_water_uMdl( full );
-
-      //glEnable(GL_BLEND);
-      //glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
-      //glBlendEquation(GL_FUNC_ADD);
-
-      scene_mesh_bind( &world->transparent_scene_mesh );
-      for( int i=0; i<world->surface_count; i++ )
-      {
-         struct world_surface *mat = &world->surfaces[i];
-
-         if( mat->info.shader == k_shader_water )
-         {
-            VG_ASSERT(i);
-            union shader_props *props = &world->meta.shader_props[i-1];
-            shader_scene_water_uShoreColour( props->water.shore_colour );
-            shader_scene_water_uOceanColour( props->water.deep_colour );
-            shader_scene_water_uFresnel( props->water.fresnel );
-            shader_scene_water_uWaterScale( props->water.water_sale );
-            shader_scene_water_uWaveSpeed( props->water.wave_speed );
-            vg_model_draw_submesh( &mat->sm_no_collide );
-         }
-      }
-
-      glDisable(GL_BLEND);
-   }
-   else if( (vg.quality_profile == k_quality_profile_low) ||
-            (vg.quality_profile == k_quality_profile_min) )
-   {
-      shader_scene_water_fast_use();
-
-      vg_tex_bind( GL_TEXTURE_2D, &world_water.tex_water_surf, 1 );
-      shader_scene_water_fast_uTexDudv( 1 );
-
-      shader_scene_water_fast_uTime( _world.time );
-      shader_scene_water_fast_uCamera( cam->transform[3] );
-      shader_scene_water_fast_uSurfaceY( world->water.height );
-      WORLD_LINK_LIGHTING( world, scene_water_fast );
-
-      m4x3f full;
-      m4x3_identity( full );
-      shader_scene_water_fast_uMdl( full );
-      shader_scene_water_fast_uPv( cam->mtx.pv );
-      shader_scene_water_fast_uPvmPrev( cam->mtx_prev.pv );
-
-      glEnable(GL_BLEND);
-      glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
-      glBlendEquation(GL_FUNC_ADD);
-
-      scene_mesh_bind( &world->transparent_scene_mesh );
-      for( int i=0; i<world->surface_count; i++ )
-      {
-         struct world_surface *mat = &world->surfaces[i];
-
-         if( mat->info.shader == k_shader_water )
-         {
-            VG_ASSERT(i);
-            union shader_props *props = &world->meta.shader_props[i-1];
-            shader_scene_water_fast_uShoreColour( props->water.shore_colour );
-            shader_scene_water_fast_uOceanColour( props->water.deep_colour );
-
-            vg_model_draw_submesh( &mat->sm_no_collide );
-         }
-      }
-
-      glDisable(GL_BLEND);
-   }
-}
-
-static void world_water_drown(void)
-{
-   if( localplayer.immunity )
-      return;
-
-   if( localplayer.drowned ) 
-      return;
-
-   player__networked_sfx( k_player_subsystem_walk, 32, k_player_walk_soundeffect_splash, localplayer.rb.co, 1.0f );
-   vg_info( "player fell of due to walking into walker\n" );
-   localplayer.drowned = 1;
-   player__dead_transition( k_player_die_type_water );
-
-   if( !((_world.event == k_world_event_challenge) && (_world.challenge_state >= k_challenge_state_running)) )
-   {
-      vg_str str;
-      struct gui_helper *helper;
-      if( (helper = _gui_new_helper(input_button_list[k_srbind_reset], &str)) )
-      {
-         vg_strcat( &str, "Respawn" );
-      }
-   }
-}
-
-bool world_water_player_safe( world_instance *world, f32 allowance )
-{
-   if( !world->water.enabled ) 
-      return 1;
-   if( world->info.flags & k_world_flag_water_is_safe ) 
-      return 1;
-
-   if( localplayer.rb.co[1]+allowance < world->water.height )
-   {
-      world_water_drown();
-      return 0;
-   }
-
-   return 1;
-}
-
-entity_event_result ent_water_event( ent_event *event )
-{
-   world_instance *world = &_world.main;
-   if( AF_STR_EQ( world->meta.packed_strings, event->pstr_recieve_event, "drown" ) )
-   {
-      world_water_drown();
-      return k_entity_event_result_OK;
-   }
-   else return k_entity_event_result_unhandled;
-}
diff --git a/source/vg_water/vg_water.h b/source/vg_water/vg_water.h
deleted file mode 100644 (file)
index e7538f7..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-
-void _vg_water_set_height( f32 height );
-
-void render_water_texture( world_instance *world, vg_camera *cam );
-void render_water_surface( world_instance *world, vg_camera *cam );
-entity_event_result ent_water_event( ent_event *event );
-bool world_water_player_safe( world_instance *world, f32 allowance );
diff --git a/source/vg_water/vg_water.kv b/source/vg_water/vg_water.kv
deleted file mode 100644 (file)
index 4c15cda..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-add vg_water.c
-include ""
-
-shader
-{
-   name vg_water
-
-   subshader
-   {
-      type vertex
-   }
-}
diff --git a/source/vg_water/water.fs b/source/vg_water/water.fs
deleted file mode 100644 (file)
index a7be103..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-uniform sampler2D uTexMain;
-uniform sampler2D uTexDudv;
-uniform sampler2D uTexBack;
-uniform sampler2D uTexRefraction;
-
-uniform vec2 uInvRes;
-uniform float uTime;
-uniform vec3 uCamera;
-uniform float uSurfaceY;
-uniform vec3 uBoard0;
-uniform vec3 uBoard1;
-
-uniform vec3 uShoreColour;
-uniform vec3 uOceanColour;
-uniform float uFresnel;
-uniform float uWaterScale;
-uniform vec4 uWaveSpeed;
-
-#include "light_clearskies_stddef.glsl"
-#include "common_scene.glsl"
-#include "motion_vectors_fs.glsl"
-
-// Pasted from common_world.glsl
-vec3 water_compute_lighting( vec3 diffuse, vec3 normal, vec3 co )
-{
-   float light_mask = compute_board_shadow();
-
-   if( g_light_preview == 1 )
-      diffuse = vec3(0.75);
-
-   // Lighting
-   vec3 halfview = uCamera - co;
-   float fdist = length(halfview);
-   halfview /= fdist;
-
-   float world_shadow = newlight_compute_sun_shadow( 
-               co, g_sun_dir.xyz * (1.0/(max(g_sun_dir.y,0.0)+0.2)) );
-
-   vec3 total_light = clearskies_lighting( 
-                           normal, min( light_mask, world_shadow ), halfview );
-
-   vec3 cube_coord = (co - g_cube_min.xyz) * g_cube_inv_range.xyz;
-        cube_coord = floor( cube_coord );
-
-   if( g_debug_indices == 1 )
-   {
-      return rand33(cube_coord);
-   }
-
-   if( g_debug_complexity == 1 )
-   {
-      ivec3 coord = ivec3( cube_coord );
-      uvec4 index_sample = texelFetch( uLightsIndex, coord, 0 );
-
-      uint light_count = (index_sample.x & 0x3u) + (index_sample.y & 0x3u);
-      return vec3( float(light_count)*(1.0/6.0), 0.0, 0.5 );
-   }
-
-   // FIXME: this coord should absolutely must be clamped!
-   
-   ivec3 coord = ivec3( cube_coord );
-   uvec4 index_sample = texelFetch( uLightsIndex, coord, 0 );
-
-   total_light += 
-      scene_calculate_packed_light_patch( index_sample.x,
-                                          halfview, co, normal ) 
-                                          * light_mask;
-   total_light += 
-      scene_calculate_packed_light_patch( index_sample.y,
-                                          halfview, co, normal )
-                                          * light_mask;
-
-   return diffuse * total_light;
-}
-
-vec4 water_surf( vec3 halfview, vec3 vnorm, float depthvalue, 
-                 vec4 beneath, vec4 above, vec4 dudva )
-{
-   vec3 surface_tint = mix(uShoreColour, uOceanColour, depthvalue);
-
-   float ffresnel = pow(1.0-dot( vnorm, halfview ),uFresnel);
-
-   vec3 lightdir = vec3(0.95,0.0,-0.3);
-   vec3 specdir = reflect( -lightdir, vnorm );
-   float spec = pow(max(dot(halfview,specdir),0.0),20.0)*0.3;
-   
-   // Depth 
-   float depthblend = pow( beneath.r, 0.8 );
-
-   // Foam
-   float fband = fract( aCo.z*0.02+uTime*0.1+depthvalue*10.0 );
-   fband = step( fband+dudva.a*0.8, 0.3 ) * max((1.0-depthvalue*4.0),0.0);
-
-   //vec4 surf = mix( vec4(surface_tint,depthblend), vec4(1.0,1.0,1.0,0.5), fband );
-   vec4 surf = vec4(surface_tint,depthblend);
-   surf.rgb = water_compute_lighting( surf.rgb, aNorm.xyz, aWorldCo );
-   surf.rgb = mix(surf.rgb, above.rgb, ffresnel );
-
-   // Take a section of the sky function to give us a matching fog colour
-   vec3 fog_colour  = clearskies_ambient( -halfview );
-   float sun_theta  = dot( -halfview, g_sun_dir.xyz );
-   float sun_size   = max( 0.0, sun_theta * 0.5 + 0.5 );
-   float sun_shape  = sun_size * max(g_sun_dir.y,0.0) * 0.5;
-         
-   vec3 sun_colour  = mix( vec3(1.0), g_sunset_colour.rgb, g_sunset_phase*0.5 );
-        sun_colour *= sun_shape;
-
-   fog_colour += sun_colour;
-   surf.rgb = scene_apply_fog( surf.rgb, fog_colour,  
-                               distance(uCamera, aWorldCo) );
-
-   return surf;
-}
-
-void main()
-{
-   compute_motion_vectors();
-
-   // Create texture coords
-   vec2 ssuv = gl_FragCoord.xy*uInvRes;
-   
-   // Surface colour composite
-   float depthvalue = clamp( -world_water_depth(aCo)*(1.0/25.0)*2.0, 0.0,1.0 );
-
-   vec2 world_coord = aCo.xz * uWaterScale;
-   vec4 time_offsets = vec4( uTime ) * uWaveSpeed;
-   vec4 dudva = texture( uTexDudv, world_coord + time_offsets.xy )-0.5;
-   vec4 dudvb = texture( uTexDudv, world_coord *7.0 - time_offsets.zw )-0.5;
-
-   vec3 surfnorm = dudva.rgb + dudvb.rgb;
-   surfnorm = normalize(vec3(0.0,1.0,0.0) + dudva.xyz*0.4 + dudvb.xyz*0.1);
-   
-   // Lighting
-   vec3 halfview = -normalize( aCo-uCamera );
-
-   // Sample textures
-   vec4 above = texture( uTexMain, ssuv + surfnorm.xz*0.2 );
-   vec4 beneath = texture( uTexBack, ssuv );
-
-   float ripple_strength = 0.6;
-   vec4 rippled = texture( uTexRefraction, ssuv  - surfnorm.xz*ripple_strength*depthvalue );
-
-   // Fog
-   float fdist = pow(length( aCo.xz-uCamera.xz ) * 0.00047, 2.6);
-
-   // Composite
-   vec4 vsurface = water_surf( halfview, surfnorm, depthvalue, beneath, above, dudva );
-   vsurface.a -= fdist;
-
-   oColour = vec4( mix( rippled.rgb*0.9, vsurface.rgb, vsurface.a ), 1.0 );
-}
diff --git a/submodules/SDL b/submodules/SDL
new file mode 160000 (submodule)
index 0000000..cbcb145
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit cbcb145eb4d0b1a9ef2f57a837d42fef1f930b65