[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
+++ /dev/null
-add source/foundation/async.c
-
-hook
-{
- event START
- function _async_init
-}
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 \
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
+++ /dev/null
-include include/
-include source/
+++ /dev/null
-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"
- }
-}
+++ /dev/null
-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
-}
+++ /dev/null
-add source/graphics/font.c
-add source/graphics/ui.c
-add source/graphics/graphics.c
-add source/graphics/graphics_software.c
+++ /dev/null
-/* 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 );
+++ /dev/null
-#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 );
+++ /dev/null
-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 );
+++ /dev/null
-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"
+++ /dev/null
-#define GLFW_INCLUDE_NONE
-#include "vg/dep/glfw-3.4/include/GLFW/glfw3.h"
+++ /dev/null
-/* 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 );
+++ /dev/null
-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"
+++ /dev/null
-#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;
-}
-
+++ /dev/null
-/*
- * 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 );
+++ /dev/null
-#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 );
+++ /dev/null
-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
+++ /dev/null
-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;
-}
+++ /dev/null
-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;
-}
+++ /dev/null
-out vec4 FragColor;
-uniform vec4 uColour;
-
-in vec2 aUv;
-
-void main()
-{
- FragColor = uColour;
-}
+++ /dev/null
-out vec4 FragColor;
-in vec2 aUv;
-
-void main()
-{
- FragColor = texture( uTexMain, aUv );
-}
+++ /dev/null
-out vec4 FragColor;
-
-in vec4 s_colour;
-
-void main()
-{
- FragColor = s_colour;
-}
+++ /dev/null
-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;
-}
+++ /dev/null
-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);
-}
+++ /dev/null
-const float k_motion_lerp_amount = 0.01;
+++ /dev/null
-#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 );
-}
+++ /dev/null
-#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 );
-}
+++ /dev/null
-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;
-}
+++ /dev/null
-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;
-}
+++ /dev/null
-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;
-}
+++ /dev/null
-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;
-}
+++ /dev/null
-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;
-}
+++ /dev/null
-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;
-}
+++ /dev/null
-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);
-}
+++ /dev/null
-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 );
-}
--- /dev/null
+include ""
+
+{
+ if SDL
+ add sdl_async.c
+}
+
+hook
+{
+ affinity -20000
+ event START
+ function _async_init
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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 );
--- /dev/null
+#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 );
+}
--- /dev/null
+#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 );
--- /dev/null
+include ""
+add console_system.c
+
+hook
+{
+ event START
+ function _console_init
+}
+++ /dev/null
-#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 );
-}
+++ /dev/null
-#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 );
--- /dev/null
+#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;
+}
--- /dev/null
+#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 );
.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)
{
/* 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 ) );
_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();
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();
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 );
}
}
-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 ++ )
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 ++ )
{
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;
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;
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 )
{
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;
-#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;
--- /dev/null
+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
+++ /dev/null
-#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} );
- }
- }
- }
- }
-}
+++ /dev/null
-#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;
-}
--- /dev/null
+#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
--- /dev/null
+#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] );
--- /dev/null
+#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;
+}
--- /dev/null
+#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] );
--- /dev/null
+#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 );
+}
--- /dev/null
+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;
+};
--- /dev/null
+#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;
+};
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 )
+++ /dev/null
-#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 ] );
-}
--- /dev/null
+#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];
--- /dev/null
+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;
+}
--- /dev/null
+out vec4 FragColor;
+in vec2 aUv;
+
+void main()
+{
+ FragColor = texture( uTexMain, aUv );
+}
--- /dev/null
+out vec4 FragColor;
+
+in vec4 s_colour;
+
+void main()
+{
+ FragColor = s_colour;
+}
--- /dev/null
+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;
+}
+++ /dev/null
-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
--- /dev/null
+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
+++ /dev/null
-#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) );
-}
+++ /dev/null
-#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 );
-}
--- /dev/null
+#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 );
+}
--- /dev/null
+#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
+}
--- /dev/null
+#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 );
--- /dev/null
+#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 );
+}
--- /dev/null
+//#define VG_ECHO_LPF_BUTTERWORTH
+#pragma once
+
+void _dsp_update_tunings( f32 echo_distances[14] );
+void _dsp_process( f32 *stereo_in, f32 *stereo_out );
--- /dev/null
+#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;
+}
--- /dev/null
+#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 );
--- /dev/null
+#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 );
+}
--- /dev/null
+#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 );
--- /dev/null
+#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;
+}
--- /dev/null
+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;
--- /dev/null
+{
+ 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
#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
{
}
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" );
#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
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
--- /dev/null
+#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} );
+ }
+ }
+ }
+ }
+}
--- /dev/null
+#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"
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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 );
--- /dev/null
+#pragma once
+#include "SDL3/SDL.h"
+#include "vg/dep/glad.4.3/glad/glad.h"
--- /dev/null
+#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 );
+}
--- /dev/null
+#pragma once
+void _render_fullscreen_quad(void);
--- /dev/null
+#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 ] );
+}
--- /dev/null
+#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 );
--- /dev/null
+#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) );
+}
--- /dev/null
+#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 );
--- /dev/null
+#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 );
+}
--- /dev/null
+#pragma once
+
+void _engine_ui_pre_render(void);
+void _engine_ui_post_render(void);
+#include "foundation.h"
#include <stdlib.h>
-#include "common_api.h"
void *_heap_allocate( u64 size )
{
-#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 )
{
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 );
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;
-#include "common_api.h"
+#include "foundation.h"
void queue_init( struct queue_allocator *queue, void *buffer, u32 buffer_size )
{
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 )
{
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 )
-#include "common_api.h"
+#include "foundation.h"
void stack_init( struct stack_allocator *stack, void *buffer, u32 capacity, const c8 *debug_name )
{
-#include "common_api.h"
+#include "foundation.h"
+
#define SMALL_SEGMENTS 4
void stretchy_init( struct stretchy_allocator *stretchy, u32 element_size )
{
+++ /dev/null
-#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;
-}
-#include "common_api.h"
+#include "foundation.h"
void zero_buffer( void *buffer, u32 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;
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 );
-#include "common_api.h"
+#include "foundation.h"
#include <signal.h>
#include <execinfo.h>
#include <stdio.h>
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();
}
--- /dev/null
+#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);
+}
--- /dev/null
+/* 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 );
--- /dev/null
+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
+}
-#include "common_api.h"
-
+#include "foundation.h"
#include <dirent.h>
#include <stdio.h>
#include <sys/stat.h>
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] == '.' )
return 0;
}
-bool directory_next_entry( struct directory *directory )
+b8 directory_next_entry( struct directory *directory )
{
while( (directory->data = readdir(directory->handle)) )
{
-#include "common_api.h"
+#include "foundation.h"
#include <fileapi.h>
#include <shlobj.h>
#include <stdio.h>
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);
}
-#include "common_api.h"
+#include "foundation.h"
#include <string.h>
#define KV_PAGE_COUNT 32
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;
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 );
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 );
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 );
if( c == '\0' )
break;
- bool is_control_character = 0;
+ b8 is_control_character = 0;
if( parser.token0_deliminator )
{
if( c == parser.token0_deliminator )
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 ++ )
{
}
#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 ) )
}
#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 ) )
-#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;
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;
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 ];
}
_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 ++ ];
_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';
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;
_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;
-#include "common_api.h"
+#include "foundation.h"
+
#define MAX_OPTIONS 32
#define MAX_ARGUMENTS 32
_normal_exit();
}
- bool errors = 0;
+ b8 errors = 0;
for( u32 i=0; i<_options.argument_count; i ++ )
{
struct argument *argument = &_options.arguments[ i ];
_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 ++ )
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;
-#include "common_api.h"
+#include "foundation.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
}
}
-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 ) )
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 );
stream->offset = offset;
}
-bool stream_error( struct stream *stream )
+b8 stream_error( struct stream *stream )
{
return stream->flags & k_stream_overflow_error? 1: 0;
}
-#include "common_api.h"
+#include "foundation.h"
#include <errno.h>
#include <string.h>
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' )
info = string_parse_c8( string, &c );
}
- bool got = 0;
+ b8 got = 0;
while( info == k_string_parse_ok )
{
if( c >= '0' && c <= '9' )
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 == '.' )
-#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)
{
--- /dev/null
+#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 )
+{
+}
--- /dev/null
+#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 );
+}
-#include "common_api.h"
-#include "graphics_api.h"
+#include "foundation.h"
+#include "vg_graphics.h"
struct _font _font =
{
_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,
+++ /dev/null
-#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 );
-}
--- /dev/null
+include ""
+add font.c
+add ui.c
+add vg_graphics.c
+add graphics_software.c
-#include "common_api.h"
-#include "graphics_api.h"
+#include "foundation.h"
+#include "vg_graphics.h"
struct
{
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;
-#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
//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
k_ui_control_button
}
active_control_type;
- bool active_control_touched;
- bool redraw;
+ b8 active_control_touched;
+ b8 redraw;
union
{
_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]);
}
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' )
{
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)) )
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 )
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] )
_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 )
i16 kerning[3];
_font_get_kerning( kerning );
- bool init = 0;
+ b8 init = 0;
if( _ui_button( rect ) == k_button_click )
init = 1;
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 )
return k_dropdown_none;
}
-bool _ui_update(void)
+b8 _ui_update(void)
{
if( _input_button_down( k_input_action_ui_click, 0 ) )
{
_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 );
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 )
loop = 0;
}
} while( loop );
- bool loop1; do
+ b8 loop1; do
{
loop1 = ui_string_decode( &decoder1 );
if( decoder1.grid_co[1] == user_co[1]-1 )
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 )
_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;
--- /dev/null
+#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 );
+}
--- /dev/null
+/* 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 );
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],
}
}
-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 ),
--- /dev/null
+#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;
+}
+
--- /dev/null
+include ""
+add random.c
--- /dev/null
+#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);
+}
+
--- /dev/null
+#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 );
--- /dev/null
+/*
+ * 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 );
+++ /dev/null
-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 );
#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;
struct
{
- bool using;
+ b8 using;
struct stream layer_enums;
struct stream input_enums, input_structures;
}
struct
{
- bool using;
+ b8 using;
struct stream enums, structures;
}
static _threads;
};
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;
}
};
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;
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 ++ )
{
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;
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 )
{
}
$v_string( &source, {"\n"} );
- bool sorted = 0;
+ b8 sorted = 0;
while( !sorted )
{
sorted = 1;
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"} );
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 "} );
--- /dev/null
+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
+++ /dev/null
-#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 );
-}
+++ /dev/null
-#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 );
+++ /dev/null
-add vg_camera.c
-include ""
+++ /dev/null
-out vec4 FragColor;
-
-in vec4 s_colour;
-
-void main()
-{
- FragColor = s_colour;
-}
+++ /dev/null
-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;
-}
+++ /dev/null
-#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;
- }
-}
+++ /dev/null
-#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 );
+++ /dev/null
-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
- }
-}
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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 );
+++ /dev/null
-#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 );
-}
+++ /dev/null
-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;
-};
+++ /dev/null
-#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;
-};
+++ /dev/null
-#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
+++ /dev/null
-#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] );
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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] );
+++ /dev/null
-add model.c
-add metascene.c
-add array_file.c
-include ""
+++ /dev/null
-#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];
+++ /dev/null
-#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) );
-}
+++ /dev/null
-#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 );
+++ /dev/null
-add vg_tex.c
-include ""
-
-hook
-{
- event START
- function _vg_tex_init
-}
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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 );
+++ /dev/null
-add vg_water.c
-include ""
-
-shader
-{
- name vg_water
-
- subshader
- {
- type vertex
- }
-}
+++ /dev/null
-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 );
-}
--- /dev/null
+Subproject commit cbcb145eb4d0b1a9ef2f57a837d42fef1f930b65