fffffffffff
authorhgn <hgodden00@gmail.com>
Sat, 29 Nov 2025 19:26:38 +0000 (19:26 +0000)
committerhgn <hgodden00@gmail.com>
Sat, 29 Nov 2025 19:26:38 +0000 (19:26 +0000)
41 files changed:
source/engine/array_file.c [deleted file]
source/engine/array_file.h [deleted file]
source/engine/audio_mixer.c [deleted file]
source/engine/audio_mixer.c_UNUSED [new file with mode: 0644]
source/engine/console.c [deleted file]
source/engine/engine.kv
source/engine/metascene.c [deleted file]
source/engine/metascene.h [deleted file]
source/engine/model.c [deleted file]
source/engine/model.h [deleted file]
source/engine/model_entity.h [deleted file]
source/engine/profiler.c [deleted file]
source/engine/profiler.c_UNUSED [new file with mode: 0644]
source/engine/steamworks.c_UNUSED [deleted file]
source/engine/ui.c_UNUSED [deleted file]
source/engine/vg_af.c [new file with mode: 0644]
source/engine/vg_af.h [new file with mode: 0644]
source/engine/vg_audio.h
source/engine/vg_audio_dsp.h
source/engine/vg_console.c [new file with mode: 0644]
source/engine/vg_console.h [new file with mode: 0644]
source/engine/vg_engine.c
source/engine/vg_engine.h
source/engine/vg_engine_core.kv [deleted file]
source/engine/vg_entity.c [new file with mode: 0644]
source/engine/vg_entity.h [new file with mode: 0644]
source/engine/vg_framebuffer.h
source/engine/vg_input.h
source/engine/vg_lines.h
source/engine/vg_metascene.c [new file with mode: 0644]
source/engine/vg_metascene.h [new file with mode: 0644]
source/engine/vg_model.c [new file with mode: 0644]
source/engine/vg_model.h [new file with mode: 0644]
source/engine/vg_render.h
source/engine/vg_shader.h
source/engine/vg_tex.h
source/engine/vg_ui.h
source/foundation/foundation.h
source/foundation/foundation.kv
source/foundation/keyvalues.c
source/tools/metacompiler.c

diff --git a/source/engine/array_file.c b/source/engine/array_file.c
deleted file mode 100644 (file)
index 3c34f72..0000000
+++ /dev/null
@@ -1,356 +0,0 @@
-#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 defined( __EMSCRIPTEN__ )
-   if( arr->item_count )
-   {
-      stream_seek( ctx->stream, arr->file_offset );
-      if( stride != arr->item_size )
-      {
-         zero_buffer( buffer, stride*arr->item_count );
-         u32 temp_frame = _start_temporary_frame();
-         {
-            u8 *temp = _temporary_allocate( arr->item_count * arr->item_size, 8 );
-            stream_read( ctx->stream, temp, arr->item_count * arr->item_size );
-
-            u32 read_size = u32_min( stride, arr->item_size );
-            for( u32 i=0; i<arr->item_count; i++ )
-               buffer_copy( temp + i*arr->item_size, read_size, buffer+i*stride, read_size );
-         }
-         _end_temporary_frame( temp_frame );
-      }
-      else
-         stream_read( ctx->stream, buffer, arr->item_count * arr->item_size );
-   }
-#else
-   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 );
-      }
-   }
-#endif
-}
-
-void af_load_array_file( struct array_file_context *ctx, struct array_file_ptr *out_ptr, 
-                         struct array_file_meta *arr, struct stack_allocator *stack, u32 stride )
-{
-   if( arr->item_count )
-   {
-      u32 size = stride*arr->item_count;
-      out_ptr->data = stack_allocate( stack, size, 8, NULL );
-      af_load_array_file_buffer( ctx, arr, out_ptr->data, stride );
-   }
-   else
-      out_ptr->data = NULL;
-   
-   out_ptr->stride = stride;
-   out_ptr->count = arr->item_count;
-}
-
-void *af_arritm( struct array_file_ptr *arr, u32 index )
-{
-   if( index >= arr->count )
-   {
-      $log( $fatal, {"Index out of range"}, $unsigned(index), {" >= "}, $unsigned( arr->count ) );
-      _fatal_exit();
-   }
-
-   return ((u8 *)arr->data) + index*arr->stride;
-}
-
-u32 af_arrcount( struct array_file_ptr *arr )
-{
-   return arr->count;
-}
-
-struct array_file_meta *af_find_array( struct array_file_context *ctx, const c8 *name )
-{
-   for( u32 i=0; i<af_arrcount(&ctx->index); i++ )
-   {
-      struct array_file_meta *arr = af_arritm( &ctx->index, i );
-      if( compare_buffers( arr->name, 0, name, 0 ) )
-         return arr;
-   }
-
-   return NULL;
-}
-
-b8 af_load_array( struct array_file_context *ctx, struct array_file_ptr *ptr, const c8 *name, 
-                   struct stack_allocator *stack, u32 stride )
-{
-   struct array_file_meta *arr = af_find_array( ctx, name );
-
-   if( arr )
-   {
-      af_load_array_file( ctx, ptr, arr, stack, stride );
-      return 1;
-   }
-   else
-   {
-      ptr->data = NULL;
-      ptr->count = 0;
-      ptr->stride = 0;
-      return 0;
-   }
-}
-
-b8 af_open_stream( struct array_file_context *afc, struct stream *stream, u32 min_version, u32 max_version, 
-                     struct stack_allocator *stack )
-{
-   afc->stream = stream;
-   if( stream_read( stream, &afc->header, sizeof(struct array_file_header)) != sizeof(struct array_file_header) )
-   {
-      $log( $error, {"Array file not large enough to contain header."} );
-      return 0;
-   }
-
-   if( (afc->header.version < min_version) || (afc->header.version > max_version) )
-   {
-      $log( $error, {"Array file version is out of range ("}, $unsigned( afc->header.version ),
-                    {"\nAccepted: "}, $unsigned( min_version ), {" -> "}, $unsigned( max_version ) );
-      return 0;
-   }
-
-   af_load_array_file( afc, &afc->index, &afc->header.index, stack, sizeof(struct array_file_meta) );
-   return 1;
-}
-
-/* compiler
- * ---------------------------------------------------------------------- */
-struct af_compiler_iter
-{
-   u32 i, j;
-   struct af_compiler_index *index;
-   struct af_compiler_item *current_item;
-   void *data;
-};
-
-static void af_init_iterator( struct af_compiler_iter *iter, struct af_compiler_index *index )
-{
-   iter->i = 0;
-   iter->j = 0;
-   iter->index = index;
-   iter->current_item = NULL;
-   iter->data = NULL;
-}
-
-static b8 af_next( struct af_compiler_iter *iter )
-{
-   if( iter->i == 0 )
-   {
-      if( iter->index->first == NULL )
-         return 0;
-      iter->current_item = iter->index->first;
-   }
-
-   if( iter->j >= iter->current_item->count )
-   {
-      if( iter->current_item->next == NULL )
-         return 0;
-
-      iter->current_item = iter->current_item->next;
-      iter->j = 0;
-   }
-
-   iter->data = iter->current_item->data + (iter->j * iter->index->element_size);
-   iter->j ++;
-   iter->i ++;
-   return 1;
-}
-
-struct af_compiler_item *af_compiler_allocate_items( struct af_compiler *compiler, struct af_compiler_index *index, u32 count )
-{
-   struct af_compiler_item *entry = stack_allocate( compiler->stack, sizeof(struct af_compiler_item), 1, "Compiler item" );
-   entry->next = NULL;
-
-   u32 data_size = count * index->element_size;
-   index->element_count += count;
-
-   entry->data = stack_allocate( compiler->stack, data_size, 8, NULL );
-   entry->count = count;
-
-   for( u32 i=0; i<data_size; i ++ )
-      ((u8 *)entry->data)[i] = 0xab;
-
-   if( index->last )
-      index->last->next = entry;
-   index->last = entry;
-
-   if( !index->first )
-      index->first = entry;
-
-   return entry;
-}
-
-static void af_compiler_init_index( struct af_compiler_index *index, const c8 *alias, u32 element_size )
-{
-   ASSERT_CRITICAL( element_size );
-   if( !buffer_copy( alias, 0, index->name, sizeof(index->name) ) )
-   {
-      $log( $fatal, {"Index name overflowed: "}, {alias} );
-      _fatal_exit();
-   }
-   index->element_size = element_size;
-   index->element_count = 0;
-   index->first = NULL;
-   index->last = NULL;
-}
-
-struct af_compiler_index *af_compiler_create_index( struct af_compiler *compiler, const c8 *alias, u32 element_size )
-{
-   struct af_compiler_item *item = af_compiler_allocate_items( compiler, &compiler->index, 1 );
-   struct af_compiler_index *index = item->data;
-   af_compiler_init_index( index, alias, element_size );
-   return index;
-}
-
-u32 af_compile_string( struct af_compiler *compiler, const c8 *string )
-{
-   u32 string_hash = buffer_djb2( string, 0 );
-
-   // TODO: Hash table against existing strings (low priority)
-   u32 offset = offset = compiler->strings_index->element_count;
-   u32 bytes = PAD_TO_4( buffer_first_index( string, 0,0 )+1 + 4 );
-   struct af_compiler_item *item = af_compiler_allocate_items( compiler, compiler->strings_index, bytes );
-   *((u32 *)item->data) = string_hash;
-   buffer_copy( string, 0, item->data+4, 0 );
-   return offset;
-}
-
-void af_compiler_init( struct af_compiler *compiler, struct stack_allocator *stack )
-{
-   compiler->stack = stack;
-   af_compiler_init_index( &compiler->index, "index", sizeof(struct af_compiler_index) );
-   compiler->strings_index = af_compiler_create_index( compiler, "strings", 1 );
-   af_compile_string( compiler, "nul" );
-}
-
-static void af_write_bin( struct af_compiler *compiler, void *data, u32 data_len, u32 padding )
-{
-   if( data )
-   {
-      stream_write( &compiler->stream, data, data_len );
-      compiler->file_offset += data_len;
-   }
-
-   if( padding )
-   {
-      while( compiler->file_offset % padding )
-      {
-         const u8 pad_byte = 0xac;
-         stream_write( &compiler->stream, &pad_byte, 1 );
-         compiler->file_offset ++;
-      }
-   }
-}
-
-struct af_compiler_index *af_get_or_make_index( struct af_compiler *compiler, const c8 *alias, u32 element_size )
-{
-   struct af_compiler_iter iter;
-   af_init_iterator( &iter, &compiler->index );
-   while( af_next( &iter ) )
-   {
-      struct af_compiler_index *index = iter.data;
-      if( compare_buffers( index->name, 0, alias, 0 ) )
-         return index;
-   }
-   
-   return af_compiler_create_index( compiler, alias, element_size );
-}
-
-b8 af_write( struct af_compiler *compiler, const c8 *path, u32 version )
-{
-   u32 indices_to_write = 0;
-   
-   struct af_compiler_iter iter;
-   af_init_iterator( &iter, &compiler->index );
-   while( af_next( &iter ) )
-   {
-      struct af_compiler_index *index = iter.data;
-      if( index->element_count )
-         indices_to_write ++;
-   }
-
-   u32 header_size = PAD_TO_8( sizeof( struct array_file_header ) );
-   u32 index_size = PAD_TO_8( sizeof( struct array_file_meta ) * indices_to_write );
-   
-   if( !stream_open_file( &compiler->stream, path, k_stream_write ) )
-      return 0;
-
-   compiler->file_offset = 0;
-
-   struct array_file_header header;
-   header.version = version;
-   header.index.file_offset = header_size;
-   header.index.item_count = indices_to_write;
-   header.index.item_size = sizeof(struct array_file_meta);
-   buffer_copy( "index", 0, header.index.name, sizeof(header.index.name) );
-   af_write_bin( compiler, &header, sizeof(struct array_file_header), 8 );
-
-   /* write index */
-   u32 file_offset = header_size + index_size;
-   af_init_iterator( &iter, &compiler->index );
-   while( af_next( &iter ) )
-   {
-      struct af_compiler_index *index = iter.data;
-      if( index->element_count )
-      {
-         struct array_file_meta meta;
-         buffer_copy( index->name, sizeof(index->name), meta.name, sizeof(meta.name) );
-         meta.item_count = index->element_count;
-         meta.item_size = index->element_size;
-         meta.file_offset = file_offset;
-         file_offset += PAD_TO_8( meta.item_size*meta.item_count );
-         af_write_bin( compiler, &meta, sizeof(struct array_file_meta), 0 );
-      }
-   }
-   af_write_bin( compiler, NULL, 0, 8 );
-
-   af_init_iterator( &iter, &compiler->index );
-   while( af_next( &iter ) )
-   {
-      struct af_compiler_index *index = iter.data;
-
-      if( index->element_count )
-      {
-         struct af_compiler_iter item_iter;
-         af_init_iterator( &item_iter, index );
-
-         while( af_next( &item_iter ) )
-            af_write_bin( compiler, item_iter.data, index->element_size, 0 );
-         af_write_bin( compiler, NULL, 0, 8 );
-      }
-   }
-
-   stream_close( &compiler->stream );
-   return 1;
-}
diff --git a/source/engine/array_file.h b/source/engine/array_file.h
deleted file mode 100644 (file)
index 5abd164..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#pragma once
-
-struct array_file_ptr
-{
-   void *data;
-   u32 count, stride;
-};
-
-struct array_file_meta
-{
-   u32 file_offset,
-       item_count,
-       item_size;
-
-   c8 name[16];
-};
-
-struct array_file_header
-{
-   u32 version;
-   struct array_file_meta index;
-};
-
-struct array_file_context
-{
-   struct stream *stream;
-   struct array_file_header header;
-   struct array_file_ptr index;
-};
-
-/* array loading */
-struct array_file_meta *af_find_array( struct array_file_context *ctx, const c8 *name );
-
-void af_load_array_file( struct array_file_context *ctx, struct array_file_ptr *out_ptr, 
-                         struct array_file_meta *arr, struct stack_allocator *stack, u32 stride );
-
-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( PACKED, PSTR, CONSTR ) \
-   af_str_eq( PACKED, PSTR, CONSTR, buffer_djb2( CONSTR, 0 ) )
-
-/* 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 );
diff --git a/source/engine/audio_mixer.c b/source/engine/audio_mixer.c
deleted file mode 100644 (file)
index 0209e62..0000000
+++ /dev/null
@@ -1,1455 +0,0 @@
-struct vg_audio _vg_audio = 
-{ 
-   .master_volume_ui = 1.0f,
-   .dsp_enabled_ui = 1
-};
-
-_Thread_local static b8 _vg_audio_thread_has_lock = 0;
-
-void vg_audio_lock(void)
-{
-   SDL_LockMutex( _vg_audio.mutex );
-   _vg_audio_thread_has_lock = 1;
-}
-
-void vg_audio_unlock(void)
-{
-   _vg_audio_thread_has_lock = 0;
-   SDL_UnlockMutex( _vg_audio.mutex );
-}
-
-static void vg_audio_assert_lock(void)
-{
-   if( _vg_audio_thread_has_lock == 0 )
-   {
-      vg_error( "vg_audio function requires locking\n" );
-      abort();
-   }
-}
-
-/* clip loading from disk
- * -------------------------------------------------------------------------------
- */
-VG_TIER_2 b8 vg_audio_clip_load( audio_clip *clip, vg_stack_allocator *stack )
-{
-   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_BACKGROUND ) );
-
-   /* load in directly */
-   u32 format = clip->flags & AUDIO_FLAG_FORMAT;
-   if( format == k_audio_format_vorbis )
-   {
-      if( clip->path )
-      {
-         clip->any_data = vg_file_read( stack, clip->path, &clip->size, 0 );
-         if( clip->any_data )
-         {
-            float mb = (float)(clip->size) / (1024.0f*1024.0f);
-            vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb );
-            return 1;
-         }
-         else
-         {
-            vg_error( "Audio failed to load '%s'\n", clip->path );
-            return 0;
-         }
-      }
-      else
-      {
-         vg_error( "No path specified, embeded vorbis unsupported\n" );
-         return 0;
-      }
-   }
-   else if( format == k_audio_format_stereo )
-   {
-      vg_error( "Unsupported format (Stereo uncompressed)\n" );
-      return 0;
-   }
-   else if( format == k_audio_format_bird )
-   {
-      if( !clip->any_data )
-      {
-         vg_error( "No data, external birdsynth unsupported\n" );
-         return 0;
-      }
-
-      struct synth_bird *bird = vg_stack_allocate( stack, sizeof(struct synth_bird), 8, NULL );
-      bird->settings = clip->any_data;
-      clip->any_data = bird;
-      vg_info( "Loaded bird synthesis pattern (%u bytes)\n", clip->size );
-      return 1;
-   }
-   else
-   {
-      if( !clip->path )
-      {
-         vg_error( "No path specified, embeded mono unsupported\n" );
-         return 0;
-      }
-
-      u32 temp_frame = _vg_start_temp_frame();
-      stb_vorbis_alloc alloc = 
-      {
-         .alloc_buffer = vg_stack_allocate( _vg_temp_stack(), AUDIO_DECODE_SIZE, 8, "Vorbis alloc buffer (temp)" ),
-         .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE
-      };
-
-      u32 fsize;
-      void *filedata = vg_file_read( _vg_temp_stack(), clip->path, &fsize, 0 );
-      int err;
-      stb_vorbis *decoder = stb_vorbis_open_memory( filedata, fsize, &err, &alloc );
-      if( !decoder )
-      {
-         vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", clip->path, err );
-         _vg_end_temp_frame( temp_frame );
-         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 = vg_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 );
-      _vg_end_temp_frame( temp_frame );
-
-      if( read_samples == length_samples )
-         return 1;
-      else
-      {
-         vg_error( "Decode error, read_samples did not match length_samples\n" );
-         return 0;
-      }
-   }
-}
-
-VG_TIER_2 u32 vg_audio_clip_loadn( audio_clip *arr, u32 count, vg_stack_allocator *stack )
-{
-   u32 succeeded = 0;
-   for( u32 i=0; i<count; i++ )
-      succeeded += (u32)vg_audio_clip_load( &arr[i], stack );
-   return succeeded;
-}
-
-/* 
- * -------------------------------------------------------------------------------
- */
-
-static audio_channel *get_audio_channel( audio_channel_id id )
-{
-   VG_ASSERT( (id > 0) && (id <= AUDIO_CHANNELS) );
-   return &_vg_audio.channels[ id-1 ];
-}
-
-static struct audio_channel_controls *get_audio_channel_controls( audio_channel_id id )
-{
-   vg_audio_assert_lock();
-   VG_ASSERT( (id > 0) && (id <= AUDIO_CHANNELS) );
-   return &_vg_audio.channels[ id-1 ].controls;
-}
-
-static struct audio_channel_state *get_audio_channel_state( audio_channel_id id )
-{
-   VG_ASSERT( (id > 0) && (id <= AUDIO_CHANNELS) );
-   return &_vg_audio.channels[ id-1 ].state;
-}
-
-static audio_lfo *get_audio_lfo( audio_channel_id lfo_id )
-{
-   VG_ASSERT( (lfo_id > 0) && (lfo_id <= AUDIO_LFOS) );
-   return &_vg_audio.lfos[ lfo_id-1 ];
-}
-
-static struct audio_lfo_controls *get_audio_lfo_controls( audio_channel_id lfo_id )
-{
-   vg_audio_assert_lock();
-   VG_ASSERT( (lfo_id > 0) && (lfo_id <= AUDIO_LFOS) );
-   return &_vg_audio.lfos[ lfo_id-1 ].controls;
-}
-
-static struct audio_lfo_state *get_audio_lfo_state( audio_channel_id lfo_id )
-{
-   VG_ASSERT( (lfo_id > 0) && (lfo_id <= AUDIO_LFOS) );
-   return &_vg_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 vg_audio_get_first_idle_channel(void)
-{
-   vg_audio_assert_lock();
-   for( int id=1; id<=AUDIO_CHANNELS; id ++ )
-   {
-      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( (v4f){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;
-         state->decoder_handle.bird = NULL;
-         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 vg_audio_set_channel_clip( audio_channel_id id, audio_clip *clip )
-{
-   vg_audio_assert_lock();
-
-   audio_channel *channel = get_audio_channel( id );
-   VG_ASSERT( channel->stage == k_channel_stage_allocation );
-   VG_ASSERT( channel->clip == NULL );
-
-   channel->clip = clip;
-
-   u32 audio_format = channel->clip->flags & AUDIO_FLAG_FORMAT;
-   if( audio_format == k_audio_format_bird )
-      strcpy( channel->ui_name, "[array]" );
-   else if( audio_format == k_audio_format_gen )
-      strcpy( channel->ui_name, "[program]" );
-   else
-      vg_strncpy( clip->path, channel->ui_name, 32, k_strncpy_always_add_null );
-}
-
-void vg_audio_set_channel_group( audio_channel_id id, u16 group )
-{
-   vg_audio_assert_lock();
-
-   audio_channel *channel = get_audio_channel( id );
-   VG_ASSERT( channel->stage == k_channel_stage_allocation );
-   VG_ASSERT( channel->group == 0 );
-   channel->group = group;
-   if( group )
-      channel->ui_colour = (((u32)group * 29986577) & 0x00ffffff) | 0xff000000;
-}
-
-u32 vg_audio_count_channels_in_group( u16 group )
-{
-   vg_audio_assert_lock();
-
-   u32 count = 0;
-   for( int id=1; id<=AUDIO_CHANNELS; id ++ )
-   {
-      audio_channel *channel = get_audio_channel( id );
-      if( channel->stage != k_channel_stage_none )
-      {
-         if( channel->group == group )
-            count ++;
-      }
-   }
-   
-   return count;
-}
-
-audio_channel_id vg_audio_get_first_active_channel_in_group( u16 group )
-{
-   vg_audio_assert_lock();
-   for( int id=1; id<=AUDIO_CHANNELS; id ++ )
-   {
-      audio_channel *channel = get_audio_channel( id );
-      if( (channel->stage != k_channel_stage_none) && (channel->group == group) )
-         return id;
-   }
-   return 0;
-}
-
-void vg_audio_sidechain_lfo_to_channel( audio_channel_id id, audio_channel_id lfo_id, f32 amount )
-{
-   vg_audio_assert_lock();
-   
-   audio_lfo *lfo = get_audio_lfo( lfo_id );
-   VG_ASSERT( 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 vg_audio_add_channel_flags( audio_channel_id id, u32 flags )
-{
-   vg_audio_assert_lock();
-   struct audio_channel_controls *controls = get_audio_channel_controls( id );
-   controls->flags |= flags;
-}
-
-void vg_audio_set_channel_spacial_falloff( audio_channel_id id, v3f co, f32 range )
-{
-   vg_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;
-
-   vg_audio_add_channel_flags( id, AUDIO_FLAG_SPACIAL_3D );
-}
-
-void vg_audio_sync_ui_master_controls(void)
-{
-   vg_audio_assert_lock();
-   _vg_audio.controls.volume_target = ((f64)AUDIO_VOLUME_100) * _vg_audio.master_volume_ui;
-   _vg_audio.controls.dsp_enabled = _vg_audio.dsp_enabled_ui;
-}
-
-void vg_audio_set_channel_volume( audio_channel_id id, f64 volume, b8 instant )
-{
-   vg_audio_assert_lock();
-
-   struct audio_channel_controls *controls = get_audio_channel_controls( id );
-   controls->volume_target = ((f64)AUDIO_VOLUME_100) * volume;
-
-   if( instant )
-   {
-      audio_channel *channel = get_audio_channel( id );
-      VG_ASSERT( channel->stage == k_channel_stage_allocation );
-
-      struct audio_channel_state *state = get_audio_channel_state( id );
-      state->volume = controls->volume_target;
-   }
-}
-
-void vg_audio_set_channel_volume_slew_duration( audio_channel_id id, f64 length_seconds )
-{
-   vg_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 vg_audio_set_channel_pan( audio_channel_id id, f64 pan, b8 instant )
-{
-   vg_audio_assert_lock();
-
-   struct audio_channel_controls *controls = get_audio_channel_controls( id );
-   controls->pan_target = ((f64)AUDIO_PAN_RIGHT_100) * pan;
-
-   if( instant )
-   {
-      audio_channel *channel = get_audio_channel( id );
-      VG_ASSERT( channel->stage == k_channel_stage_allocation );
-
-      struct audio_channel_state *state = get_audio_channel_state( id );
-      state->pan = controls->pan_target;
-   }
-}
-
-void vg_audio_set_channel_pan_slew_duration( audio_channel_id id, f64 length_seconds )
-{
-   vg_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 vg_audio_set_channel_sampling_rate( audio_channel_id id, f32 rate )
-{
-   vg_audio_assert_lock();
-   struct audio_channel_controls *controls = get_audio_channel_controls( id );
-   controls->sampling_rate_multiplier = rate;
-}
-
-void vg_audio_start_channel( audio_channel_id id )
-{
-   vg_audio_assert_lock();
-
-   audio_channel *channel = get_audio_channel( id );
-   VG_ASSERT( channel->stage == k_channel_stage_allocation );
-   VG_ASSERT( channel->clip );
-   channel->stage = k_channel_stage_active;
-}
-
-audio_channel_id vg_audio_crossfade( audio_channel_id id, audio_clip *new_clip, f32 transition_seconds )
-{
-   vg_audio_assert_lock();
-
-   audio_channel *channel = get_audio_channel( id );
-   audio_channel_id new_id = 0;
-   if( new_clip )
-   {
-      new_id = vg_audio_get_first_idle_channel();
-      if( new_id )
-      {
-         vg_audio_set_channel_clip( new_id, new_clip );
-         vg_audio_set_channel_volume_slew_duration( new_id, transition_seconds );
-         vg_audio_set_channel_volume( new_id, 1.0, 0 );
-         vg_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 );
-
-         memcpy( new_controls, existing_controls, sizeof( struct audio_channel_controls ) );
-         vg_audio_start_channel( new_id );
-      }
-   }
-
-   vg_audio_set_channel_volume_slew_duration( id, transition_seconds );
-   vg_audio_set_channel_volume( id, 0.0, 0 );
-   vg_audio_add_channel_flags( id, AUDIO_FLAG_RELINQUISHED );
-
-   return new_id;
-}
-
-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 );
-
-   if( channel->clip == clip ) return 1;
-   else return 0;
-}
-
-void vg_audio_fadeout_flagged_audio( u32 flag, f32 length )
-{
-   vg_audio_assert_lock();
-   for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
-   {
-      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( _vg_audio.working )
-               vg_audio_crossfade( id, NULL, 1.0f );
-            else
-               channel->stage = k_channel_stage_none;
-         }
-      }
-   }
-}
-
-b8 vg_audio_flagged_stopped( u32 flag )
-{
-   vg_audio_lock();
-   for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
-   {
-      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 )
-         {
-            vg_audio_unlock();
-            return 0;
-         }
-      }
-   }
-   vg_audio_unlock();
-   return 1;
-}
-
-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, b8 pause )
-{
-   vg_audio_assert_lock();
-   for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
-   {
-      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 )
-            vg_audio_set_channel_pause( id, pause );
-      }
-   }
-}
-
-void vg_audio_oneshot_3d( audio_clip *clip, v3f co, f32 range, f32 volume, u16 group, u32 flags )
-{
-   vg_audio_assert_lock();
-   audio_channel_id id = vg_audio_get_first_idle_channel();
-
-   if( id )
-   {
-      vg_audio_set_channel_clip( id, clip );
-      vg_audio_set_channel_spacial_falloff( id, co, range );
-      vg_audio_set_channel_group( id, group );
-      vg_audio_set_channel_volume( id, volume, 1 );
-      vg_audio_add_channel_flags( id, AUDIO_FLAG_RELINQUISHED | flags );
-      vg_audio_start_channel( id );
-   }
-}
-
-void vg_audio_oneshot( audio_clip *clip, f32 volume, f32 pan, u16 group, u32 flags )
-{
-   vg_audio_assert_lock();
-   audio_channel_id id = vg_audio_get_first_idle_channel();
-
-   if( id )
-   {
-      vg_audio_set_channel_clip( id, clip );
-      vg_audio_set_channel_group( id, group );
-      vg_audio_set_channel_volume( id, volume, 1 );
-      vg_audio_set_channel_pan( id, pan, 1 );
-      vg_audio_add_channel_flags( id, AUDIO_FLAG_RELINQUISHED | flags );
-      vg_audio_start_channel( id );
-   }
-}
-
-
-
-/* lfos
- * ---------------------------------------------------------------------------------------- */
-
-audio_channel_id vg_audio_get_first_idle_lfo(void)
-{
-   vg_audio_assert_lock();
-
-   for( int id=1; id<=AUDIO_LFOS; id ++ )
-   {
-      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 vg_audio_set_lfo_polynomial_bipolar( audio_channel_id lfo_id, f32 coefficient )
-{
-   vg_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 vg_audio_set_lfo_frequency( audio_channel_id lfo_id, f32 freq )
-{
-   vg_audio_assert_lock();
-
-   struct audio_lfo_controls *controls = get_audio_lfo_controls( lfo_id );
-   u32 length = 44100.0f / freq;
-   controls->period_in_samples = length;
-
-   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 vg_audio_start_lfo( audio_channel_id lfo_id )
-{
-   vg_audio_assert_lock();
-   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 )
-{
-   _vg_profiler_enter_block( _vg_audio.profiler, "vg_audio:decode" );
-
-   u32 remaining = count;
-   u32 buffer_pos = 0;
-
-   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 = VG_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;
-            abort();
-         }
-      }
-      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 )
-         {
-            vg_warn( "Invalid samples read (%s)\n", 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 );
-      }
-      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 );
-         else if( format == k_audio_format_bird )
-            synth_bird_reset( state->decoder_handle.bird );
-
-         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 --;
-   }
-
-   _vg_profiler_exit_block( _vg_audio.profiler );
-}
-
-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 );
-
-   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;
-
-   i32 spacial_volume_target = 0,
-       spacial_pan_target = 0;
-
-   if( is_3d )
-   {
-      v3f delta;
-      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 || !vg_validf( dist ) )
-      {
-         spacial_pan_target = 0;
-         spacial_volume_target = 0;
-      }
-      else
-      {
-         f32 vol = vg_maxf( 0.0f, 1.0f - controls->spacial_falloff[3]*dist );
-         vol = powf( vol, 5.0f );
-         spacial_volume_target = (f64)vg_clampf( 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)vg_clampf( pan, -1.0f, 1.0f ) * (f64)AUDIO_PAN_RIGHT_100;
-
-         if( use_doppler )
-         {
-            const float vs = 323.0f;
-
-            f32 dv = v3_dot( delta, master_controls->listener_velocity );
-            f32 doppler = (vs+dv)/vs;
-                doppler = vg_clampf( 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 );
-
-   _vg_profiler_enter_block( _vg_audio.profiler, "vg_audio:mixing" );
-
-   /* TODO: Slew this */
-   f64 master_volume = (f64)_vg_audio.state.volume / (f64)AUDIO_VOLUME_100;
-
-   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 = vg_fractf( 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;
-   }
-
-   _vg_profiler_exit_block( _vg_audio.profiler );
-}
-
-
-static void _vg_audio_mixer( void *user, u8 *stream, int byte_count )
-{
-   _vg_profiler_tick( _vg_audio.profiler );
-
-   int sample_count = byte_count/(2*sizeof(f32));
-   
-   f32 *output_stereo = (f32 *)stream;
-   for( int i=0; i<sample_count*2; i ++ )
-      output_stereo[i] = 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 = &_vg_audio.state;
-
-   vg_audio_lock();
-   memcpy( &master_controls, &_vg_audio.controls, sizeof(struct audio_master_controls) );
-
-   for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
-   {
-      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;
-         memcpy( &channel_controls[ active_channel_count ], controls, sizeof( struct audio_channel_controls ) );
-         active_channel_count ++;
-      }
-   }
-   for( u32 id=1; id<=AUDIO_LFOS; id ++ )
-   {
-      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;
-         memcpy( local_controls, get_audio_lfo_controls(id), 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();
-   vg_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 )
-      {
-         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*)_vg_audio.decoding_buffer,
-               *loc = &buf[AUDIO_DECODE_SIZE*id];
-
-            stb_vorbis_alloc alloc = {
-               .alloc_buffer = (char *)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 )
-            {
-               vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", channel->clip->path, 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 )
-         {
-            u8 *buf = (u8*)_vg_audio.decoding_buffer;
-            struct synth_bird *loc = (void *)&buf[AUDIO_DECODE_SIZE*id];
-
-            memcpy( loc, channel->clip->any_data, 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;
-         }
-         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 )
-         {
-            _vg_profiler_enter_block( _vg_audio.profiler, "vg_audio:dsp/effects" );
-            for( int i=0; i<sample_count; i++ )
-               vg_dsp_process( output_stereo + i*2, output_stereo + i*2 );
-            _vg_profiler_exit_block( _vg_audio.profiler );
-         }
-      }
-      else break;
-   }
-
-   vg_audio_lock();
-   for( u32 i=0; i<active_channel_count; i ++ )
-   {
-      audio_channel_id id = active_channel_list[i];
-      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 )
-      {
-         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;
-
-         if( die )
-         {
-            channel->stage = k_channel_stage_none;
-         }
-      }
-   }
-
-   _vg_audio.samples_written_last_audio_frame = sample_count;
-   vg_audio_unlock();
-}
-
-/* 
- * Debugging
- */
-struct vg_audio_view_data
-{
-   i32 view_3d;
-};
-
-static f32 audio_volume_integer_to_float( i32 volume )
-{
-   return (f32)volume / (f32)AUDIO_VOLUME_100;
-}
-
-static void cb_vg_audio_view( ui_context *ctx, ui_rect rect, struct vg_magi_panel *magi )
-{
-   struct vg_audio_view_data *vd = magi->data;
-
-   ui_rect left, panel;
-   ui_split( rect, k_ui_axis_v, 256, 2, left, panel );
-   ui_checkbox( ctx, left, "3D labels", &vd->view_3d );
-
-   vg_audio_lock();
-   char perf[128];
-   ui_rect overlap_buffer[ AUDIO_CHANNELS ];
-   u32 overlap_length = 0;
-
-       /* Draw audio stack */
-       for( int id=1; id<=AUDIO_CHANNELS; id ++ )
-   {
-      audio_channel *channel = get_audio_channel( id );
-
-      ui_rect row;
-      ui_split( panel, k_ui_axis_h, 18, 1, row, panel );
-
-      b8 show_row = ui_clip( rect, row, row );
-
-      if( channel->stage == k_channel_stage_none )
-      {
-         if( show_row )
-            ui_fill( ctx, row, 0x50333333 );
-      }
-      else if( channel->stage == k_channel_stage_allocation )
-      {
-         if( show_row )
-            ui_fill( ctx, row, 0x50ff3333 );
-      }
-      else if( channel->stage == k_channel_stage_active )
-      {
-         if( show_row )
-         {
-            char buf[256];
-            vg_str str;
-            vg_strnull( &str, buf, sizeof(buf) );
-            vg_strcati64r( &str, id, 10, 2, ' ' );
-
-            if( channel->group )
-            {
-               vg_strcat( &str, " grp" );
-               vg_strcati64r( &str, channel->group, 10, 6, ' ' );
-            }
-            else
-               vg_strcat( &str, "          " );
-
-            vg_strcat( &str, " flags:" );
-            u32 flags = get_audio_channel_controls( id )->flags;
-            vg_strcatch( &str, (flags & AUDIO_FLAG_RELINQUISHED)? 'R': '_' );
-            vg_strcatch( &str, (flags & AUDIO_FLAG_SPACIAL_3D)? 'S': '_' );
-            vg_strcatch( &str, (flags & AUDIO_FLAG_WORLD)? 'W': '_' );
-            vg_strcatch( &str, (flags & AUDIO_FLAG_NO_DOPPLER)? '_':'D' );
-            vg_strcatch( &str, (flags & AUDIO_FLAG_NO_DSP)? '_':'E' );
-            vg_strcatch( &str, (flags & AUDIO_FLAG_LOOP)? 'L':'_' );
-
-            const char *formats[] =
-            {
-               "   mono   ",
-               "  stereo  ", 
-               "  vorbis  ",
-               "   none0  ",
-               "   none1  ",
-               "   none2  ",
-               "   none3  ",
-               "   none4  ",
-               "synth:bird",
-               "   none5  ",
-               "   none6  ",
-               "   none7  ",
-               "   none8  ",
-               "   none9  ",
-               "  none10  ",
-               "  none11  ",
-            };
-            u32 format_index = (channel->clip->flags & AUDIO_FLAG_FORMAT)>>9;
-            vg_strcat( &str, " format:" );
-            vg_strcat( &str, formats[format_index] );
-
-            const char *activties[] =
-            {
-               "wake ",
-               "play ",
-               "pause",
-               "end  ",
-               "error"
-            };
-            vg_strcat( &str, " " );
-            vg_strcat( &str, activties[channel->ui_activity] );
-
-            vg_strcat( &str, " " );
-            f32 volume = (f32)channel->ui_volume / (f32)AUDIO_VOLUME_100;
-            vg_strcati64r( &str, volume * 100.0f, 10, 3, ' ' );
-            vg_strcatch( &str, '%' );
-
-            vg_strcat( &str, " " );
-            vg_strcat( &str, channel->ui_name );
-
-            ui_rect row_l, row_r;
-            ui_split( row, k_ui_axis_v, 32, 2, row_l, row_r );
-            
-            ui_rect indicator_l, indicator_r;
-            ui_split_ratio( row_l, k_ui_axis_v, 0.5f, 1, indicator_l, indicator_r );
-
-            ui_fill( ctx, indicator_l, 0xff000000 );
-            if( volume > 0.01f )
-            {
-               f32 h = volume * (f32)indicator_l[3];
-               ui_rect vol_bar = { indicator_l[0], indicator_l[1] + indicator_l[3] - h, indicator_l[2], h };
-               ui_fill( ctx, vol_bar, 0xff00ff00 );
-
-               if( flags & AUDIO_FLAG_SPACIAL_3D )
-               {
-                  f32 h = ((f32)channel->ui_spacial_volume / (f32)AUDIO_VOLUME_100) * (f32)indicator_l[3];
-                  ui_rect space_bar = { indicator_l[0], indicator_l[1] + indicator_l[3] - h, indicator_l[2], 1 };
-                  ui_fill( ctx, space_bar, 0xffffffff );
-               }
-            }
-
-            f32 pan = (f32)channel->ui_pan / (f32)AUDIO_PAN_RIGHT_100;
-            ui_fill( ctx, indicator_r, 0xff111111 );
-            f32 midpoint = (f32)indicator_r[0] + (f32)indicator_r[2] * 0.5f;
-
-            f32 pan_abs = fabsf(pan);
-            if( pan_abs > 0.01f )
-            {
-               ui_rect bar = { midpoint,indicator_r[1], pan_abs * (f32)indicator_r[2] * 0.5f, indicator_r[3] };
-               
-               if( pan < 0.0f )
-                  bar[0] -= (f32)bar[2];
-
-               ui_fill( ctx, bar, 0xff00aaff );
-            }
-
-            if( flags & AUDIO_FLAG_SPACIAL_3D )
-            {
-               f32 w = ((f32)channel->ui_spacial_pan / (f32)AUDIO_PAN_RIGHT_100) * (f32)indicator_r[2] * 0.5f;
-               ui_rect space_bar = { midpoint+w, indicator_r[1], 1, indicator_r[3] };
-               ui_fill( ctx, space_bar, 0xffffffff );
-            }
-            
-            ui_fill( ctx, row_r, 0xa0000000 | channel->ui_colour );
-            ui_text( ctx, row_r, buf, 1, k_ui_align_middle_left, 0 );
-         }
-      }
-      
-#if 0
-#ifdef VG_3D
-      if( vd->view_3d && (ch->flags & AUDIO_FLAG_SPACIAL_3D) )
-      {
-         v4f wpos;
-         v3_copy( ch->spacial_falloff, wpos );
-
-         wpos[3] = 1.0f;
-         m4x4_mulv( vg.pv, wpos, wpos );
-
-         if( wpos[3] > 0.0f )
-         {
-            v2_muls( wpos, (1.0f/wpos[3]) * 0.5f, wpos );
-            v2_add( wpos, (v2f){ 0.5f, 0.5f }, wpos );
-            
-            ui_rect wr;
-            wr[0] = vg_clampf(wpos[0] * vg.window_x, -32000.0f,32000.0f);
-            wr[1] = vg_clampf((1.0f-wpos[1]) * vg.window_y,-32000.0f,32000.0f);
-            wr[2] = 1000;
-            wr[3] = 17;
-            
-            for( int j=0; j<12; j++ )
-            {
-               int collide = 0;
-               for( int k=0; k<overlap_length; k++ )
-               {
-                  ui_px *wk = overlap_buffer[k];
-                  if( ((wr[0] <= wk[0]+wk[2]) && (wr[0]+wr[2] >= wk[0])) &&
-                      ((wr[1] <= wk[1]+wk[3]) && (wr[1]+wr[3] >= wk[1])) )
-                  {
-                     collide = 1;
-                     break;
-                  }
-               }
-
-               if( !collide )
-                  break;
-               else
-                  wr[1] += 18;
-            }
-
-            ui_text( ctx, wr, perf, 1, k_ui_align_middle_left, 0 );
-            rect_copy( wr, overlap_buffer[ overlap_length ++ ] );
-         }
-      }
-#endif
-#endif
-       }
-
-   vg_audio_unlock();
-}
-
-static void cb_vg_audio_close( struct vg_magi_panel *me )
-{
-   vg_audio_lock();
-   _vg_audio.inspector_open = 0;
-   vg_audio_unlock();
-   free( me->data );
-}
-
-static int cmd_vg_audio( int argc, const char *argv[] )
-{
-   vg_audio_lock();
-   if( _vg_audio.inspector_open )
-   {
-      vg_error( "Only 1 audio inspector at a time.\n" );
-      vg_audio_unlock();
-      return 0;
-   }
-
-   _vg_audio.inspector_open = 1;
-   vg_audio_unlock();
-   ui_px w = 800, h=8*18;
-   struct vg_magi_panel *magi = _vg_magi_open( w,h, VG_MAGI_ALL );
-   magi->title = "Audio inspector";
-
-   struct vg_audio_view_data *vd = malloc(sizeof(struct vg_audio_view_data));
-   vd->view_3d = 0;
-
-   magi->data = vd;
-   magi->ui_cb = cb_vg_audio_view;
-   magi->close_cb = cb_vg_audio_close;
-   return 1;
-}
-
-VG_API void _vg_audio_register(void)
-{
-   vg_console_reg_cmd( "vg_audio", cmd_vg_audio, NULL );
-   vg_console_reg_var( "volume", &_vg_audio.master_volume_ui, k_var_dtype_f32, VG_VAR_PERSISTENT );
-   vg_console_reg_var( "vg_dsp", &_vg_audio.dsp_enabled_ui, k_var_dtype_i32, VG_VAR_PERSISTENT );
-   vg_console_reg_var( "vg_audio_device", &_vg_audio.device_choice, k_var_dtype_str, VG_VAR_PERSISTENT );
-}
-
-void vg_audio_device_init(void)
-{
-   SDL_AudioSpec spec_desired, spec_got;
-   spec_desired.callback = _vg_audio_mixer;
-   spec_desired.channels = 2;
-   spec_desired.format   = AUDIO_F32;
-   spec_desired.freq     = 44100;
-   spec_desired.padding  = 0;
-   spec_desired.samples  = AUDIO_FRAME_SIZE;
-   spec_desired.silence  = 0;
-   spec_desired.size     = 0;
-   spec_desired.userdata = NULL;
-
-   _vg_audio.sdl_output_device = SDL_OpenAudioDevice( _vg_audio.device_choice.buffer, 0, 
-                                                      &spec_desired, &spec_got, 0 );
-
-   vg_info( "Start audio device (%u, F32, %u) @%s\n", spec_desired.freq, AUDIO_FRAME_SIZE, 
-            _vg_audio.device_choice.buffer );
-
-   if( _vg_audio.sdl_output_device )
-   {
-      SDL_PauseAudioDevice( _vg_audio.sdl_output_device, 0 );
-      vg_success( "Unpaused device %d.\n", _vg_audio.sdl_output_device );
-      _vg_audio.working = 1;
-   }
-   else
-   {
-      _vg_audio.working = 0;
-      vg_error( 
-         "SDL_OpenAudioDevice failed. Your default audio device must support (or transcode from):\n"
-         "  Sample rate: 44100 hz\n"
-         "  Buffer size: 512 samples\n"
-         "  Channels: 2\n"
-         "  Format: s16 or f32\n" );
-   }
-}
-
-static void vg_audio_free(void)
-{
-   SDL_CloseAudioDevice( _vg_audio.sdl_output_device );
-   _vg_audio.sdl_output_device = 0;
-}
-
-VG_API void _vg_audio_init(void)
-{
-   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_SDL ) );
-
-   _vg_audio.profiler = _vg_profiler_create( "vg.audio", 1000.0f/20.0f );
-
-   /* fixed */
-   u32 decode_size = AUDIO_DECODE_SIZE * AUDIO_CHANNELS;
-   _vg_audio.decoding_buffer = vg_stack_allocate( NULL, decode_size, 8, "Decoding buffers" );
-
-   struct audio_master_controls *master_controls = &_vg_audio.controls;
-   master_controls->dsp_enabled = _vg_audio.dsp_enabled_ui;
-   master_controls->volume_target = (f64)_vg_audio.master_volume_ui * (f64)AUDIO_VOLUME_100;
-   v3_copy( (v3f){1,0,0}, master_controls->listener_right_ear_direction );
-   v3_zero( master_controls->listener_velocity );
-   v3_zero( master_controls->listener_position );
-   _vg_dsp_init();
-
-   _vg_audio.mutex = SDL_CreateMutex();
-   if( _vg_audio.mutex == NULL )
-      vg_fatal_error( "SDL2: %s\n", SDL_GetError() );
-   vg_audio_device_init();
-
-   _vg_add_exit_function( vg_audio_free );
-}
-
-void vg_audio_preupdate(void)
-{
-   b8 before_working = _vg_audio.working;
-   _vg_audio.working = 1;
-   if( _vg_audio.sdl_output_device == 0 )
-      _vg_audio.working = 0;
-   else
-      if( SDL_GetAudioDeviceStatus( _vg_audio.sdl_output_device ) == SDL_AUDIO_STOPPED )
-         _vg_audio.working = 0;
-}
diff --git a/source/engine/audio_mixer.c_UNUSED b/source/engine/audio_mixer.c_UNUSED
new file mode 100644 (file)
index 0000000..0209e62
--- /dev/null
@@ -0,0 +1,1455 @@
+struct vg_audio _vg_audio = 
+{ 
+   .master_volume_ui = 1.0f,
+   .dsp_enabled_ui = 1
+};
+
+_Thread_local static b8 _vg_audio_thread_has_lock = 0;
+
+void vg_audio_lock(void)
+{
+   SDL_LockMutex( _vg_audio.mutex );
+   _vg_audio_thread_has_lock = 1;
+}
+
+void vg_audio_unlock(void)
+{
+   _vg_audio_thread_has_lock = 0;
+   SDL_UnlockMutex( _vg_audio.mutex );
+}
+
+static void vg_audio_assert_lock(void)
+{
+   if( _vg_audio_thread_has_lock == 0 )
+   {
+      vg_error( "vg_audio function requires locking\n" );
+      abort();
+   }
+}
+
+/* clip loading from disk
+ * -------------------------------------------------------------------------------
+ */
+VG_TIER_2 b8 vg_audio_clip_load( audio_clip *clip, vg_stack_allocator *stack )
+{
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_BACKGROUND ) );
+
+   /* load in directly */
+   u32 format = clip->flags & AUDIO_FLAG_FORMAT;
+   if( format == k_audio_format_vorbis )
+   {
+      if( clip->path )
+      {
+         clip->any_data = vg_file_read( stack, clip->path, &clip->size, 0 );
+         if( clip->any_data )
+         {
+            float mb = (float)(clip->size) / (1024.0f*1024.0f);
+            vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb );
+            return 1;
+         }
+         else
+         {
+            vg_error( "Audio failed to load '%s'\n", clip->path );
+            return 0;
+         }
+      }
+      else
+      {
+         vg_error( "No path specified, embeded vorbis unsupported\n" );
+         return 0;
+      }
+   }
+   else if( format == k_audio_format_stereo )
+   {
+      vg_error( "Unsupported format (Stereo uncompressed)\n" );
+      return 0;
+   }
+   else if( format == k_audio_format_bird )
+   {
+      if( !clip->any_data )
+      {
+         vg_error( "No data, external birdsynth unsupported\n" );
+         return 0;
+      }
+
+      struct synth_bird *bird = vg_stack_allocate( stack, sizeof(struct synth_bird), 8, NULL );
+      bird->settings = clip->any_data;
+      clip->any_data = bird;
+      vg_info( "Loaded bird synthesis pattern (%u bytes)\n", clip->size );
+      return 1;
+   }
+   else
+   {
+      if( !clip->path )
+      {
+         vg_error( "No path specified, embeded mono unsupported\n" );
+         return 0;
+      }
+
+      u32 temp_frame = _vg_start_temp_frame();
+      stb_vorbis_alloc alloc = 
+      {
+         .alloc_buffer = vg_stack_allocate( _vg_temp_stack(), AUDIO_DECODE_SIZE, 8, "Vorbis alloc buffer (temp)" ),
+         .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE
+      };
+
+      u32 fsize;
+      void *filedata = vg_file_read( _vg_temp_stack(), clip->path, &fsize, 0 );
+      int err;
+      stb_vorbis *decoder = stb_vorbis_open_memory( filedata, fsize, &err, &alloc );
+      if( !decoder )
+      {
+         vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", clip->path, err );
+         _vg_end_temp_frame( temp_frame );
+         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 = vg_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 );
+      _vg_end_temp_frame( temp_frame );
+
+      if( read_samples == length_samples )
+         return 1;
+      else
+      {
+         vg_error( "Decode error, read_samples did not match length_samples\n" );
+         return 0;
+      }
+   }
+}
+
+VG_TIER_2 u32 vg_audio_clip_loadn( audio_clip *arr, u32 count, vg_stack_allocator *stack )
+{
+   u32 succeeded = 0;
+   for( u32 i=0; i<count; i++ )
+      succeeded += (u32)vg_audio_clip_load( &arr[i], stack );
+   return succeeded;
+}
+
+/* 
+ * -------------------------------------------------------------------------------
+ */
+
+static audio_channel *get_audio_channel( audio_channel_id id )
+{
+   VG_ASSERT( (id > 0) && (id <= AUDIO_CHANNELS) );
+   return &_vg_audio.channels[ id-1 ];
+}
+
+static struct audio_channel_controls *get_audio_channel_controls( audio_channel_id id )
+{
+   vg_audio_assert_lock();
+   VG_ASSERT( (id > 0) && (id <= AUDIO_CHANNELS) );
+   return &_vg_audio.channels[ id-1 ].controls;
+}
+
+static struct audio_channel_state *get_audio_channel_state( audio_channel_id id )
+{
+   VG_ASSERT( (id > 0) && (id <= AUDIO_CHANNELS) );
+   return &_vg_audio.channels[ id-1 ].state;
+}
+
+static audio_lfo *get_audio_lfo( audio_channel_id lfo_id )
+{
+   VG_ASSERT( (lfo_id > 0) && (lfo_id <= AUDIO_LFOS) );
+   return &_vg_audio.lfos[ lfo_id-1 ];
+}
+
+static struct audio_lfo_controls *get_audio_lfo_controls( audio_channel_id lfo_id )
+{
+   vg_audio_assert_lock();
+   VG_ASSERT( (lfo_id > 0) && (lfo_id <= AUDIO_LFOS) );
+   return &_vg_audio.lfos[ lfo_id-1 ].controls;
+}
+
+static struct audio_lfo_state *get_audio_lfo_state( audio_channel_id lfo_id )
+{
+   VG_ASSERT( (lfo_id > 0) && (lfo_id <= AUDIO_LFOS) );
+   return &_vg_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 vg_audio_get_first_idle_channel(void)
+{
+   vg_audio_assert_lock();
+   for( int id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      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( (v4f){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;
+         state->decoder_handle.bird = NULL;
+         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 vg_audio_set_channel_clip( audio_channel_id id, audio_clip *clip )
+{
+   vg_audio_assert_lock();
+
+   audio_channel *channel = get_audio_channel( id );
+   VG_ASSERT( channel->stage == k_channel_stage_allocation );
+   VG_ASSERT( channel->clip == NULL );
+
+   channel->clip = clip;
+
+   u32 audio_format = channel->clip->flags & AUDIO_FLAG_FORMAT;
+   if( audio_format == k_audio_format_bird )
+      strcpy( channel->ui_name, "[array]" );
+   else if( audio_format == k_audio_format_gen )
+      strcpy( channel->ui_name, "[program]" );
+   else
+      vg_strncpy( clip->path, channel->ui_name, 32, k_strncpy_always_add_null );
+}
+
+void vg_audio_set_channel_group( audio_channel_id id, u16 group )
+{
+   vg_audio_assert_lock();
+
+   audio_channel *channel = get_audio_channel( id );
+   VG_ASSERT( channel->stage == k_channel_stage_allocation );
+   VG_ASSERT( channel->group == 0 );
+   channel->group = group;
+   if( group )
+      channel->ui_colour = (((u32)group * 29986577) & 0x00ffffff) | 0xff000000;
+}
+
+u32 vg_audio_count_channels_in_group( u16 group )
+{
+   vg_audio_assert_lock();
+
+   u32 count = 0;
+   for( int id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      audio_channel *channel = get_audio_channel( id );
+      if( channel->stage != k_channel_stage_none )
+      {
+         if( channel->group == group )
+            count ++;
+      }
+   }
+   
+   return count;
+}
+
+audio_channel_id vg_audio_get_first_active_channel_in_group( u16 group )
+{
+   vg_audio_assert_lock();
+   for( int id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      audio_channel *channel = get_audio_channel( id );
+      if( (channel->stage != k_channel_stage_none) && (channel->group == group) )
+         return id;
+   }
+   return 0;
+}
+
+void vg_audio_sidechain_lfo_to_channel( audio_channel_id id, audio_channel_id lfo_id, f32 amount )
+{
+   vg_audio_assert_lock();
+   
+   audio_lfo *lfo = get_audio_lfo( lfo_id );
+   VG_ASSERT( 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 vg_audio_add_channel_flags( audio_channel_id id, u32 flags )
+{
+   vg_audio_assert_lock();
+   struct audio_channel_controls *controls = get_audio_channel_controls( id );
+   controls->flags |= flags;
+}
+
+void vg_audio_set_channel_spacial_falloff( audio_channel_id id, v3f co, f32 range )
+{
+   vg_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;
+
+   vg_audio_add_channel_flags( id, AUDIO_FLAG_SPACIAL_3D );
+}
+
+void vg_audio_sync_ui_master_controls(void)
+{
+   vg_audio_assert_lock();
+   _vg_audio.controls.volume_target = ((f64)AUDIO_VOLUME_100) * _vg_audio.master_volume_ui;
+   _vg_audio.controls.dsp_enabled = _vg_audio.dsp_enabled_ui;
+}
+
+void vg_audio_set_channel_volume( audio_channel_id id, f64 volume, b8 instant )
+{
+   vg_audio_assert_lock();
+
+   struct audio_channel_controls *controls = get_audio_channel_controls( id );
+   controls->volume_target = ((f64)AUDIO_VOLUME_100) * volume;
+
+   if( instant )
+   {
+      audio_channel *channel = get_audio_channel( id );
+      VG_ASSERT( channel->stage == k_channel_stage_allocation );
+
+      struct audio_channel_state *state = get_audio_channel_state( id );
+      state->volume = controls->volume_target;
+   }
+}
+
+void vg_audio_set_channel_volume_slew_duration( audio_channel_id id, f64 length_seconds )
+{
+   vg_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 vg_audio_set_channel_pan( audio_channel_id id, f64 pan, b8 instant )
+{
+   vg_audio_assert_lock();
+
+   struct audio_channel_controls *controls = get_audio_channel_controls( id );
+   controls->pan_target = ((f64)AUDIO_PAN_RIGHT_100) * pan;
+
+   if( instant )
+   {
+      audio_channel *channel = get_audio_channel( id );
+      VG_ASSERT( channel->stage == k_channel_stage_allocation );
+
+      struct audio_channel_state *state = get_audio_channel_state( id );
+      state->pan = controls->pan_target;
+   }
+}
+
+void vg_audio_set_channel_pan_slew_duration( audio_channel_id id, f64 length_seconds )
+{
+   vg_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 vg_audio_set_channel_sampling_rate( audio_channel_id id, f32 rate )
+{
+   vg_audio_assert_lock();
+   struct audio_channel_controls *controls = get_audio_channel_controls( id );
+   controls->sampling_rate_multiplier = rate;
+}
+
+void vg_audio_start_channel( audio_channel_id id )
+{
+   vg_audio_assert_lock();
+
+   audio_channel *channel = get_audio_channel( id );
+   VG_ASSERT( channel->stage == k_channel_stage_allocation );
+   VG_ASSERT( channel->clip );
+   channel->stage = k_channel_stage_active;
+}
+
+audio_channel_id vg_audio_crossfade( audio_channel_id id, audio_clip *new_clip, f32 transition_seconds )
+{
+   vg_audio_assert_lock();
+
+   audio_channel *channel = get_audio_channel( id );
+   audio_channel_id new_id = 0;
+   if( new_clip )
+   {
+      new_id = vg_audio_get_first_idle_channel();
+      if( new_id )
+      {
+         vg_audio_set_channel_clip( new_id, new_clip );
+         vg_audio_set_channel_volume_slew_duration( new_id, transition_seconds );
+         vg_audio_set_channel_volume( new_id, 1.0, 0 );
+         vg_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 );
+
+         memcpy( new_controls, existing_controls, sizeof( struct audio_channel_controls ) );
+         vg_audio_start_channel( new_id );
+      }
+   }
+
+   vg_audio_set_channel_volume_slew_duration( id, transition_seconds );
+   vg_audio_set_channel_volume( id, 0.0, 0 );
+   vg_audio_add_channel_flags( id, AUDIO_FLAG_RELINQUISHED );
+
+   return new_id;
+}
+
+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 );
+
+   if( channel->clip == clip ) return 1;
+   else return 0;
+}
+
+void vg_audio_fadeout_flagged_audio( u32 flag, f32 length )
+{
+   vg_audio_assert_lock();
+   for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      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( _vg_audio.working )
+               vg_audio_crossfade( id, NULL, 1.0f );
+            else
+               channel->stage = k_channel_stage_none;
+         }
+      }
+   }
+}
+
+b8 vg_audio_flagged_stopped( u32 flag )
+{
+   vg_audio_lock();
+   for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      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 )
+         {
+            vg_audio_unlock();
+            return 0;
+         }
+      }
+   }
+   vg_audio_unlock();
+   return 1;
+}
+
+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, b8 pause )
+{
+   vg_audio_assert_lock();
+   for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      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 )
+            vg_audio_set_channel_pause( id, pause );
+      }
+   }
+}
+
+void vg_audio_oneshot_3d( audio_clip *clip, v3f co, f32 range, f32 volume, u16 group, u32 flags )
+{
+   vg_audio_assert_lock();
+   audio_channel_id id = vg_audio_get_first_idle_channel();
+
+   if( id )
+   {
+      vg_audio_set_channel_clip( id, clip );
+      vg_audio_set_channel_spacial_falloff( id, co, range );
+      vg_audio_set_channel_group( id, group );
+      vg_audio_set_channel_volume( id, volume, 1 );
+      vg_audio_add_channel_flags( id, AUDIO_FLAG_RELINQUISHED | flags );
+      vg_audio_start_channel( id );
+   }
+}
+
+void vg_audio_oneshot( audio_clip *clip, f32 volume, f32 pan, u16 group, u32 flags )
+{
+   vg_audio_assert_lock();
+   audio_channel_id id = vg_audio_get_first_idle_channel();
+
+   if( id )
+   {
+      vg_audio_set_channel_clip( id, clip );
+      vg_audio_set_channel_group( id, group );
+      vg_audio_set_channel_volume( id, volume, 1 );
+      vg_audio_set_channel_pan( id, pan, 1 );
+      vg_audio_add_channel_flags( id, AUDIO_FLAG_RELINQUISHED | flags );
+      vg_audio_start_channel( id );
+   }
+}
+
+
+
+/* lfos
+ * ---------------------------------------------------------------------------------------- */
+
+audio_channel_id vg_audio_get_first_idle_lfo(void)
+{
+   vg_audio_assert_lock();
+
+   for( int id=1; id<=AUDIO_LFOS; id ++ )
+   {
+      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 vg_audio_set_lfo_polynomial_bipolar( audio_channel_id lfo_id, f32 coefficient )
+{
+   vg_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 vg_audio_set_lfo_frequency( audio_channel_id lfo_id, f32 freq )
+{
+   vg_audio_assert_lock();
+
+   struct audio_lfo_controls *controls = get_audio_lfo_controls( lfo_id );
+   u32 length = 44100.0f / freq;
+   controls->period_in_samples = length;
+
+   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 vg_audio_start_lfo( audio_channel_id lfo_id )
+{
+   vg_audio_assert_lock();
+   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 )
+{
+   _vg_profiler_enter_block( _vg_audio.profiler, "vg_audio:decode" );
+
+   u32 remaining = count;
+   u32 buffer_pos = 0;
+
+   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 = VG_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;
+            abort();
+         }
+      }
+      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 )
+         {
+            vg_warn( "Invalid samples read (%s)\n", 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 );
+      }
+      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 );
+         else if( format == k_audio_format_bird )
+            synth_bird_reset( state->decoder_handle.bird );
+
+         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 --;
+   }
+
+   _vg_profiler_exit_block( _vg_audio.profiler );
+}
+
+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 );
+
+   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;
+
+   i32 spacial_volume_target = 0,
+       spacial_pan_target = 0;
+
+   if( is_3d )
+   {
+      v3f delta;
+      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 || !vg_validf( dist ) )
+      {
+         spacial_pan_target = 0;
+         spacial_volume_target = 0;
+      }
+      else
+      {
+         f32 vol = vg_maxf( 0.0f, 1.0f - controls->spacial_falloff[3]*dist );
+         vol = powf( vol, 5.0f );
+         spacial_volume_target = (f64)vg_clampf( 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)vg_clampf( pan, -1.0f, 1.0f ) * (f64)AUDIO_PAN_RIGHT_100;
+
+         if( use_doppler )
+         {
+            const float vs = 323.0f;
+
+            f32 dv = v3_dot( delta, master_controls->listener_velocity );
+            f32 doppler = (vs+dv)/vs;
+                doppler = vg_clampf( 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 );
+
+   _vg_profiler_enter_block( _vg_audio.profiler, "vg_audio:mixing" );
+
+   /* TODO: Slew this */
+   f64 master_volume = (f64)_vg_audio.state.volume / (f64)AUDIO_VOLUME_100;
+
+   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 = vg_fractf( 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;
+   }
+
+   _vg_profiler_exit_block( _vg_audio.profiler );
+}
+
+
+static void _vg_audio_mixer( void *user, u8 *stream, int byte_count )
+{
+   _vg_profiler_tick( _vg_audio.profiler );
+
+   int sample_count = byte_count/(2*sizeof(f32));
+   
+   f32 *output_stereo = (f32 *)stream;
+   for( int i=0; i<sample_count*2; i ++ )
+      output_stereo[i] = 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 = &_vg_audio.state;
+
+   vg_audio_lock();
+   memcpy( &master_controls, &_vg_audio.controls, sizeof(struct audio_master_controls) );
+
+   for( u32 id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      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;
+         memcpy( &channel_controls[ active_channel_count ], controls, sizeof( struct audio_channel_controls ) );
+         active_channel_count ++;
+      }
+   }
+   for( u32 id=1; id<=AUDIO_LFOS; id ++ )
+   {
+      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;
+         memcpy( local_controls, get_audio_lfo_controls(id), 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();
+   vg_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 )
+      {
+         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*)_vg_audio.decoding_buffer,
+               *loc = &buf[AUDIO_DECODE_SIZE*id];
+
+            stb_vorbis_alloc alloc = {
+               .alloc_buffer = (char *)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 )
+            {
+               vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", channel->clip->path, 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 )
+         {
+            u8 *buf = (u8*)_vg_audio.decoding_buffer;
+            struct synth_bird *loc = (void *)&buf[AUDIO_DECODE_SIZE*id];
+
+            memcpy( loc, channel->clip->any_data, 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;
+         }
+         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 )
+         {
+            _vg_profiler_enter_block( _vg_audio.profiler, "vg_audio:dsp/effects" );
+            for( int i=0; i<sample_count; i++ )
+               vg_dsp_process( output_stereo + i*2, output_stereo + i*2 );
+            _vg_profiler_exit_block( _vg_audio.profiler );
+         }
+      }
+      else break;
+   }
+
+   vg_audio_lock();
+   for( u32 i=0; i<active_channel_count; i ++ )
+   {
+      audio_channel_id id = active_channel_list[i];
+      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 )
+      {
+         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;
+
+         if( die )
+         {
+            channel->stage = k_channel_stage_none;
+         }
+      }
+   }
+
+   _vg_audio.samples_written_last_audio_frame = sample_count;
+   vg_audio_unlock();
+}
+
+/* 
+ * Debugging
+ */
+struct vg_audio_view_data
+{
+   i32 view_3d;
+};
+
+static f32 audio_volume_integer_to_float( i32 volume )
+{
+   return (f32)volume / (f32)AUDIO_VOLUME_100;
+}
+
+static void cb_vg_audio_view( ui_context *ctx, ui_rect rect, struct vg_magi_panel *magi )
+{
+   struct vg_audio_view_data *vd = magi->data;
+
+   ui_rect left, panel;
+   ui_split( rect, k_ui_axis_v, 256, 2, left, panel );
+   ui_checkbox( ctx, left, "3D labels", &vd->view_3d );
+
+   vg_audio_lock();
+   char perf[128];
+   ui_rect overlap_buffer[ AUDIO_CHANNELS ];
+   u32 overlap_length = 0;
+
+       /* Draw audio stack */
+       for( int id=1; id<=AUDIO_CHANNELS; id ++ )
+   {
+      audio_channel *channel = get_audio_channel( id );
+
+      ui_rect row;
+      ui_split( panel, k_ui_axis_h, 18, 1, row, panel );
+
+      b8 show_row = ui_clip( rect, row, row );
+
+      if( channel->stage == k_channel_stage_none )
+      {
+         if( show_row )
+            ui_fill( ctx, row, 0x50333333 );
+      }
+      else if( channel->stage == k_channel_stage_allocation )
+      {
+         if( show_row )
+            ui_fill( ctx, row, 0x50ff3333 );
+      }
+      else if( channel->stage == k_channel_stage_active )
+      {
+         if( show_row )
+         {
+            char buf[256];
+            vg_str str;
+            vg_strnull( &str, buf, sizeof(buf) );
+            vg_strcati64r( &str, id, 10, 2, ' ' );
+
+            if( channel->group )
+            {
+               vg_strcat( &str, " grp" );
+               vg_strcati64r( &str, channel->group, 10, 6, ' ' );
+            }
+            else
+               vg_strcat( &str, "          " );
+
+            vg_strcat( &str, " flags:" );
+            u32 flags = get_audio_channel_controls( id )->flags;
+            vg_strcatch( &str, (flags & AUDIO_FLAG_RELINQUISHED)? 'R': '_' );
+            vg_strcatch( &str, (flags & AUDIO_FLAG_SPACIAL_3D)? 'S': '_' );
+            vg_strcatch( &str, (flags & AUDIO_FLAG_WORLD)? 'W': '_' );
+            vg_strcatch( &str, (flags & AUDIO_FLAG_NO_DOPPLER)? '_':'D' );
+            vg_strcatch( &str, (flags & AUDIO_FLAG_NO_DSP)? '_':'E' );
+            vg_strcatch( &str, (flags & AUDIO_FLAG_LOOP)? 'L':'_' );
+
+            const char *formats[] =
+            {
+               "   mono   ",
+               "  stereo  ", 
+               "  vorbis  ",
+               "   none0  ",
+               "   none1  ",
+               "   none2  ",
+               "   none3  ",
+               "   none4  ",
+               "synth:bird",
+               "   none5  ",
+               "   none6  ",
+               "   none7  ",
+               "   none8  ",
+               "   none9  ",
+               "  none10  ",
+               "  none11  ",
+            };
+            u32 format_index = (channel->clip->flags & AUDIO_FLAG_FORMAT)>>9;
+            vg_strcat( &str, " format:" );
+            vg_strcat( &str, formats[format_index] );
+
+            const char *activties[] =
+            {
+               "wake ",
+               "play ",
+               "pause",
+               "end  ",
+               "error"
+            };
+            vg_strcat( &str, " " );
+            vg_strcat( &str, activties[channel->ui_activity] );
+
+            vg_strcat( &str, " " );
+            f32 volume = (f32)channel->ui_volume / (f32)AUDIO_VOLUME_100;
+            vg_strcati64r( &str, volume * 100.0f, 10, 3, ' ' );
+            vg_strcatch( &str, '%' );
+
+            vg_strcat( &str, " " );
+            vg_strcat( &str, channel->ui_name );
+
+            ui_rect row_l, row_r;
+            ui_split( row, k_ui_axis_v, 32, 2, row_l, row_r );
+            
+            ui_rect indicator_l, indicator_r;
+            ui_split_ratio( row_l, k_ui_axis_v, 0.5f, 1, indicator_l, indicator_r );
+
+            ui_fill( ctx, indicator_l, 0xff000000 );
+            if( volume > 0.01f )
+            {
+               f32 h = volume * (f32)indicator_l[3];
+               ui_rect vol_bar = { indicator_l[0], indicator_l[1] + indicator_l[3] - h, indicator_l[2], h };
+               ui_fill( ctx, vol_bar, 0xff00ff00 );
+
+               if( flags & AUDIO_FLAG_SPACIAL_3D )
+               {
+                  f32 h = ((f32)channel->ui_spacial_volume / (f32)AUDIO_VOLUME_100) * (f32)indicator_l[3];
+                  ui_rect space_bar = { indicator_l[0], indicator_l[1] + indicator_l[3] - h, indicator_l[2], 1 };
+                  ui_fill( ctx, space_bar, 0xffffffff );
+               }
+            }
+
+            f32 pan = (f32)channel->ui_pan / (f32)AUDIO_PAN_RIGHT_100;
+            ui_fill( ctx, indicator_r, 0xff111111 );
+            f32 midpoint = (f32)indicator_r[0] + (f32)indicator_r[2] * 0.5f;
+
+            f32 pan_abs = fabsf(pan);
+            if( pan_abs > 0.01f )
+            {
+               ui_rect bar = { midpoint,indicator_r[1], pan_abs * (f32)indicator_r[2] * 0.5f, indicator_r[3] };
+               
+               if( pan < 0.0f )
+                  bar[0] -= (f32)bar[2];
+
+               ui_fill( ctx, bar, 0xff00aaff );
+            }
+
+            if( flags & AUDIO_FLAG_SPACIAL_3D )
+            {
+               f32 w = ((f32)channel->ui_spacial_pan / (f32)AUDIO_PAN_RIGHT_100) * (f32)indicator_r[2] * 0.5f;
+               ui_rect space_bar = { midpoint+w, indicator_r[1], 1, indicator_r[3] };
+               ui_fill( ctx, space_bar, 0xffffffff );
+            }
+            
+            ui_fill( ctx, row_r, 0xa0000000 | channel->ui_colour );
+            ui_text( ctx, row_r, buf, 1, k_ui_align_middle_left, 0 );
+         }
+      }
+      
+#if 0
+#ifdef VG_3D
+      if( vd->view_3d && (ch->flags & AUDIO_FLAG_SPACIAL_3D) )
+      {
+         v4f wpos;
+         v3_copy( ch->spacial_falloff, wpos );
+
+         wpos[3] = 1.0f;
+         m4x4_mulv( vg.pv, wpos, wpos );
+
+         if( wpos[3] > 0.0f )
+         {
+            v2_muls( wpos, (1.0f/wpos[3]) * 0.5f, wpos );
+            v2_add( wpos, (v2f){ 0.5f, 0.5f }, wpos );
+            
+            ui_rect wr;
+            wr[0] = vg_clampf(wpos[0] * vg.window_x, -32000.0f,32000.0f);
+            wr[1] = vg_clampf((1.0f-wpos[1]) * vg.window_y,-32000.0f,32000.0f);
+            wr[2] = 1000;
+            wr[3] = 17;
+            
+            for( int j=0; j<12; j++ )
+            {
+               int collide = 0;
+               for( int k=0; k<overlap_length; k++ )
+               {
+                  ui_px *wk = overlap_buffer[k];
+                  if( ((wr[0] <= wk[0]+wk[2]) && (wr[0]+wr[2] >= wk[0])) &&
+                      ((wr[1] <= wk[1]+wk[3]) && (wr[1]+wr[3] >= wk[1])) )
+                  {
+                     collide = 1;
+                     break;
+                  }
+               }
+
+               if( !collide )
+                  break;
+               else
+                  wr[1] += 18;
+            }
+
+            ui_text( ctx, wr, perf, 1, k_ui_align_middle_left, 0 );
+            rect_copy( wr, overlap_buffer[ overlap_length ++ ] );
+         }
+      }
+#endif
+#endif
+       }
+
+   vg_audio_unlock();
+}
+
+static void cb_vg_audio_close( struct vg_magi_panel *me )
+{
+   vg_audio_lock();
+   _vg_audio.inspector_open = 0;
+   vg_audio_unlock();
+   free( me->data );
+}
+
+static int cmd_vg_audio( int argc, const char *argv[] )
+{
+   vg_audio_lock();
+   if( _vg_audio.inspector_open )
+   {
+      vg_error( "Only 1 audio inspector at a time.\n" );
+      vg_audio_unlock();
+      return 0;
+   }
+
+   _vg_audio.inspector_open = 1;
+   vg_audio_unlock();
+   ui_px w = 800, h=8*18;
+   struct vg_magi_panel *magi = _vg_magi_open( w,h, VG_MAGI_ALL );
+   magi->title = "Audio inspector";
+
+   struct vg_audio_view_data *vd = malloc(sizeof(struct vg_audio_view_data));
+   vd->view_3d = 0;
+
+   magi->data = vd;
+   magi->ui_cb = cb_vg_audio_view;
+   magi->close_cb = cb_vg_audio_close;
+   return 1;
+}
+
+VG_API void _vg_audio_register(void)
+{
+   vg_console_reg_cmd( "vg_audio", cmd_vg_audio, NULL );
+   vg_console_reg_var( "volume", &_vg_audio.master_volume_ui, k_var_dtype_f32, VG_VAR_PERSISTENT );
+   vg_console_reg_var( "vg_dsp", &_vg_audio.dsp_enabled_ui, k_var_dtype_i32, VG_VAR_PERSISTENT );
+   vg_console_reg_var( "vg_audio_device", &_vg_audio.device_choice, k_var_dtype_str, VG_VAR_PERSISTENT );
+}
+
+void vg_audio_device_init(void)
+{
+   SDL_AudioSpec spec_desired, spec_got;
+   spec_desired.callback = _vg_audio_mixer;
+   spec_desired.channels = 2;
+   spec_desired.format   = AUDIO_F32;
+   spec_desired.freq     = 44100;
+   spec_desired.padding  = 0;
+   spec_desired.samples  = AUDIO_FRAME_SIZE;
+   spec_desired.silence  = 0;
+   spec_desired.size     = 0;
+   spec_desired.userdata = NULL;
+
+   _vg_audio.sdl_output_device = SDL_OpenAudioDevice( _vg_audio.device_choice.buffer, 0, 
+                                                      &spec_desired, &spec_got, 0 );
+
+   vg_info( "Start audio device (%u, F32, %u) @%s\n", spec_desired.freq, AUDIO_FRAME_SIZE, 
+            _vg_audio.device_choice.buffer );
+
+   if( _vg_audio.sdl_output_device )
+   {
+      SDL_PauseAudioDevice( _vg_audio.sdl_output_device, 0 );
+      vg_success( "Unpaused device %d.\n", _vg_audio.sdl_output_device );
+      _vg_audio.working = 1;
+   }
+   else
+   {
+      _vg_audio.working = 0;
+      vg_error( 
+         "SDL_OpenAudioDevice failed. Your default audio device must support (or transcode from):\n"
+         "  Sample rate: 44100 hz\n"
+         "  Buffer size: 512 samples\n"
+         "  Channels: 2\n"
+         "  Format: s16 or f32\n" );
+   }
+}
+
+static void vg_audio_free(void)
+{
+   SDL_CloseAudioDevice( _vg_audio.sdl_output_device );
+   _vg_audio.sdl_output_device = 0;
+}
+
+VG_API void _vg_audio_init(void)
+{
+   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_SDL ) );
+
+   _vg_audio.profiler = _vg_profiler_create( "vg.audio", 1000.0f/20.0f );
+
+   /* fixed */
+   u32 decode_size = AUDIO_DECODE_SIZE * AUDIO_CHANNELS;
+   _vg_audio.decoding_buffer = vg_stack_allocate( NULL, decode_size, 8, "Decoding buffers" );
+
+   struct audio_master_controls *master_controls = &_vg_audio.controls;
+   master_controls->dsp_enabled = _vg_audio.dsp_enabled_ui;
+   master_controls->volume_target = (f64)_vg_audio.master_volume_ui * (f64)AUDIO_VOLUME_100;
+   v3_copy( (v3f){1,0,0}, master_controls->listener_right_ear_direction );
+   v3_zero( master_controls->listener_velocity );
+   v3_zero( master_controls->listener_position );
+   _vg_dsp_init();
+
+   _vg_audio.mutex = SDL_CreateMutex();
+   if( _vg_audio.mutex == NULL )
+      vg_fatal_error( "SDL2: %s\n", SDL_GetError() );
+   vg_audio_device_init();
+
+   _vg_add_exit_function( vg_audio_free );
+}
+
+void vg_audio_preupdate(void)
+{
+   b8 before_working = _vg_audio.working;
+   _vg_audio.working = 1;
+   if( _vg_audio.sdl_output_device == 0 )
+      _vg_audio.working = 0;
+   else
+      if( SDL_GetAudioDeviceStatus( _vg_audio.sdl_output_device ) == SDL_AUDIO_STOPPED )
+         _vg_audio.working = 0;
+}
diff --git a/source/engine/console.c b/source/engine/console.c
deleted file mode 100644 (file)
index 735580f..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-#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;
-   b8 open;
-}
-_console;
-
-struct log_history_item
-{
-   u32 type;
-   c8 buffer[];
-};
-
-static c8 _input_buffer[ 1024 ];
-
-static void _log_listener( const c8 *line, u32 length, u32 type )
-{
-again:;
-   struct log_history_item *item = queue_alloc( &_console.log_history, sizeof( struct log_history_item ) + length+1 );
-   if( !item )
-   {
-      queue_pop( &_console.log_history );
-      goto again;
-   }
-
-   item->type = type;
-   buffer_copy( line, 0, item->buffer, length+1 );
-}
-
-void _engine_console_init(void)
-{
-   _log_add_listener( _log_listener, ($info | $ok | $warning | $error | $fatal | $shell), 0 );
-   queue_init( &_console.input_history, _heap_allocate( BYTES_KB(2) ), BYTES_KB(2) );
-   queue_init( &_console.log_history, _heap_allocate( BYTES_KB(4) ), BYTES_KB(4) );
-}
-
-void _engine_console_update(void)
-{
-   for( u8 j=0; j<_input_button_down( k_input_action_console, 0 ); j ++ )
-   {
-      _console.open ^= 0x1;
-      u32 console_mask = (1 << k_input_layer_console) | (1 << k_input_layer_ui);
-      _input_layer_whitelist( _console.open? console_mask: 0 );
-   }
-}
-
-void _engine_console_ui(void)
-{
-   if( !_console.open )
-      return;
-   
-   i16 kerning[3];
-   _font_get_kerning( kerning );
-   i16 history_box[4] = { 0, 0, _engine.w, kerning[1] * 32 };
-
-   if( _console.log_history.allocation_count )
-   {
-      u32 item_offset = _console.log_history.head_offset;
-      for( u32 i=0; i<32; i ++ )
-      {
-         struct log_history_item *item = queue_data( &_console.log_history, item_offset );
-         i16 line_rect[4] = (i16[]){ 0, kerning[1] * (31-i), _engine.w, kerning[1] };
-         _ui_text( line_rect, item->buffer, k_ui_align_x_left, (union colour){{128,128,128,255}} );
-
-         if( !queue_previous( &_console.log_history, item_offset, &item_offset ) )
-            break;
-      }
-   }
-
-   _graphics_line_rect( history_box, (union colour){{0,255,255,255}} );
-
-   enum textbox_action action = _ui_textbox( (i16[]){ 0, kerning[1]*32, _engine.w, kerning[1] }, 
-         _input_buffer, sizeof(_input_buffer), 1, UI_AUTOFOCUS );
-
-   if( action == k_textbox_enter )
-   {
-      _console_execute( _input_buffer, 0, 0 );
-      _input_buffer[0] = '\0';
-   }
-}
index 4c570b37913d26127de17012afa50007ee412b21..0561b7350b5163f366ee9a5bfbe152e01b776853 100644 (file)
@@ -23,55 +23,9 @@ 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
@@ -101,38 +55,9 @@ ccmd
 }
 
 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_console.c
 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
@@ -337,11 +262,6 @@ shader
    }
 }
 
-hook
-{
-   event START
-   function "_shader_init"
-}
 ccmd
 {
    name reload_shaders
@@ -349,25 +269,9 @@ ccmd
    description "Recompile shaders (DEVELOPER ONLY!)"
 }
 
-
-
 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 vg_lines_enable
@@ -400,18 +304,6 @@ shader
 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
-}
 cvar
 {
    name vg_audio
@@ -421,6 +313,253 @@ cvar
    description "Show audio info"
 }
 
-add model.c
-add metascene.c
-add array_file.c
+add vg_model.c
+add vg_entity.c
+add vg_metascene.c
+add vg_af.c
+
+entity
+{
+   name ent_spawn
+   id 2
+
+   parameter
+   {
+      name transform
+      type transform
+   }
+   parameter
+   {
+      name name
+      type pstr
+   }
+   parameter
+   {
+      name flags
+      type u32
+      flag
+      {
+         name locked
+         value 0x1
+      }
+      flag
+      {
+         name group1
+         value 0x2
+      }
+      flag
+      {
+         name group2
+         value 0x4
+      }
+      flag
+      {
+         name group3
+         value 0x8
+      }
+   }
+   function
+   {
+      name lantern
+   }
+   function
+   {
+      name boat
+   }
+   function
+   {
+      name me
+   }
+}
+entity
+{
+   name ent_water
+   id 5
+   function
+   {
+      name enable
+   }
+   function
+   {
+      name disable
+   }
+   function
+   {
+      name show
+   }
+}
+entity
+{
+   name ent_volume
+   id 6
+   function
+   {
+      name enable
+   }
+   function
+   {
+      name disable
+   }
+}
+entity
+{
+   name ent_audio
+   id 7
+}
+entity
+{
+   name ent_marker
+   id 8
+   function
+   {
+      name push
+   }
+   function
+   {
+      name tooltip
+   }
+   function
+   {
+      name tooltip_special
+   }
+}
+entity
+{
+   name ent_camera
+   id 13
+   function
+   {
+      name focus
+   }
+   function
+   {
+      name unfocus
+   }
+}
+entity
+{
+   name ent_ccmd
+   id 17
+   function
+   {
+      name exec
+   }
+}
+entity
+{
+   name ent_cubemap
+   id 21
+}
+entity
+{
+   name ent_prop
+   id 23
+}
+entity
+{
+   name ent_armature
+   id 28
+}
+entity
+{
+   name ent_atom
+   id 30
+   function
+   {
+      name pass_equal
+   }
+   function
+   {
+      name pass_greater
+   }
+   function
+   {
+      name set
+   }
+   function
+   {
+      name set_or
+   }
+   function
+   {
+      name set_and
+   }
+}
+entity
+{
+   name ent_cutscene
+   id 31
+   function
+   {
+      name play
+   }
+   function
+   {
+      name pause
+   }
+   function
+   {
+      name resume
+   }
+   function
+   {
+      name end
+   }
+}
+entity
+{
+   name ent_light
+   id 32
+
+   parameter
+   {
+      name transform
+      type transform
+   }
+   parameter
+   {
+      name flags
+      type u32
+      flag
+      {
+         name daytime
+         value 0x1
+      }
+      flag
+      {
+         name off
+         value 0x2
+      }
+   }
+   parameter
+   {
+      name type
+      type u32
+   }
+   parameter
+   {
+      name colour
+      type vec4
+      ui rgbe
+   }
+   parameter
+   {
+      name angle
+      type f32
+   }
+   parameter
+   {
+      name range
+      type f32
+   }
+
+   function
+   {
+      name on
+      argument
+      {
+         name enable
+         type i32
+      }
+   }
+}
diff --git a/source/engine/metascene.c b/source/engine/metascene.c
deleted file mode 100644 (file)
index f7328ac..0000000
+++ /dev/null
@@ -1,499 +0,0 @@
-#include "vg_engine.h"
-#include "metascene.h"
-#include "common_maths.h"
-#include "model.h"
-#include "model_entity.h"
-#include "shader_props.h"
-#include "array_file.h"
-#include "vg_async.h"
-
-void metascene_load( struct metascene *ms, const c8 *path, struct stack_allocator *stack )
-{
-   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
-   zero_buffer( ms, sizeof(struct metascene) );
-
-   struct stream stream;
-   ASSERT_CRITICAL( stream_open_file( &stream, path, k_stream_read ) );
-
-   u32 temp_frame = _start_temporary_frame();
-   {
-      struct array_file_context af;
-      ASSERT_CRITICAL( af_open_stream( &af, &stream, MS_VERSION_MIN, MS_VERSION_NR, _temporary_stack_allocator() ) );
-
-      struct array_file_ptr strings;
-      af_load_array( &af, &strings, "strings", stack, 1 );
-      ms->packed_strings = strings.data;
-      af_load_array( &af, &ms->infos, "ms_scene_info", stack, sizeof(struct ms_scene_info) );
-      af_load_array( &af, &ms->instances, "ms_instance", stack, sizeof(struct ms_instance) );
-      af_load_array( &af, &ms->overrides, "ms_override", stack, sizeof(struct ms_override) );
-      af_load_array( &af, &ms->strips, "ms_strip", stack, sizeof(struct ms_strip) );
-      af_load_array( &af, &ms->tracks, "ms_track", stack, sizeof(struct ms_track) );
-      af_load_array( &af, &ms->keyframes, "ms_keyframe", stack, sizeof(struct ms_keyframe) );
-      af_load_array( &af, &ms->cameras, "ent_camera", stack, sizeof(struct ent_camera) );
-      af_load_array( &af, &ms->audios, "ent_audio", stack, sizeof(struct ent_audio) );
-      af_load_array( &af, &ms->audio_clips, "ent_audio_clip", stack, sizeof(struct ent_audio_clip) );
-      af_load_array( &af, &ms->curves, "ms_curves", stack, sizeof(struct ms_curve_keyframe) );
-
-      ASSERT_CRITICAL( af_arrcount( &ms->infos ) );
-      struct ms_scene_info *src_inf = af_arritm( &ms->infos, 0 );
-      ms->info = *src_inf;
-
-      stream_close( &stream );
-   }
-   _end_temporary_frame( temp_frame );
-}
-
-u32 skeleton_bone_id( struct ms_skeleton *skele, const c8 *name )
-{
-   for( u32 i=1; i<skele->bone_count; i++ )
-      if( compare_buffers( skele->bones[i].name, 0, name, 0 ) )
-         return i;
-
-   $log( $fatal, {"skeleton_bone_id( *, "}, {name}, {" ); -> Bone does not exist"} );
-   _fatal_exit();
-   return 0;
-}
-
-void keyframe_copy_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, i32 num )
-{
-   for( i32 i=0; i<num; i++ )
-      kfb[i] = kfa[i];
-}
-
-
-/* apply a rotation from the perspective of root */
-void keyframe_rotate_around( struct ms_keyframe *kf, f32 origin[3], f32 offset[3], f32 q[4] )
-{
-   f32 v0[3], co[3];
-   v3_add( kf->co, offset, co );
-   v3_sub( co, origin, v0 );
-   q_mulv( q, v0, v0 );
-   v3_add( v0, origin, co );
-   v3_sub( co, offset, kf->co );
-   q_mul( q, kf->q, kf->q );
-   q_normalize( kf->q );
-}
-
-void keyframe_lerp( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd )
-{
-   v3_lerp( kfa->co, kfb->co, t, kfd->co );
-   q_nlerp( kfa->q,  kfb->q,  t, kfd->q );
-   v3_lerp( kfa->s,  kfb->s,  t, kfd->s );
-}
-
-/*
- * Lerp between two sets of keyframes and store in dest. Rotations use Nlerp.
- */
-void keyframe_lerp_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd, i32 count )
-{
-   if( t <= 0.0001f )
-   {
-      keyframe_copy_pose( kfa, kfd, count );
-      return;
-   }
-   else if( t >= 0.9999f )
-   {
-      keyframe_copy_pose( kfb, kfd, count );
-      return;
-   }
-
-   for( i32 i=0; i<count; i++ )
-      keyframe_lerp( kfa+i, kfb+i, t, kfd+i );
-}
-
-void skeleton_lerp_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd )
-{
-   keyframe_lerp_pose( kfa, kfb, t, kfd, skele->bone_count-1 );
-}
-
-void skeleton_copy_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfd )
-{
-   keyframe_copy_pose( kfa, kfd, skele->bone_count-1 );
-}
-
-/*
- * Sample animation between 2 closest frames using time value. Output is a
- * keyframe buffer that is allocated with an appropriate size
- *
- * Time is in SECONDS
- */
-void skeleton_sample_anim( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output )
-{
-   struct ms_strip *strip = anim->strip;
-   f32 animtime  = fmodf( time*anim->framerate, (f32)strip->strip.length ),
-       animframe = floorf( animtime ),
-       t = animtime - animframe;
-
-   u32 frame = (u32)animframe % strip->strip.length,
-       next  = (frame+1) % strip->strip.length;
-
-   struct ms_keyframe *base  = anim->keyframes_base + strip->strip.count*frame,
-                      *nbase = anim->keyframes_base + strip->strip.count*next;
-   skeleton_lerp_pose( skele, base, nbase, t, output );
-}
-
-/* time is in SECONDS */
-i32 skeleton_sample_anim_clamped( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output )
-{
-   struct ms_strip *strip = anim->strip;
-   f32 end = (strip->strip.length-1)/anim->framerate;
-   skeleton_sample_anim( skele, anim, f32_min( end, time ), output );
-   if( time > end ) return 0;
-   else             return 1;
-}
-
-static i32 should_apply_bone( struct ms_skeleton *skele, u32 id, enum anim_apply type )
-{
-   struct ms_skeleton_bone *sb = &skele->bones[ id ],
-                           *sp = &skele->bones[ sb->parent ];
-   if( type == k_anim_apply_defer_ik )
-   {
-      if( ((sp->flags & k_bone_flag_ik) && !(sb->flags & k_bone_flag_ik)) || sp->defer )
-      {
-         sb->defer = 1;
-         return 0;
-      }
-      else
-      {
-         sb->defer = 0;
-         return 1;
-      }
-   }
-   else if( type == k_anim_apply_deffered_only )
-   {
-      if( sb->defer )
-         return 1;
-      else
-         return 0;
-   }
-
-   return 1;
-}
-
-/*
- * Apply block of keyframes to skeletons final pose
- */
-void skeleton_apply_pose( struct ms_skeleton *skele, struct ms_keyframe *pose, enum anim_apply passtype, f32 final_mtx[][4][3] )
-{
-   if( passtype == k_anim_apply_absolute )
-   {
-      for( u32 i=1; i<skele->bone_count; i++ )
-      {
-         struct ms_keyframe *kf = &pose[i-1];
-         q_m3x3( kf->q, final_mtx[i] );
-         m3x3_scale( final_mtx[i], kf->s );
-         v3_copy( kf->co, final_mtx[i][3] );
-      }
-      return;
-   }
-
-   m4x3_identity( final_mtx[0] );
-   skele->bones[0].defer = 0;
-   skele->bones[0].flags &= ~k_bone_flag_ik;
-
-   for( u32 i=1; i<skele->bone_count; i++ )
-   {
-      struct ms_skeleton_bone *sb = &skele->bones[i];
-      if( !should_apply_bone( skele, i, passtype ) )
-         continue;
-
-      sb->defer = 0;
-
-      /* process pose */
-      f32 posemtx[4][3];
-      f32 temp_delta[3];
-      v3_sub( skele->bones[i].co, skele->bones[sb->parent].co, temp_delta );
-
-      /* pose matrix */
-      struct ms_keyframe *kf = &pose[i-1];
-      q_m3x3( kf->q, posemtx );
-      m3x3_scale( posemtx, kf->s );
-      v3_copy( kf->co, posemtx[3] );
-      v3_add( temp_delta, posemtx[3], posemtx[3] );
-
-      /* final matrix */
-      m4x3_mul( final_mtx[ sb->parent ], posemtx, final_mtx[i] );
-   }
-}
-
-/* 
- * Take the final matrices and decompose it into an absolute positioned anim
- */
-void skeleton_decompose_mtx_absolute( struct ms_skeleton *skele, struct ms_keyframe *anim, f32 final_mtx[][4][3] )
-{
-   for( u32 i=1; i<skele->bone_count; i++ )
-   {
-      struct ms_keyframe *kf = &anim[i-1];
-      m4x3_decompose( final_mtx[i], kf->co, kf->q, kf->s );
-   }
-}
-
-/* 
- * creates the reference inverse matrix for an IK bone, as it has an initial 
- * intrisic rotation based on the direction that the IK is setup..
- */
-void skeleton_inverse_for_ik( struct ms_skeleton *skele, f32 ivaxis[3], u32 id, f32 inverse[3][3] )
-{
-   v3_copy( ivaxis, inverse[0] );
-   v3_copy( skele->bones[id].end, inverse[1] );
-   v3_normalize( inverse[1] );
-   v3_cross( inverse[0], inverse[1], inverse[2] );
-   m3x3_transpose( inverse, inverse );
-}
-
-/*
- * Creates inverse rotation matrices which the IK system uses.
- */
-void skeleton_create_inverses( struct ms_skeleton *skele )
-{
-   /* IK: inverse 'plane-bone space' axis '(^axis,^bone,...)[base] */
-   for( u32 i=0; i<skele->ik_count; i++ )
-   {
-      struct ms_skeleton_ik *ik = &skele->ik[i];
-      f32 iv0[3], iv1[3], ivaxis[3];
-      v3_sub( skele->bones[ik->target].co, skele->bones[ik->lower].co, iv0 );
-      v3_sub( skele->bones[ik->pole].co,   skele->bones[ik->lower].co, iv1 );
-      v3_cross( iv0, iv1, ivaxis );
-      v3_normalize( ivaxis );
-
-      skeleton_inverse_for_ik( skele, ivaxis, ik->lower, ik->ia );
-      skeleton_inverse_for_ik( skele, ivaxis, ik->upper, ik->ib );
-   }
-}
-
-/*
- * Apply a model matrix to all bones, should be done last
- */
-void skeleton_apply_transform( struct ms_skeleton *skele, f32 transform[4][3], f32 final_mtx[][4][3] )
-{
-   for( u32 i=0; i<skele->bone_count; i++ )
-      m4x3_mul( transform, final_mtx[i], final_mtx[i] );
-}
-
-/*
- * Apply an inverse matrix to all bones which maps vertices from bind space into
- * bone relative positions
- */
-void skeleton_apply_inverses( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
-{
-   for( u32 i=0; i<skele->bone_count; i++ )
-   {
-      struct ms_skeleton_bone *sb = &skele->bones[i];
-      f32 inverse[4][3];
-      m3x3_identity( inverse );
-      v3_negate( sb->co, inverse[3] );
-      m4x3_mul( final_mtx[i], inverse, final_mtx[i] );
-   }
-}
-
-/*
- * Apply all IK modifiers (2 bone ik reference from blender is supported)
- */
-void skeleton_apply_ik_pass( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
-{
-   for( u32 i=0; i<skele->ik_count; i++ )
-   {
-      struct ms_skeleton_ik *ik = &skele->ik[i];
-      f32 v0[3], /* base -> target */
-          v1[3], /* base -> pole */
-          vaxis[3];
-      f32 co_base[3],
-          co_target[3],
-          co_pole[3];
-
-      v3_copy( final_mtx[ik->lower][3], co_base );
-      v3_copy( final_mtx[ik->target][3], co_target );
-      v3_copy( final_mtx[ik->pole][3], co_pole );
-
-      v3_sub( co_target, co_base, v0 );
-      v3_sub( co_pole, co_base, v1 );
-      v3_cross( v0, v1, vaxis );
-      v3_normalize( vaxis );
-      v3_normalize( v0 );
-      v3_cross( vaxis, v0, v1 );
-
-      /* localize problem into [x:v0,y:v1] 2d plane */
-      f32 base[2] = { v3_dot( v0, co_base   ), v3_dot( v1, co_base   ) },
-          end[2]  = { v3_dot( v0, co_target ), v3_dot( v1, co_target ) },
-          knee[2];
-
-      /* Compute angles (basic trig)*/
-      f32 delta[2];
-      v2_sub( end, base, delta );
-
-      f32 l1 = v3_length( skele->bones[ik->lower].end ),
-          l2 = v3_length( skele->bones[ik->upper].end ),
-          d = f32_clamp( v2_length(delta), fabsf(l1 - l2), l1+l2-0.00001f ),
-          c = acosf( (l1*l1 + d*d - l2*l2) / (2.0f*l1*d) ),
-          rot = atan2f( delta[1], delta[0] ) + c - VG_PIf/2.0f;
-
-      knee[0] = sinf(-rot) * l1;
-      knee[1] = cosf(-rot) * l1;
-
-      m4x3_identity( final_mtx[ik->lower] );
-      m4x3_identity( final_mtx[ik->upper] );
-
-      /* create rotation matrix */
-      f32 co_knee[3];
-      v3_muladds( co_base, v0, knee[0], co_knee );
-      v3_muladds( co_knee, v1, knee[1], co_knee );
-      // vg_line( co_base, co_knee, 0xff00ff00 );
-
-      f32 transform[4][3];
-      v3_copy( vaxis, transform[0] );
-      v3_muls( v0, knee[0], transform[1] );
-      v3_muladds( transform[1], v1, knee[1], transform[1] );
-      v3_normalize( transform[1] );
-      v3_cross( transform[0], transform[1], transform[2] );
-      v3_copy( co_base, transform[3] );
-
-      m3x3_mul( transform, ik->ia, transform );
-      m4x3_copy( transform, final_mtx[ik->lower] );
-
-      /* upper/knee bone */
-      v3_copy( vaxis, transform[0] );
-      v3_sub( co_target, co_knee, transform[1] );
-      v3_normalize( transform[1] );
-      v3_cross( transform[0], transform[1], transform[2] );
-      v3_copy( co_knee, transform[3] );
-
-      m3x3_mul( transform, ik->ib, transform );
-      m4x3_copy( transform, final_mtx[ik->upper] );
-   }
-}
-
-/*
- * Applies the typical operations that you want for an IK rig: 
- *    Pose, IK, Pose(deferred), Inverses, Transform
- */
-void skeleton_apply_standard( struct ms_skeleton *skele, struct ms_keyframe *pose, f32 transform[4][3], f32 final_mtx[][4][3] )
-{
-   skeleton_apply_pose( skele, pose, k_anim_apply_defer_ik, final_mtx );
-   skeleton_apply_ik_pass( skele, final_mtx );
-   skeleton_apply_pose( skele, pose, k_anim_apply_deffered_only, final_mtx );
-   skeleton_apply_inverses( skele, final_mtx );
-   skeleton_apply_transform( skele, transform, final_mtx );
-}
-
-void skeleton_alloc_from( struct ms_skeleton *skele, struct stack_allocator *stack, 
-                          struct vg_model *model, struct mdl_armature *armature )
-{
-   skele->bone_count     = armature->bone_count+1;
-   skele->ik_count       = 0;
-   skele->collider_count = 0;
-
-   for( u32 i=0; i<armature->bone_count; i++ )
-   {
-      struct mdl_bone *bone = &model->bones[ armature->bone_start+i ];
-      if( bone->flags & k_bone_flag_ik )
-         skele->ik_count ++;
-      if( bone->collider )
-         skele->collider_count ++;
-   }
-
-   u32 bone_size = sizeof(struct ms_skeleton_bone) * skele->bone_count,
-       ik_size   = sizeof(struct ms_skeleton_ik)   * skele->ik_count;
-
-   skele->bones = stack_allocate( stack, bone_size, 8, NULL );
-   skele->ik    = stack_allocate( stack, ik_size, 8, NULL );
-
-   zero_buffer( skele->bones, bone_size );
-   zero_buffer( skele->ik, ik_size );
-}
-
-/* Setup a skeleton from model. mdl's metadata should stick around */
-void skeleton_setup( struct ms_skeleton *skele, struct vg_model *model, u32 index, struct stack_allocator *stack )
-{
-   u32 ik_count = 0, collider_count = 0;
-   skele->bone_count = 0;
-   skele->bones = NULL;
-
-   if( !model->armature_count )
-   {
-      $log( $fatal, {"No skeleton in model"} );
-      _fatal_exit();
-   }
-
-   struct mdl_armature *armature = &model->armatures[ index ];
-   skeleton_alloc_from( skele, stack, model, armature );
-
-   for( u32 i=0; i<armature->bone_count; i++ )
-   {
-      struct mdl_bone *bone = &model->bones[ armature->bone_start+i ];
-      struct ms_skeleton_bone *sb = &skele->bones[i+1];
-
-      v3_copy( bone->co, sb->co );
-      v3_copy( bone->end, sb->end );
-
-      sb->parent = bone->parent;
-      sb->name   = af_str( model->packed_strings, bone->pstr_name );
-      sb->flags  = bone->flags;
-      sb->collider = bone->collider;
-      sb->orig_bone = bone;
-
-      if( sb->flags & k_bone_flag_ik )
-      {
-         skele->bones[ sb->parent ].flags |= k_bone_flag_ik;
-         
-         if( ik_count == skele->ik_count )
-         {
-            $log( $fatal, {"Too many ik bones, corrupt model file"} );
-            _fatal_exit();
-         }
-
-         struct ms_skeleton_ik *ik = &skele->ik[ ik_count ++ ];
-         ik->upper = i+1;
-         ik->lower = bone->parent;
-         ik->target = bone->ik_target;
-         ik->pole = bone->ik_pole;
-      }
-
-      box_copy( bone->hitbox, sb->hitbox );
-      if( bone->collider )
-      {
-         if( collider_count == skele->collider_count )
-         {
-            $log( $fatal, {"Too many collider bones"} );
-            _fatal_exit();
-         }
-         collider_count ++;
-      }
-   }
-
-   /* fill in implicit root bone */
-   v3_fill( skele->bones[0].co, 0 );
-   v3_copy( (f32[3]){0.0f,1.0f,0.0f}, skele->bones[0].end );
-   skele->bones[0].parent = 0xffffffff;
-   skele->bones[0].flags = 0;
-   skele->bones[0].name = "[root]";
-
-   skeleton_create_inverses( skele );
-   $log( $ok, {"Loaded skeleton with "}, $unsigned( skele->bone_count ), {" bones"} );
-   $log( $ok, {"                     "}, $unsigned( skele->collider_count), {" colliders"} );
-}
-
-#if 0
-void skeleton_debug( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
-{
-   for( u32 i=1; i<skele->bone_count; i ++ )
-   {
-      struct ms_skeleton_bone *sb = &skele->bones[i];
-      f32 p0[3], p1[3];
-      v3_copy( sb->co, p0 );
-      v3_add( p0, sb->end, p1 );
-
-      m4x3_mulv( final_mtx[i], p0, p0 );
-      m4x3_mulv( final_mtx[i], p1, p1 );
-
-      if( sb->flags & k_bone_flag_deform )
-      {
-         if( sb->flags & k_bone_flag_ik )
-            vg_line( p0, p1, 0xff0000ff );
-         else
-            vg_line( p0, p1, 0xffcccccc );
-      }
-      else
-         vg_line( p0, p1, 0xff00ffff );
-   }
-}
-#endif
diff --git a/source/engine/metascene.h b/source/engine/metascene.h
deleted file mode 100644 (file)
index 505d353..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-#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
-};
-
-void skeleton_apply_pose( struct ms_skeleton *skele, struct ms_keyframe *pose, enum anim_apply passtype, f32 final_mtx[][4][3] );
-void skeleton_decompose_mtx_absolute( struct ms_skeleton *skele, struct ms_keyframe *anim, f32 final_mtx[][4][3] );
-void skeleton_inverse_for_ik( struct ms_skeleton *skele, f32 ivaxis[3], u32 id, f32 inverse[3][3] );
-void skeleton_create_inverses( struct ms_skeleton *skele );
-void skeleton_apply_transform( struct ms_skeleton *skele, f32 transform[4][3], f32 final_mtx[][4][3] );
-void skeleton_apply_inverses( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
-void skeleton_apply_ik_pass( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
-void skeleton_apply_standard( struct ms_skeleton *skele, struct ms_keyframe *pose, f32 transform[4][3], f32 final_mtx[][4][3] );
-void skeleton_setup( struct ms_skeleton *skele, struct vg_model *model, u32 index, struct stack_allocator *stack );
-void skeleton_debug( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
diff --git a/source/engine/model.c b/source/engine/model.c
deleted file mode 100644 (file)
index c02999c..0000000
+++ /dev/null
@@ -1,562 +0,0 @@
-#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 ||
-             mat->shader == k_shader_pipe )
-         {
-            keyvalues_read_u32s( &kvs, root, "tex_diffuse", (u32[]){0}, &props->standard.tex_diffuse, 1 );
-            keyvalues_read_u32s( &kvs, root, "tex_normal", (u32[]){0}, &props->standard.tex_normal, 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_LINEAR | 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;
-}
-
-void vg_model_bind_texture( struct vg_model *model, GLuint target, u32 tex_id, u32 slot )
-{
-   if( tex_id )
-   {
-      union mdl_texture *mdl_tex = &model->textures[ tex_id -1 ];
-      vg_tex_bind( target, &mdl_tex->tex, slot );
-   }
-   else
-      vg_tex_bind( target, NULL, slot );
-}
diff --git a/source/engine/model.h b/source/engine/model.h
deleted file mode 100644 (file)
index f6b260c..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-#pragma once
-#include "vg_tex.h"
-#include "shader_props.h"
-#include "array_file.h"
-
-#define VG_MODEL_VERSION_MIN 110
-#define VG_MODEL_VERSION_NR 110
-
-enum mdl_shader
-{
-   k_shader_standard                = 0,
-   k_shader_standard_cutout         = 1,
-   k_shader_terrain_blend           = 2,
-   k_shader_standard_vertex_blend   = 3,
-   k_shader_water                   = 4,
-   k_shader_invisible               = 5,
-   k_shader_boundary                = 6,
-   k_shader_fxglow                  = 7,
-   k_shader_cubemap                 = 8,
-   k_shader_walking                 = 9,
-   k_shader_foliage                 = 10,
-   k_shader_workshop                = 11,
-   k_shader_pipe                    = 12,
-   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,
-
-   k_material_flag_spritesheet      = 0x1000
-};
-
-#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???
-                                                                   hgn: 25.10.2025 Yes it is. */
-       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_bind_texture( struct vg_model *model, GLuint target, u32 tex_id, u32 slot );
-void vg_model_draw_elements( u32 start, u32 count );
-void vg_model_draw_submesh( struct mdl_submesh *sm );
-i32  vg_model_get_mesh_index( struct vg_model *model, const c8 *name );
-i32  vg_model_get_submesh_index( struct vg_model *model, const c8 *mesh_name );
-void mdl_transform_m4x3( struct mdl_transform *transform, f32 mtx[4][3] );
diff --git a/source/engine/model_entity.h b/source/engine/model_entity.h
deleted file mode 100644 (file)
index 682bb66..0000000
+++ /dev/null
@@ -1,782 +0,0 @@
-#pragma once
-#include "model.h"
-
-enum entity_alias
-{
-   k_ent_none        = 0,
-   k_ent_gate        = 1,
-   k_ent_spawn       = 2,
-   k_ent_route_node  = 3,
-   k_ent_route       = 4,
-   k_ent_water       = 5,
-   k_ent_volume      = 6,
-   k_ent_audio       = 7,
-   k_ent_marker      = 8,
-   k_ent_font        = 9,
-   k_ent_font_variant= 10,
-   k_ent_traffic     = 11,
-   k_ent_skateshop   = 12,
-   k_ent_camera      = 13,
-   k_ent_swspreview  = 14,
-   k_ent_deleted1    = 15, //k_ent_menuitem    = 15,
-   k_ent_worldinfo   = 16,
-   k_ent_ccmd        = 17,
-   k_ent_objective   = 18,
-   k_ent_challenge   = 19,
-   k_ent_deleted0    = 20, //k_ent_relay       = 20,
-   k_ent_cubemap     = 21,
-   k_ent_miniworld   = 22,
-   k_ent_prop        = 23,
-   k_ent_list        = 24,
-   k_ent_region      = 25,
-   k_ent_glider      = 26,
-   k_ent_npc         = 27,
-   k_ent_armature    = 28,
-   k_ent_script      = 29, 
-   k_ent_atom        = 30,
-   k_ent_cutscene    = 31,
-   k_ent_light       = 32,
-   k_mdl_mesh        = 33,
-   k_editer_property = 34,
-   k_editer_item     = 35,
-
-   k_ent_flame       = 200, // FIXME: Move thiese into own file per-game.
-   k_ent_waterfall   = 201,
-   k_ent_rb2         = 202,
-   k_ent_heatpipe    = 203,
-   k_ent_waterlevel  = 204,
-   k_ent_viewstone   = 205,
-   k_ent_gauge       = 206,
-   k_ent_particle    = 207,
-   k_ent_deep_npc    = 208,
-   k_ent_max
-};
-
-// FIXME
-static 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",
-   [k_ent_max] = NULL
-};
-
-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
-};
-
-#define ENT_LIGHT_FLAG_DAYTIME 0x1
-#define ENT_LIGHT_FLAG_OFF     0x2
-
-struct ent_light
-{
-   struct mdl_transform transform;
-   u32 flags,
-       type;
-   f32 colour[4];
-   f32 angle,
-       range;
-
-   union
-   {
-      /* These are temporary, we could remove them from the file structure. */
-      struct
-      {
-         f32 inverse_world[4][3];
-         f32 angle_sin_cos[2];
-      };
-
-      struct
-      {
-         f32 lerp_start[3], lerp_end[3];
-         f32 lerp_duration, timer;
-      };
-   };
-};
-
-/* 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
-{
-   u32 blank1;
-   i32 blank;
-};
-
-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 filter;
-   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_void = 0x0,
-   k_ent_event_data_const_i32 = 0x1,
-   k_ent_event_data_const_f32 = 0x2,
-   k_ent_event_data_const_entity_id = 0x4,
-   k_ent_event_data_const_string = 0x8,
-   k_ent_event_data_data_alias = 0x10,
-   k_ent_event_data_v3f = 0x20
-};
-
-struct ent_event
-{
-   u32 pstr_source_event,
-       pstr_recieve_event,
-       source_entity_id,
-       recieve_entity_id,
-       flags;
-
-   f32 delay;
-   u32 unused0;
-
-   union
-   {
-      i32 const_i32;
-      f32 const_f32;
-      u32 const_entity_id;
-      u32 const_pstr;
-      u32 pstr_data_alias;
-   }
-   data;
-};
diff --git a/source/engine/profiler.c b/source/engine/profiler.c
deleted file mode 100644 (file)
index a03f85e..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-#define PROFILER_ROW_MAX 16
-#define PROFILER_STACK_MAX 8
-#define PROFILER_HISTORY_LENGTH 64
-
-struct vg_profiler
-{
-   const c8 *name;
-   f32 history_ms[ PROFILER_HISTORY_LENGTH ][ PROFILER_ROW_MAX ];
-   u32 buffer_current;
-
-   u64 row_accumulated[ PROFILER_ROW_MAX ];
-   const c8 *row_names[ PROFILER_ROW_MAX ];
-   u32 row_length;
-
-   u64 sample_stack[ PROFILER_STACK_MAX ];
-   u32 block_stack[ PROFILER_STACK_MAX ];
-   u32 stack_height;
-
-   u64 tick_last, segment_last;
-
-   f32 default_budget_ms;
-};
-
-struct
-{
-   vg_stack_allocator stack;
-   u32 count;
-   vg_mutex tick_lock;
-}
-_vg_profiler;
-
-VG_API void _vg_profiler_init(void)
-{
-   vg_stack_init( &_vg_profiler.stack, NULL, VG_MB(8), "Profilers" );
-   VG_ASSERT( VG_MUTEX_INIT( _vg_profiler.tick_lock ) );
-}
-
-VG_API u32 _vg_profiler_create( const c8 *name, f32 default_budget_ms )
-{
-   struct vg_profiler *profiler = vg_stack_allocate( &_vg_profiler.stack, sizeof(struct vg_profiler), 1, "Profiler" );
-   vg_zero_mem( profiler, sizeof(struct vg_profiler) );
-   profiler->name = name;
-   profiler->default_budget_ms = default_budget_ms;
-   _vg_profiler.count ++;
-   return vg_stack_offset( &_vg_profiler.stack, profiler );
-}
-
-VG_API_INTERNAL static struct vg_profiler *_vg_profiler_get( u32 id )
-{
-   return vg_stack_pointer( &_vg_profiler.stack, id );
-}
-
-VG_API void _vg_profiler_tick( u32 profiler_id )
-{
-   struct vg_profiler *profiler = _vg_profiler_get( profiler_id );
-
-   u64 new_tick = SDL_GetPerformanceCounter(),
-       duration = new_tick - profiler->tick_last;
-
-   f64 to_ms = 1000.0 / (f64)SDL_GetPerformanceFrequency();
-
-   VG_MUTEX_LOCK( _vg_profiler.tick_lock );
-   for( u32 i=0; i<PROFILER_ROW_MAX; i ++ )
-   {
-      profiler->history_ms[ profiler->buffer_current ][ i ] = (f32)((f64)profiler->row_accumulated[i] * to_ms);
-      profiler->row_accumulated[i] = 0;
-   }
-   profiler->buffer_current ++;
-   if( profiler->buffer_current >= PROFILER_HISTORY_LENGTH )
-      profiler->buffer_current = 0;
-   VG_MUTEX_UNLOCK( _vg_profiler.tick_lock );
-
-   profiler->tick_last = new_tick;
-}
-
-static u32 vg_profiler_block_id( struct vg_profiler *profiler, const c8 *block_name )
-{
-   for( u32 i=0; i<profiler->row_length; i ++ )
-      if( profiler->row_names[i] == block_name )
-         return i + 1;
-   if( profiler->row_length < PROFILER_ROW_MAX )
-   {
-      profiler->row_names[ profiler->row_length ++ ] = block_name;
-      return profiler->row_length;
-   }
-   else return 0;
-}
-
-VG_API void _vg_profiler_enter_block( u32 profiler_id, const c8 *block_name )
-{
-   u64 seg_end = SDL_GetPerformanceCounter();
-
-   struct vg_profiler *profiler = _vg_profiler_get( profiler_id );
-   VG_ASSERT( profiler->stack_height < PROFILER_STACK_MAX );
-
-   if( profiler->stack_height )
-   {
-      u32 lower_block = profiler->block_stack[ profiler->stack_height ];
-      if( lower_block )
-         profiler->row_accumulated[ lower_block-1 ] += (seg_end - profiler->segment_last);
-   }
-
-   u32 block_id = vg_profiler_block_id( profiler, block_name );
-   profiler->block_stack[ profiler->stack_height ] = block_id;
-   profiler->stack_height ++;
-   profiler->segment_last = seg_end;
-}
-
-VG_API void _vg_profiler_exit_block( u32 profiler_id )
-{
-   u64 seg_end = SDL_GetPerformanceCounter();
-
-   struct vg_profiler *profiler = _vg_profiler_get( profiler_id );
-   VG_ASSERT( profiler->stack_height );
-
-   profiler->stack_height --;
-   u32 block_id = profiler->block_stack[ profiler->stack_height ];
-
-   if( block_id )
-      profiler->row_accumulated[ block_id-1 ] += (seg_end - profiler->segment_last );
-
-   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, b8 normalize )
-{
-   struct vg_profiler *profiler = _vg_profiler_get( profiler_id );
-   if( panel[2] == 0 )
-      panel[2] = 256;
-   if( panel[3] == 0 )
-      panel[3] = PROFILER_HISTORY_LENGTH * 2;
-
-   f32 sh = (f32)panel[3^dir] / (f32)PROFILER_HISTORY_LENGTH,
-       sw = (f32)panel[2^dir];
-
-   ui_fill( ctx, panel, 0xa0000000 );
-
-   u32 colours[ PROFILER_ROW_MAX ];
-   for( u32 i=0; i<profiler->row_length; i ++ )
-      colours[i] = vg_strdjb2( profiler->row_names[i] ) | 0xff000000;
-
-   for( i32 i=0; i<PROFILER_HISTORY_LENGTH; i++ )
-   {
-      f32 total_ms = 0.0f;
-
-      for( u32 j=0; j<profiler->row_length; j ++ )
-      {
-         f32 sample_ms = profiler->history_ms[i][j],
-                 pos_x = (total_ms / budget_ms) * sw,
-                 width = (sample_ms / budget_ms) * sw;
-
-         ui_rect block;
-         block[0^dir] = panel[0^dir] + pos_x;
-         block[1^dir] = panel[1^dir] + (f32)i*sh;
-         block[2^dir] = VG_MAX( 1, width-1 );
-         block[3^dir] = ceilf(sh)-1;
-         ui_fill( ctx, block, colours[j] );
-         total_ms += sample_ms;
-      }
-   }
-
-#if 0
-   c8 infbuf[64];
-   snprintf( infbuf, 64, "accuracy: %.7fms", rate_mul );
-   ui_text( ctx, (ui_rect){ panel[0], panel[1], panel[2]-4, 24 }, infbuf, 1, k_ui_align_right, 0 );
-
-   for( int i=0; i<count; i++ )
-   {
-      const c8 *name = _vg_profiler_get( profilers[i] )->name;
-      snprintf( infbuf, 64, "%.4fms %s", avgs[i] * (1.0f/(VG_PROFILE_SAMPLE_COUNT-1)), name );
-      ui_text( ctx, (ui_rect){ panel[0], panel[1] + 24 + i*14, panel[2]-4, 14 }, infbuf, 1, k_ui_align_right, 0 );
-   }
-#endif
-}
-
-static void cb_vg_profiler( ui_context *ctx, ui_rect rect, struct vg_magi_panel *magi )
-{
-   struct vg_profiler *profiler = magi->data;
-   _vg_profiler_draw( ctx, vg_stack_offset( &_vg_profiler.stack, profiler ), profiler->default_budget_ms, rect, 0, 0 );
-}
-
-static int cmd_vg_profile( int argc, const char *argv[] )
-{
-   if( argc == 1 )
-   {
-      for( u32 i=0; i<_vg_profiler.count; i ++ )
-      {
-         struct vg_profiler *profiler = vg_stack_pointer( &_vg_profiler.stack, i*sizeof(struct vg_profiler) );
-         if( !strcmp( argv[0], profiler->name ) )
-         {
-            ui_px w = 256, h  = PROFILER_HISTORY_LENGTH * 2;
-            struct vg_magi_panel *magi = _vg_magi_open( w,h, VG_MAGI_MOVEABLE|VG_MAGI_PERSISTENT );
-            magi->title = "Profiler";
-            magi->data = profiler;
-            magi->ui_cb = cb_vg_profiler;
-            magi->close_cb = NULL;
-            return 1;
-         }
-      }
-   }
-   else
-      vg_error( "Usage: vg_profile <thread>\n" );
-
-   return 0;
-}
-
-static void cmd_vg_profile_poll( int argc, const c8 *argv[] )
-{
-   const c8 *term = argv[ argc-1 ];
-   if( argc == 1 )
-   {
-      for( u32 i=0; i<_vg_profiler.count; i ++ )
-      {
-         struct vg_profiler *profiler = vg_stack_pointer( &_vg_profiler.stack, i*sizeof(struct vg_profiler) );
-         console_suggest_score_text( profiler->name, term, 0 );
-      }
-   }
-}
-
-VG_API void _vg_profiler_register(void)
-{
-   vg_console_reg_cmd( "vg_profile", cmd_vg_profile, cmd_vg_profile_poll );
-}
diff --git a/source/engine/profiler.c_UNUSED b/source/engine/profiler.c_UNUSED
new file mode 100644 (file)
index 0000000..a03f85e
--- /dev/null
@@ -0,0 +1,223 @@
+#define PROFILER_ROW_MAX 16
+#define PROFILER_STACK_MAX 8
+#define PROFILER_HISTORY_LENGTH 64
+
+struct vg_profiler
+{
+   const c8 *name;
+   f32 history_ms[ PROFILER_HISTORY_LENGTH ][ PROFILER_ROW_MAX ];
+   u32 buffer_current;
+
+   u64 row_accumulated[ PROFILER_ROW_MAX ];
+   const c8 *row_names[ PROFILER_ROW_MAX ];
+   u32 row_length;
+
+   u64 sample_stack[ PROFILER_STACK_MAX ];
+   u32 block_stack[ PROFILER_STACK_MAX ];
+   u32 stack_height;
+
+   u64 tick_last, segment_last;
+
+   f32 default_budget_ms;
+};
+
+struct
+{
+   vg_stack_allocator stack;
+   u32 count;
+   vg_mutex tick_lock;
+}
+_vg_profiler;
+
+VG_API void _vg_profiler_init(void)
+{
+   vg_stack_init( &_vg_profiler.stack, NULL, VG_MB(8), "Profilers" );
+   VG_ASSERT( VG_MUTEX_INIT( _vg_profiler.tick_lock ) );
+}
+
+VG_API u32 _vg_profiler_create( const c8 *name, f32 default_budget_ms )
+{
+   struct vg_profiler *profiler = vg_stack_allocate( &_vg_profiler.stack, sizeof(struct vg_profiler), 1, "Profiler" );
+   vg_zero_mem( profiler, sizeof(struct vg_profiler) );
+   profiler->name = name;
+   profiler->default_budget_ms = default_budget_ms;
+   _vg_profiler.count ++;
+   return vg_stack_offset( &_vg_profiler.stack, profiler );
+}
+
+VG_API_INTERNAL static struct vg_profiler *_vg_profiler_get( u32 id )
+{
+   return vg_stack_pointer( &_vg_profiler.stack, id );
+}
+
+VG_API void _vg_profiler_tick( u32 profiler_id )
+{
+   struct vg_profiler *profiler = _vg_profiler_get( profiler_id );
+
+   u64 new_tick = SDL_GetPerformanceCounter(),
+       duration = new_tick - profiler->tick_last;
+
+   f64 to_ms = 1000.0 / (f64)SDL_GetPerformanceFrequency();
+
+   VG_MUTEX_LOCK( _vg_profiler.tick_lock );
+   for( u32 i=0; i<PROFILER_ROW_MAX; i ++ )
+   {
+      profiler->history_ms[ profiler->buffer_current ][ i ] = (f32)((f64)profiler->row_accumulated[i] * to_ms);
+      profiler->row_accumulated[i] = 0;
+   }
+   profiler->buffer_current ++;
+   if( profiler->buffer_current >= PROFILER_HISTORY_LENGTH )
+      profiler->buffer_current = 0;
+   VG_MUTEX_UNLOCK( _vg_profiler.tick_lock );
+
+   profiler->tick_last = new_tick;
+}
+
+static u32 vg_profiler_block_id( struct vg_profiler *profiler, const c8 *block_name )
+{
+   for( u32 i=0; i<profiler->row_length; i ++ )
+      if( profiler->row_names[i] == block_name )
+         return i + 1;
+   if( profiler->row_length < PROFILER_ROW_MAX )
+   {
+      profiler->row_names[ profiler->row_length ++ ] = block_name;
+      return profiler->row_length;
+   }
+   else return 0;
+}
+
+VG_API void _vg_profiler_enter_block( u32 profiler_id, const c8 *block_name )
+{
+   u64 seg_end = SDL_GetPerformanceCounter();
+
+   struct vg_profiler *profiler = _vg_profiler_get( profiler_id );
+   VG_ASSERT( profiler->stack_height < PROFILER_STACK_MAX );
+
+   if( profiler->stack_height )
+   {
+      u32 lower_block = profiler->block_stack[ profiler->stack_height ];
+      if( lower_block )
+         profiler->row_accumulated[ lower_block-1 ] += (seg_end - profiler->segment_last);
+   }
+
+   u32 block_id = vg_profiler_block_id( profiler, block_name );
+   profiler->block_stack[ profiler->stack_height ] = block_id;
+   profiler->stack_height ++;
+   profiler->segment_last = seg_end;
+}
+
+VG_API void _vg_profiler_exit_block( u32 profiler_id )
+{
+   u64 seg_end = SDL_GetPerformanceCounter();
+
+   struct vg_profiler *profiler = _vg_profiler_get( profiler_id );
+   VG_ASSERT( profiler->stack_height );
+
+   profiler->stack_height --;
+   u32 block_id = profiler->block_stack[ profiler->stack_height ];
+
+   if( block_id )
+      profiler->row_accumulated[ block_id-1 ] += (seg_end - profiler->segment_last );
+
+   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, b8 normalize )
+{
+   struct vg_profiler *profiler = _vg_profiler_get( profiler_id );
+   if( panel[2] == 0 )
+      panel[2] = 256;
+   if( panel[3] == 0 )
+      panel[3] = PROFILER_HISTORY_LENGTH * 2;
+
+   f32 sh = (f32)panel[3^dir] / (f32)PROFILER_HISTORY_LENGTH,
+       sw = (f32)panel[2^dir];
+
+   ui_fill( ctx, panel, 0xa0000000 );
+
+   u32 colours[ PROFILER_ROW_MAX ];
+   for( u32 i=0; i<profiler->row_length; i ++ )
+      colours[i] = vg_strdjb2( profiler->row_names[i] ) | 0xff000000;
+
+   for( i32 i=0; i<PROFILER_HISTORY_LENGTH; i++ )
+   {
+      f32 total_ms = 0.0f;
+
+      for( u32 j=0; j<profiler->row_length; j ++ )
+      {
+         f32 sample_ms = profiler->history_ms[i][j],
+                 pos_x = (total_ms / budget_ms) * sw,
+                 width = (sample_ms / budget_ms) * sw;
+
+         ui_rect block;
+         block[0^dir] = panel[0^dir] + pos_x;
+         block[1^dir] = panel[1^dir] + (f32)i*sh;
+         block[2^dir] = VG_MAX( 1, width-1 );
+         block[3^dir] = ceilf(sh)-1;
+         ui_fill( ctx, block, colours[j] );
+         total_ms += sample_ms;
+      }
+   }
+
+#if 0
+   c8 infbuf[64];
+   snprintf( infbuf, 64, "accuracy: %.7fms", rate_mul );
+   ui_text( ctx, (ui_rect){ panel[0], panel[1], panel[2]-4, 24 }, infbuf, 1, k_ui_align_right, 0 );
+
+   for( int i=0; i<count; i++ )
+   {
+      const c8 *name = _vg_profiler_get( profilers[i] )->name;
+      snprintf( infbuf, 64, "%.4fms %s", avgs[i] * (1.0f/(VG_PROFILE_SAMPLE_COUNT-1)), name );
+      ui_text( ctx, (ui_rect){ panel[0], panel[1] + 24 + i*14, panel[2]-4, 14 }, infbuf, 1, k_ui_align_right, 0 );
+   }
+#endif
+}
+
+static void cb_vg_profiler( ui_context *ctx, ui_rect rect, struct vg_magi_panel *magi )
+{
+   struct vg_profiler *profiler = magi->data;
+   _vg_profiler_draw( ctx, vg_stack_offset( &_vg_profiler.stack, profiler ), profiler->default_budget_ms, rect, 0, 0 );
+}
+
+static int cmd_vg_profile( int argc, const char *argv[] )
+{
+   if( argc == 1 )
+   {
+      for( u32 i=0; i<_vg_profiler.count; i ++ )
+      {
+         struct vg_profiler *profiler = vg_stack_pointer( &_vg_profiler.stack, i*sizeof(struct vg_profiler) );
+         if( !strcmp( argv[0], profiler->name ) )
+         {
+            ui_px w = 256, h  = PROFILER_HISTORY_LENGTH * 2;
+            struct vg_magi_panel *magi = _vg_magi_open( w,h, VG_MAGI_MOVEABLE|VG_MAGI_PERSISTENT );
+            magi->title = "Profiler";
+            magi->data = profiler;
+            magi->ui_cb = cb_vg_profiler;
+            magi->close_cb = NULL;
+            return 1;
+         }
+      }
+   }
+   else
+      vg_error( "Usage: vg_profile <thread>\n" );
+
+   return 0;
+}
+
+static void cmd_vg_profile_poll( int argc, const c8 *argv[] )
+{
+   const c8 *term = argv[ argc-1 ];
+   if( argc == 1 )
+   {
+      for( u32 i=0; i<_vg_profiler.count; i ++ )
+      {
+         struct vg_profiler *profiler = vg_stack_pointer( &_vg_profiler.stack, i*sizeof(struct vg_profiler) );
+         console_suggest_score_text( profiler->name, term, 0 );
+      }
+   }
+}
+
+VG_API void _vg_profiler_register(void)
+{
+   vg_console_reg_cmd( "vg_profile", cmd_vg_profile, cmd_vg_profile_poll );
+}
diff --git a/source/engine/steamworks.c_UNUSED b/source/engine/steamworks.c_UNUSED
deleted file mode 100644 (file)
index 549baf9..0000000
+++ /dev/null
@@ -1,364 +0,0 @@
-struct vg_steam_api _steam_api;
-
-static void cb_steam_warning( i32 severity, const c8 *pchMessage )
-{
-   if( severity == 0 )
-      vg_logsteam( "[message]" KBLK " '%s'\n", pchMessage );
-   else
-      vg_logsteam( "[message]" KYEL " '%s'\n", pchMessage );
-}
-
-#if defined( VG_ENGINE )
-static void cb_auth_ticket_recieved( void *result, void *context )
-{
-   EncryptedAppTicketResponse_t *response = result;
-
-   if( response->m_eResult == k_EResultOK )
-      vg_logsteam( "  New app ticket ready\n" );
-   else
-      vg_logsteam( KYEL "  Could not request new encrypted app ticket (%u)\n", response->m_eResult );
-   
-   if( SteamAPI_ISteamUser_GetEncryptedAppTicket( _steam_api.pSteamUser, _steam_api.app_symmetric_key,
-            VG_ARRAY_LEN(_steam_api.app_symmetric_key), &_steam_api.app_key_length ))
-   {
-      vg_logsteam( KGRN "  Loaded app ticket\n" );
-   }
-   else
-   {
-      vg_logsteam( KRED "  No ticket availible\n" );
-      _steam_api.app_key_length = 0;
-   }
-}
-#endif
-
-#if defined( VG_SERVER )
-static u8 vg_char_base16( c8 c )
-{
-   if( c >= '0' && c <= '9' )
-      return c-'0';
-   if( c >= 'a' && c <= 'f' )
-      return (c-'a') + 10;
-   return 0;
-}
-#endif
-
-#if defined( VG_SERVER )
-VG_API b8 _vg_steam_init( u32 unIP, u16 usGamePort, u16 usQueryPort, EServerMode eServerMode, 
-                            const c8 *pchVersionString, const c8 *appid_str )
-#else
-VG_API b8 _vg_steam_init(void)
-#endif
-{
-   if( _steam_api.disabled )
-      return 0;
-
-   SteamErrMsg err;
-
-   /* Steamworks init step
-    * ---------------------------------------------------------------------------- */
-#if defined( VG_ENGINE )
-   VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_STEAM ) );
-
-       const char *pszInternalCheckInterfaceVersions = 
-               STEAMUTILS_INTERFACE_VERSION "\0"
-               STEAMNETWORKINGUTILS_INTERFACE_VERSION "\0"
-               STEAMAPPS_INTERFACE_VERSION "\0"
-               STEAMCONTROLLER_INTERFACE_VERSION "\0"
-               STEAMFRIENDS_INTERFACE_VERSION "\0"
-               STEAMGAMESEARCH_INTERFACE_VERSION "\0"
-               STEAMHTMLSURFACE_INTERFACE_VERSION "\0"
-               STEAMHTTP_INTERFACE_VERSION "\0"
-               STEAMINPUT_INTERFACE_VERSION "\0"
-               STEAMINVENTORY_INTERFACE_VERSION "\0"
-               STEAMMATCHMAKINGSERVERS_INTERFACE_VERSION "\0"
-               STEAMMATCHMAKING_INTERFACE_VERSION "\0"
-               STEAMMUSICREMOTE_INTERFACE_VERSION "\0"
-               STEAMMUSIC_INTERFACE_VERSION "\0"
-               STEAMNETWORKINGMESSAGES_INTERFACE_VERSION "\0"
-               STEAMNETWORKINGSOCKETS_INTERFACE_VERSION "\0"
-               STEAMNETWORKING_INTERFACE_VERSION "\0"
-               STEAMPARENTALSETTINGS_INTERFACE_VERSION "\0"
-               STEAMPARTIES_INTERFACE_VERSION "\0"
-               STEAMREMOTEPLAY_INTERFACE_VERSION "\0"
-               STEAMREMOTESTORAGE_INTERFACE_VERSION "\0"
-               STEAMSCREENSHOTS_INTERFACE_VERSION "\0"
-               STEAMUGC_INTERFACE_VERSION "\0"
-               STEAMUSERSTATS_INTERFACE_VERSION "\0"
-               STEAMUSER_INTERFACE_VERSION "\0"
-               STEAMVIDEO_INTERFACE_VERSION "\0"
-
-               "\0";
-
-       if( SteamInternal_SteamAPI_Init( pszInternalCheckInterfaceVersions, &err ) != k_ESteamAPIInitResult_OK )
-   {
-      _steam_api.disabled = 1;
-      vg_logsteam( KRED "SteamInternal_SteamAPI_Init() failed: '%s'\nAll steam interactions disabled for this session\n", err );
-      return 0;
-   }
-#endif
-
-#if defined( VG_SERVER )
-   FILE *txt = fopen( "steam_appid.txt", "w" );
-   fputs( appid_str, txt );
-   fclose( txt );
-
-   // FIXME: Add no-auth launch option (hoist from skaterift, as we have it there, to vg steam module?)
-   vg_stack_allocator stack;
-   vg_stack_init( &stack, NULL, VG_KB(256), NULL );
-   u32 size;
-   c8 *src = vg_file_read( &stack, "application_key", &size, 0 );
-   if( src )
-   {
-      if( size < k_nSteamEncryptedAppTicketSymmetricKeyLen )
-      {
-         vg_error( "Application key was invalid size\n" );
-         return 0;
-      }
-      
-      for( int i=0; i<k_nSteamEncryptedAppTicketSymmetricKeyLen; i++ )
-         _steam_api.server_symmetric_key[i] = (vg_char_base16( src[i*2+0] ) << 4) | vg_char_base16( src[i*2+1] );
-   }
-   else
-   {
-      vg_error( "No application_key file\n" );
-      return 0;
-   }
-
-       const char *pszInternalCheckInterfaceVersions = 
-               STEAMUTILS_INTERFACE_VERSION "\0"
-               STEAMNETWORKINGUTILS_INTERFACE_VERSION "\0"
-
-               STEAMGAMESERVER_INTERFACE_VERSION "\0"
-               STEAMGAMESERVERSTATS_INTERFACE_VERSION "\0"
-               STEAMHTTP_INTERFACE_VERSION "\0"
-               STEAMINVENTORY_INTERFACE_VERSION "\0"
-               STEAMNETWORKING_INTERFACE_VERSION "\0"
-               STEAMNETWORKINGMESSAGES_INTERFACE_VERSION "\0"
-               STEAMNETWORKINGSOCKETS_INTERFACE_VERSION "\0"
-               STEAMUGC_INTERFACE_VERSION "\0"
-               "\0";
-       
-   ESteamAPIInitResult init_result = SteamInternal_GameServer_Init_V2( 
-         unIP, usGamePort, usQueryPort, eServerMode, pchVersionString, pszInternalCheckInterfaceVersions, &err );
-
-   if( init_result != k_ESteamAPIInitResult_OK )
-   {
-      _steam_api.disabled = 1;
-      vg_logsteam( KRED "SteamInternal_GameServer_Init_V2() failed: '%s'\n", err );
-      return 0;
-   }
-#endif
-
-   /* Manual dispatch step 
-    * ----------------------------------------------------------------------------- */
-   SteamAPI_ManualDispatch_Init();
-
-   /*
-    * Interface Init
-    * ----------------------------------------------------------------------------- */
-
-#if defined( VG_ENGINE )
-   _steam_api.hPipe = SteamAPI_GetHSteamPipe();
-   _steam_api.pSteamUtils = SteamAPI_SteamUtils_v010();
-   _steam_api.pSteamFriends = SteamAPI_SteamFriends_v018();
-   _steam_api.pSteamUGC = SteamAPI_SteamUGC_v021();
-   _steam_api.pSteamUser = SteamAPI_SteamUser_v023();
-   _steam_api.pSteamUserStats = SteamAPI_SteamUserStats_v013();
-   _steam_api.pSteamNetworkingSockets = SteamAPI_SteamNetworkingSockets_SteamAPI_v012();
-#endif
-
-#if defined( VG_SERVER )
-   _steam_api.hPipe = SteamGameServer_GetHSteamPipe();
-   _steam_api.pSteamGameServer = SteamAPI_SteamGameServer_v015();
-   _steam_api.pSteamUtils = SteamAPI_SteamGameServerUtils_v010();
-   _steam_api.pSteamUGC = SteamAPI_SteamGameServerUGC_v021();
-   _steam_api.pSteamNetworkingSockets = SteamAPI_SteamGameServerNetworkingSockets_SteamAPI_v012();
-#endif
-
-   _steam_api.pSteamNetworkingUtils = SteamAPI_SteamNetworkingUtils_SteamAPI_v004();
-
-   SteamAPI_ISteamUtils_SetWarningMessageHook( _steam_api.pSteamUtils, cb_steam_warning );
-
-#if defined( VG_SERVER )
-   SteamAPI_ISteamGameServer_LogOnAnonymous( _steam_api.pSteamGameServer );
-#endif
-
-
-#if defined( VG_ENGINE ) 
-   strcpy( _steam_api.username_at_startup, "[unassigned]" );
-   const c8 *username = SteamAPI_ISteamFriends_GetPersonaName( _steam_api.pSteamFriends );
-   str_utf8_collapse( username, _steam_api.username_at_startup, VG_ARRAY_LEN(_steam_api.username_at_startup) );
-
-# if defined( VG_MULTIPLAYER )
-   vg_logsteam( "Requesting new authorization ticket\n" );
-
-   vg_steam_api_call *call = vg_alloc_async_steam_api_call();
-   if( call )
-   {
-      call->userdata = NULL;
-      call->cb = cb_auth_ticket_recieved;
-      call->id = SteamAPI_ISteamUser_RequestEncryptedAppTicket( _steam_api.pSteamUser, NULL, 0 );
-   }
-# endif
-#endif
-
-   return 1;
-}
-
-static const c8 *string_ESteamNetworkingConnectionState( ESteamNetworkingConnectionState s )
-{
-   if( s == k_ESteamNetworkingConnectionState_None ) return "None";
-   if( s == k_ESteamNetworkingConnectionState_Connecting) return "Connecting"; 
-   if( s == k_ESteamNetworkingConnectionState_FindingRoute) return "Finding route"; 
-   if( s == k_ESteamNetworkingConnectionState_Connected) return "Connected"; 
-   if( s == k_ESteamNetworkingConnectionState_ClosedByPeer) return "Closed By Peer"; 
-   if( s == k_ESteamNetworkingConnectionState_ProblemDetectedLocally) return "Problem detected locally"; 
-   if( s == k_ESteamNetworkingConnectionState_FinWait) return "Finwait"; 
-   if( s == k_ESteamNetworkingConnectionState_Linger) return "Linger"; 
-   if( s == k_ESteamNetworkingConnectionState_Dead) return "Dead"; 
-   return "enum-out-of-range";
-}
-
-static const c8 *string_ESteamAPICallFailure( ESteamAPICallFailure e )
-{
-   if( e == k_ESteamAPICallFailureNone ) return "None";
-   if( e == k_ESteamAPICallFailureSteamGone ) return "Steam Gone";
-   if( e == k_ESteamAPICallFailureNetworkFailure ) return "Network Failure";
-   if( e == k_ESteamAPICallFailureInvalidHandle ) return KBLK "Invalid Handle";
-   if( e == k_ESteamAPICallFailureMismatchedCallback ) return "Mismatched Callback";
-   return "enum-out-of-range";
-}
-
-void vg_steam_frame(void)
-{
-   if( _steam_api.disabled )
-      return;
-
-   SteamAPI_ManualDispatch_RunFrame( _steam_api.hPipe );
-
-   CallbackMsg_t callback;
-   while( SteamAPI_ManualDispatch_GetNextCallback( _steam_api.hPipe, &callback ) )
-   {
-      /* Check for dispatching API call results */
-      i32 type = callback.m_iCallback;
-      if( type == k_iSteamUtils_SteamAPICallCompleted )
-      {
-         SteamAPICallCompleted_t *inf = callback.m_pubParam;
-
-         b8 bFailed;
-         void *call_data = alloca( inf->m_cubParam );
-         
-         if( SteamAPI_ManualDispatch_GetAPICallResult( _steam_api.hPipe, inf->m_hAsyncCall, 
-                                                       call_data, inf->m_cubParam, 
-                                                       inf->m_iCallback, &bFailed ) )
-         {
-            vg_logsteam( "api_call_completed %lu\n", inf->m_hAsyncCall );
-            
-            int j=0;
-            for( int i=0; i<_steam_api.api_call_count; i++ )
-            {
-               if( _steam_api.api_calls[i].id != inf->m_hAsyncCall )
-                  _steam_api.api_calls[j ++] = _steam_api.api_calls[i];
-               else
-                  _steam_api.api_calls[i].cb( call_data, _steam_api.api_calls[i].userdata );
-            }
-
-            if( _steam_api.api_call_count == j )
-               vg_error( "No tracker was register for API call\n" );
-
-            _steam_api.api_call_count = j;
-         } 
-         else 
-         { 
-            enum ESteamAPICallFailure e = 
-               SteamAPI_ISteamUtils_GetAPICallFailureReason( _steam_api.pSteamUtils, inf->m_hAsyncCall );
-            const c8 *fail_str = string_ESteamAPICallFailure( e );
-            vg_logsteam( KRED "Error getting call result on %lu (code %d)\n", inf->m_hAsyncCall, fail_str );
-         }
-      } 
-      else 
-      {
-         /* 
-          * Look at callback.m_iCallback to see what kind of callback it is,
-          * and dispatch to appropriate handler(s)
-          * void *data = callback.m_pubParam;
-          */
-         if( type == k_iSteamUser_SteamServersConnected )
-            vg_success( "Steam servers connected" );
-         else if( type == k_iSteamUser_SteamConnectFailure )
-         {
-            SteamServerConnectFailure_t *inf = callback.m_pubParam;
-            vg_logsteam( KRED "Steam server connect failure, code %d, retrying: %d\n", inf->m_eResult, inf->m_bStillRetrying );
-         }
-         else if( type == k_iSteamUser_SteamServersDisconnected )
-         {
-            SteamServersDisconnected_t *inf = callback.m_pubParam;
-            vg_logsteam( "Steam servers disconnect, code %d\n", inf->m_eResult );
-         }
-         else if( type == k_iSteamNetworkingSockets_SteamNetConnectionStatusChangedCallback )
-         {
-            SteamNetConnectionStatusChangedCallback_t *inf = callback.m_pubParam;
-            const c8 *status_string = string_ESteamNetworkingConnectionState( inf->m_info.m_eState );
-            vg_logsteam( "Connection status changed: %x -> %s\n   Debug: '%s'\n   EndDebug: '%s'\n", 
-                           inf->m_hConn, status_string, inf->m_info.m_szConnectionDescription,
-                           inf->m_info.m_szEndDebug );
-            
-            if( _steam_api.cb_connection_changed )
-               _steam_api.cb_connection_changed( inf );
-         }
-         else if( type == k_iSteamNetworkingSockets_SteamNetAuthenticationStatus )
-         {
-            SteamNetAuthenticationStatus_t *inf = callback.m_pubParam;
-            vg_logsteam( "Steam Authentication Status: %d\n   Debug: '%s'\n", inf->m_eAvail, inf->m_debugMsg );
-         }
-      }
-      
-      SteamAPI_ManualDispatch_FreeLastCallback( _steam_api.hPipe );
-   }
-}
-
-VG_API void _vg_steam_shutdown(void)
-{
-#if defined( VG_SERVER )
-   if( _steam_api.is_connected )
-   {
-      SteamAPI_ISteamGameServer_LogOff( _steam_api.pSteamGameServer );
-      _steam_api.is_connected = 0;
-   }
-   SteamGameServer_Shutdown();
-#else
-   SteamAPI_Shutdown();
-#endif
-}
-
-vg_steam_api_call *vg_alloc_async_steam_api_call(void)
-{
-   if( _steam_api.api_call_count == VG_ARRAY_LEN(_steam_api.api_calls) )
-      return NULL;
-   return &_steam_api.api_calls[ _steam_api.api_call_count ++ ];
-}
-
-#if defined( VG_ENGINE )
-void vg_steam_set_achievement( const c8 *name, b8 yes )
-{
-   if( _steam_api.disabled || _steam_api.demo_mode )
-      return;
-   
-   if( yes )
-   {
-      if( SteamAPI_ISteamUserStats_SetAchievement( _steam_api.pSteamUserStats, name ) )
-      {
-         vg_logsteam( KGRN "Set achievement '%s'\n", name ); 
-         SteamAPI_ISteamUserStats_StoreStats( _steam_api.pSteamUserStats );
-      }
-   }
-   else
-   {
-      if( SteamAPI_ISteamUserStats_ClearAchievement( _steam_api.pSteamUserStats, name ) )
-      {
-         vg_logsteam( KBLK "Clear achievement '%s'\n", name ); 
-         SteamAPI_ISteamUserStats_StoreStats( _steam_api.pSteamUserStats );
-      }
-   }
-}
-#endif
diff --git a/source/engine/ui.c_UNUSED b/source/engine/ui.c_UNUSED
deleted file mode 100644 (file)
index 14ca3cc..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-#include "foundation.h"
-#include "vg_engine.h"
-#include "vg_graphics.h"
-#include "vg_render.h"
-#include "vg_opengl.h"
-#include "vg_shader.h"
-
-struct graphics_target _ui_surface = 
-{
-   .mode = k_graphics_mode_software,
-   .size = { 1920, 1080 },
-};
-
-static GLuint _ui_surface_texture;
-
-#include <unistd.h>
-void _engine_ui_init(void)
-{
-   _ui_surface.buffer = _heap_allocate( 1920*1080*4 );
-
-   glGenTextures( 1, &_ui_surface_texture );
-   glActiveTexture( GL_TEXTURE0 );
-   glBindTexture( GL_TEXTURE_2D, _ui_surface_texture );
-   glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
-   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
-}
-
-void _engine_ui_pre_render(void)
-{
-   ASSERT_CRITICAL( _engine.w <= 1920 );
-   ASSERT_CRITICAL( _engine.h <= 1080 );
-   
-   f64 x = 0, y = 0;
-#if 0
-   glfwGetCursorPos( _engine.window_handle, &x, &y );
-#endif
-   _ui_set_mouse( x, y );
-   _ui_update();
-
-   _graphics_set_target( &_ui_surface );
-   _graphics_viewport( 0, 0, _engine.w, _engine.h );
-   //_graphics_fill_rect( (i16[]){ 0, 0, _engine.w, _engine.h }, (union colour){{0,0,0,255}} );
-}
-
-void _engine_ui_input_character( u32 codepoint )
-{
-   if( (codepoint >= (u32)' ') && (codepoint <= (u32)'~') )
-      _ui_input_text( (const c8[]){ codepoint, 0 } );
-}
-
-void _engine_ui_post_render(void)
-{
-   glEnable( GL_BLEND );
-   glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
-   glBlendEquation( GL_FUNC_ADD );
-
-   glActiveTexture( GL_TEXTURE0 );
-   glBindTexture( GL_TEXTURE_2D, _ui_surface_texture );
-   glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 1920, 1080, GL_BGRA, GL_UNSIGNED_BYTE, _ui_surface.buffer );
-
-   _shader_bind( k_shader_blit );
-   _shader_blit_uTexMain( 0 );
-   _shader_blit_uFlip( 1 );
-   _shader_blit_uInverseRatio( (f32[2]){ (f64)_engine.w/1920.0, (f64)_engine.h/1080.0 } );
-
-   _render_fullscreen_quad();
-   glDisable( GL_BLEND );
-}
diff --git a/source/engine/vg_af.c b/source/engine/vg_af.c
new file mode 100644 (file)
index 0000000..b1b73fb
--- /dev/null
@@ -0,0 +1,356 @@
+#include "foundation.h"
+#include "vg_af.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 defined( __EMSCRIPTEN__ )
+   if( arr->item_count )
+   {
+      stream_seek( ctx->stream, arr->file_offset );
+      if( stride != arr->item_size )
+      {
+         zero_buffer( buffer, stride*arr->item_count );
+         u32 temp_frame = _start_temporary_frame();
+         {
+            u8 *temp = _temporary_allocate( arr->item_count * arr->item_size, 8 );
+            stream_read( ctx->stream, temp, arr->item_count * arr->item_size );
+
+            u32 read_size = u32_min( stride, arr->item_size );
+            for( u32 i=0; i<arr->item_count; i++ )
+               buffer_copy( temp + i*arr->item_size, read_size, buffer+i*stride, read_size );
+         }
+         _end_temporary_frame( temp_frame );
+      }
+      else
+         stream_read( ctx->stream, buffer, arr->item_count * arr->item_size );
+   }
+#else
+   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 );
+      }
+   }
+#endif
+}
+
+void af_load_array_file( struct array_file_context *ctx, struct array_file_ptr *out_ptr, 
+                         struct array_file_meta *arr, struct stack_allocator *stack, u32 stride )
+{
+   if( arr->item_count )
+   {
+      u32 size = stride*arr->item_count;
+      out_ptr->data = stack_allocate( stack, size, 8, NULL );
+      af_load_array_file_buffer( ctx, arr, out_ptr->data, stride );
+   }
+   else
+      out_ptr->data = NULL;
+   
+   out_ptr->stride = stride;
+   out_ptr->count = arr->item_count;
+}
+
+void *af_arritm( struct array_file_ptr *arr, u32 index )
+{
+   if( index >= arr->count )
+   {
+      $log( $fatal, {"Index out of range"}, $unsigned(index), {" >= "}, $unsigned( arr->count ) );
+      _fatal_exit();
+   }
+
+   return ((u8 *)arr->data) + index*arr->stride;
+}
+
+u32 af_arrcount( struct array_file_ptr *arr )
+{
+   return arr->count;
+}
+
+struct array_file_meta *af_find_array( struct array_file_context *ctx, const c8 *name )
+{
+   for( u32 i=0; i<af_arrcount(&ctx->index); i++ )
+   {
+      struct array_file_meta *arr = af_arritm( &ctx->index, i );
+      if( compare_buffers( arr->name, 0, name, 0 ) )
+         return arr;
+   }
+
+   return NULL;
+}
+
+b8 af_load_array( struct array_file_context *ctx, struct array_file_ptr *ptr, const c8 *name, 
+                   struct stack_allocator *stack, u32 stride )
+{
+   struct array_file_meta *arr = af_find_array( ctx, name );
+
+   if( arr )
+   {
+      af_load_array_file( ctx, ptr, arr, stack, stride );
+      return 1;
+   }
+   else
+   {
+      ptr->data = NULL;
+      ptr->count = 0;
+      ptr->stride = 0;
+      return 0;
+   }
+}
+
+b8 af_open_stream( struct array_file_context *afc, struct stream *stream, u32 min_version, u32 max_version, 
+                     struct stack_allocator *stack )
+{
+   afc->stream = stream;
+   if( stream_read( stream, &afc->header, sizeof(struct array_file_header)) != sizeof(struct array_file_header) )
+   {
+      $log( $error, {"Array file not large enough to contain header."} );
+      return 0;
+   }
+
+   if( (afc->header.version < min_version) || (afc->header.version > max_version) )
+   {
+      $log( $error, {"Array file version is out of range ("}, $unsigned( afc->header.version ),
+                    {"\nAccepted: "}, $unsigned( min_version ), {" -> "}, $unsigned( max_version ) );
+      return 0;
+   }
+
+   af_load_array_file( afc, &afc->index, &afc->header.index, stack, sizeof(struct array_file_meta) );
+   return 1;
+}
+
+/* compiler
+ * ---------------------------------------------------------------------- */
+struct af_compiler_iter
+{
+   u32 i, j;
+   struct af_compiler_index *index;
+   struct af_compiler_item *current_item;
+   void *data;
+};
+
+static void af_init_iterator( struct af_compiler_iter *iter, struct af_compiler_index *index )
+{
+   iter->i = 0;
+   iter->j = 0;
+   iter->index = index;
+   iter->current_item = NULL;
+   iter->data = NULL;
+}
+
+static b8 af_next( struct af_compiler_iter *iter )
+{
+   if( iter->i == 0 )
+   {
+      if( iter->index->first == NULL )
+         return 0;
+      iter->current_item = iter->index->first;
+   }
+
+   if( iter->j >= iter->current_item->count )
+   {
+      if( iter->current_item->next == NULL )
+         return 0;
+
+      iter->current_item = iter->current_item->next;
+      iter->j = 0;
+   }
+
+   iter->data = iter->current_item->data + (iter->j * iter->index->element_size);
+   iter->j ++;
+   iter->i ++;
+   return 1;
+}
+
+struct af_compiler_item *af_compiler_allocate_items( struct af_compiler *compiler, struct af_compiler_index *index, u32 count )
+{
+   struct af_compiler_item *entry = stack_allocate( compiler->stack, sizeof(struct af_compiler_item), 1, "Compiler item" );
+   entry->next = NULL;
+
+   u32 data_size = count * index->element_size;
+   index->element_count += count;
+
+   entry->data = stack_allocate( compiler->stack, data_size, 8, NULL );
+   entry->count = count;
+
+   for( u32 i=0; i<data_size; i ++ )
+      ((u8 *)entry->data)[i] = 0xab;
+
+   if( index->last )
+      index->last->next = entry;
+   index->last = entry;
+
+   if( !index->first )
+      index->first = entry;
+
+   return entry;
+}
+
+static void af_compiler_init_index( struct af_compiler_index *index, const c8 *alias, u32 element_size )
+{
+   ASSERT_CRITICAL( element_size );
+   if( !buffer_copy( alias, 0, index->name, sizeof(index->name) ) )
+   {
+      $log( $fatal, {"Index name overflowed: "}, {alias} );
+      _fatal_exit();
+   }
+   index->element_size = element_size;
+   index->element_count = 0;
+   index->first = NULL;
+   index->last = NULL;
+}
+
+struct af_compiler_index *af_compiler_create_index( struct af_compiler *compiler, const c8 *alias, u32 element_size )
+{
+   struct af_compiler_item *item = af_compiler_allocate_items( compiler, &compiler->index, 1 );
+   struct af_compiler_index *index = item->data;
+   af_compiler_init_index( index, alias, element_size );
+   return index;
+}
+
+u32 af_compile_string( struct af_compiler *compiler, const c8 *string )
+{
+   u32 string_hash = buffer_djb2( string, 0 );
+
+   // TODO: Hash table against existing strings (low priority)
+   u32 offset = offset = compiler->strings_index->element_count;
+   u32 bytes = PAD_TO_4( buffer_first_index( string, 0,0 )+1 + 4 );
+   struct af_compiler_item *item = af_compiler_allocate_items( compiler, compiler->strings_index, bytes );
+   *((u32 *)item->data) = string_hash;
+   buffer_copy( string, 0, item->data+4, 0 );
+   return offset;
+}
+
+void af_compiler_init( struct af_compiler *compiler, struct stack_allocator *stack )
+{
+   compiler->stack = stack;
+   af_compiler_init_index( &compiler->index, "index", sizeof(struct af_compiler_index) );
+   compiler->strings_index = af_compiler_create_index( compiler, "strings", 1 );
+   af_compile_string( compiler, "nul" );
+}
+
+static void af_write_bin( struct af_compiler *compiler, void *data, u32 data_len, u32 padding )
+{
+   if( data )
+   {
+      stream_write( &compiler->stream, data, data_len );
+      compiler->file_offset += data_len;
+   }
+
+   if( padding )
+   {
+      while( compiler->file_offset % padding )
+      {
+         const u8 pad_byte = 0xac;
+         stream_write( &compiler->stream, &pad_byte, 1 );
+         compiler->file_offset ++;
+      }
+   }
+}
+
+struct af_compiler_index *af_get_or_make_index( struct af_compiler *compiler, const c8 *alias, u32 element_size )
+{
+   struct af_compiler_iter iter;
+   af_init_iterator( &iter, &compiler->index );
+   while( af_next( &iter ) )
+   {
+      struct af_compiler_index *index = iter.data;
+      if( compare_buffers( index->name, 0, alias, 0 ) )
+         return index;
+   }
+   
+   return af_compiler_create_index( compiler, alias, element_size );
+}
+
+b8 af_write( struct af_compiler *compiler, const c8 *path, u32 version )
+{
+   u32 indices_to_write = 0;
+   
+   struct af_compiler_iter iter;
+   af_init_iterator( &iter, &compiler->index );
+   while( af_next( &iter ) )
+   {
+      struct af_compiler_index *index = iter.data;
+      if( index->element_count )
+         indices_to_write ++;
+   }
+
+   u32 header_size = PAD_TO_8( sizeof( struct array_file_header ) );
+   u32 index_size = PAD_TO_8( sizeof( struct array_file_meta ) * indices_to_write );
+   
+   if( !stream_open_file( &compiler->stream, path, k_stream_write ) )
+      return 0;
+
+   compiler->file_offset = 0;
+
+   struct array_file_header header;
+   header.version = version;
+   header.index.file_offset = header_size;
+   header.index.item_count = indices_to_write;
+   header.index.item_size = sizeof(struct array_file_meta);
+   buffer_copy( "index", 0, header.index.name, sizeof(header.index.name) );
+   af_write_bin( compiler, &header, sizeof(struct array_file_header), 8 );
+
+   /* write index */
+   u32 file_offset = header_size + index_size;
+   af_init_iterator( &iter, &compiler->index );
+   while( af_next( &iter ) )
+   {
+      struct af_compiler_index *index = iter.data;
+      if( index->element_count )
+      {
+         struct array_file_meta meta;
+         buffer_copy( index->name, sizeof(index->name), meta.name, sizeof(meta.name) );
+         meta.item_count = index->element_count;
+         meta.item_size = index->element_size;
+         meta.file_offset = file_offset;
+         file_offset += PAD_TO_8( meta.item_size*meta.item_count );
+         af_write_bin( compiler, &meta, sizeof(struct array_file_meta), 0 );
+      }
+   }
+   af_write_bin( compiler, NULL, 0, 8 );
+
+   af_init_iterator( &iter, &compiler->index );
+   while( af_next( &iter ) )
+   {
+      struct af_compiler_index *index = iter.data;
+
+      if( index->element_count )
+      {
+         struct af_compiler_iter item_iter;
+         af_init_iterator( &item_iter, index );
+
+         while( af_next( &item_iter ) )
+            af_write_bin( compiler, item_iter.data, index->element_size, 0 );
+         af_write_bin( compiler, NULL, 0, 8 );
+      }
+   }
+
+   stream_close( &compiler->stream );
+   return 1;
+}
diff --git a/source/engine/vg_af.h b/source/engine/vg_af.h
new file mode 100644 (file)
index 0000000..5abd164
--- /dev/null
@@ -0,0 +1,89 @@
+#pragma once
+
+struct array_file_ptr
+{
+   void *data;
+   u32 count, stride;
+};
+
+struct array_file_meta
+{
+   u32 file_offset,
+       item_count,
+       item_size;
+
+   c8 name[16];
+};
+
+struct array_file_header
+{
+   u32 version;
+   struct array_file_meta index;
+};
+
+struct array_file_context
+{
+   struct stream *stream;
+   struct array_file_header header;
+   struct array_file_ptr index;
+};
+
+/* array loading */
+struct array_file_meta *af_find_array( struct array_file_context *ctx, const c8 *name );
+
+void af_load_array_file( struct array_file_context *ctx, struct array_file_ptr *out_ptr, 
+                         struct array_file_meta *arr, struct stack_allocator *stack, u32 stride );
+
+b8 af_load_array( struct array_file_context *ctx, struct array_file_ptr *ptr, const c8 *name, 
+                    struct stack_allocator *stack, u32 stride );
+void af_load_array_file_buffer( struct array_file_context *ctx, struct array_file_meta *arr, void *buffer, u32 stride );
+
+/* array access */
+void *af_arritm( struct array_file_ptr *arr, u32 index );
+u32 af_arrcount( struct array_file_ptr *arr );
+
+/* packed string buffer access (with djb2 hash prefix) */
+const c8 *af_str( const void *packed_strings, u32 pstr );
+u32 af_str_hash( const void *packed_strings, u32 pstr );
+b8 af_str_eq( const void *packed_strings, u32 pstr, const char *str, u32 str_hash );
+
+#define AF_STR_EQ( PACKED, PSTR, CONSTR ) \
+   af_str_eq( PACKED, PSTR, CONSTR, buffer_djb2( CONSTR, 0 ) )
+
+/* COmpiler 
+ * ------------------------------------ */
+
+struct af_compiler_item
+{
+   void *data;
+   u32 count;
+   struct af_compiler_item *next;
+};
+
+struct af_compiler_index
+{
+   c8 name[16];
+   u32 element_size, element_count;
+   struct af_compiler_item *first, *last;
+};
+
+struct af_compiler
+{
+   struct stack_allocator *stack;
+   struct af_compiler_index index,
+                           *strings_index;
+   struct af_compiler_item *most_recent_item;
+
+   struct stream stream;
+   u32 file_offset;
+};
+
+void af_compiler_init( struct af_compiler *compiler, struct stack_allocator *stack );
+struct af_compiler_item *af_compiler_allocate_items( struct af_compiler *compiler, struct af_compiler_index *index, u32 count );
+struct af_compiler_index *af_compiler_create_index( struct af_compiler *compiler, const c8 *alias, u32 element_size );
+b8 af_write( struct af_compiler *compiler, const c8 *path, u32 version );
+struct af_compiler_index *af_get_or_make_index( struct af_compiler *compiler, const c8 *alias, u32 element_size );
+u32 af_compile_string( struct af_compiler *compiler, const c8 *string );
+
+b8 af_open_stream( struct array_file_context *afc, struct stream *stream, u32 min_version, u32 max_version, 
+                     struct stack_allocator *stack );
index 0005b3c5ea7d17c917f89748b95d97d331b4ec6b..3c6601a1d6712ed65aa3f95ac4b2cb8475a91710 100644 (file)
@@ -63,6 +63,7 @@ struct audio_clip
    };
 };
 
+void _audio_init(void);
 void vg_audio_device_init(void);
 void vg_audio_begin(void);
 
index c9feb991a2e8dc3584800643efdbc54303c232f9..5e20ed09d2e32608a30481f82a1fe9e267a4c9f4 100644 (file)
@@ -1,5 +1,6 @@
 //#define VG_ECHO_LPF_BUTTERWORTH
 #pragma once
 
+void _dsp_init(void);
 void _dsp_update_tunings( f32 echo_distances[14] );
 void _dsp_process( f32 *stereo_in, f32 *stereo_out );
diff --git a/source/engine/vg_console.c b/source/engine/vg_console.c
new file mode 100644 (file)
index 0000000..735580f
--- /dev/null
@@ -0,0 +1,86 @@
+#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;
+   b8 open;
+}
+_console;
+
+struct log_history_item
+{
+   u32 type;
+   c8 buffer[];
+};
+
+static c8 _input_buffer[ 1024 ];
+
+static void _log_listener( const c8 *line, u32 length, u32 type )
+{
+again:;
+   struct log_history_item *item = queue_alloc( &_console.log_history, sizeof( struct log_history_item ) + length+1 );
+   if( !item )
+   {
+      queue_pop( &_console.log_history );
+      goto again;
+   }
+
+   item->type = type;
+   buffer_copy( line, 0, item->buffer, length+1 );
+}
+
+void _engine_console_init(void)
+{
+   _log_add_listener( _log_listener, ($info | $ok | $warning | $error | $fatal | $shell), 0 );
+   queue_init( &_console.input_history, _heap_allocate( BYTES_KB(2) ), BYTES_KB(2) );
+   queue_init( &_console.log_history, _heap_allocate( BYTES_KB(4) ), BYTES_KB(4) );
+}
+
+void _engine_console_update(void)
+{
+   for( u8 j=0; j<_input_button_down( k_input_action_console, 0 ); j ++ )
+   {
+      _console.open ^= 0x1;
+      u32 console_mask = (1 << k_input_layer_console) | (1 << k_input_layer_ui);
+      _input_layer_whitelist( _console.open? console_mask: 0 );
+   }
+}
+
+void _engine_console_ui(void)
+{
+   if( !_console.open )
+      return;
+   
+   i16 kerning[3];
+   _font_get_kerning( kerning );
+   i16 history_box[4] = { 0, 0, _engine.w, kerning[1] * 32 };
+
+   if( _console.log_history.allocation_count )
+   {
+      u32 item_offset = _console.log_history.head_offset;
+      for( u32 i=0; i<32; i ++ )
+      {
+         struct log_history_item *item = queue_data( &_console.log_history, item_offset );
+         i16 line_rect[4] = (i16[]){ 0, kerning[1] * (31-i), _engine.w, kerning[1] };
+         _ui_text( line_rect, item->buffer, k_ui_align_x_left, (union colour){{128,128,128,255}} );
+
+         if( !queue_previous( &_console.log_history, item_offset, &item_offset ) )
+            break;
+      }
+   }
+
+   _graphics_line_rect( history_box, (union colour){{0,255,255,255}} );
+
+   enum textbox_action action = _ui_textbox( (i16[]){ 0, kerning[1]*32, _engine.w, kerning[1] }, 
+         _input_buffer, sizeof(_input_buffer), 1, UI_AUTOFOCUS );
+
+   if( action == k_textbox_enter )
+   {
+      _console_execute( _input_buffer, 0, 0 );
+      _input_buffer[0] = '\0';
+   }
+}
diff --git a/source/engine/vg_console.h b/source/engine/vg_console.h
new file mode 100644 (file)
index 0000000..09750a3
--- /dev/null
@@ -0,0 +1,5 @@
+#pragma once
+
+void _engine_console_init(void);
+void _engine_console_update(void);
+void _engine_console_ui(void);
index d5080547042d9a8ba88a5fb6730f9492949a37ca..8a16dc4874998f4b39e751a3b7664c0666267f6c 100644 (file)
@@ -9,10 +9,17 @@
 #include "vg_ui.h"
 #include "vg_audio.h"
 #include "vg_graphics.h"
+#include "vg_audio_dsp.h"
+#include "vg_audio.h"
+#include "vg_shader.h"
+#include "vg_render.h"
+#include "vg_tex.h"
+#include "vg_lines.h"
+#include "vg_framebuffer.h"
+#include "vg_console.h"
 
 // TODO: temp
 #include "console_system.h"
-#include "generated/hooks.h"
 
 #if defined( __EMSCRIPTEN__ )
 #include <emscripten.h>
@@ -89,7 +96,12 @@ SDL_Thread *_async_thread_handle = NULL;
 
 i32 main( i32 argc, const c8 *argv[] )
 {
-   VG_PRE_MAIN;
+   _exit_init();
+   _log_init();
+   _options_init( argc, argv );
+   _engine_options();
+   if( _vg_engine_hooks.options ) _vg_engine_hooks.options();
+   _options_check_end();
 
    $log( $info, {"SDL_INIT_VIDEO"} );
    if( SDL_Init( SDL_INIT_VIDEO ) == 0 )
@@ -236,7 +248,19 @@ i32 main( i32 argc, const c8 *argv[] )
 
    mt_random_seed( &_engine.random, 887765 );
 
-   EVENT_CALL( START );
+   _async_init();
+   _dsp_init();
+   _audio_init();
+   _engine_ui_init();
+   _input_init();
+   _console_init();
+   _engine_console_init();
+   _vg_render_init();
+   _shader_init();
+   _vg_tex_init();
+   _vg_lines_init();
+   if( _vg_engine_hooks.start ) _vg_engine_hooks.start();
+
    _async_thread_handle = SDL_CreateThread( _async_thread, "ASync thread", NULL );
 
 #if defined( __EMSCRIPTEN__ )
@@ -288,7 +312,9 @@ void _vg_engine_frame(void)
    while( _task_queue_process( 0 ) ) {}
 
    /* normal update */
-   EVENT_CALL( ENGINE_NEW_FRAME );
+   _input_update();
+   _engine_console_update();
+   if( _vg_engine_hooks.frame_start ) _vg_engine_hooks.frame_start();
 
    /* fixed update */
    _fixed_accumulator += _engine.time_delta;
@@ -302,7 +328,7 @@ void _vg_engine_frame(void)
          _fixed_accumulator -= 1.0/60.0;
          _fixed_time += _engine.time_delta;
          _engine.time = _fixed_time;
-         EVENT_CALL( ENGINE_FIXED_UPDATE );
+         if( _vg_engine_hooks.fixed_frame ) _vg_engine_hooks.fixed_frame();
       }
       else break;
    }
@@ -314,13 +340,13 @@ void _vg_engine_frame(void)
    //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 );
-   }
+      _framebuffer_resize();
 
-   EVENT_CALL( ENGINE_RENDER );
+   if( _vg_engine_hooks.frame_render ) _vg_engine_hooks.frame_render();
    _engine_ui_pre_render();
-   EVENT_CALL( ENGINE_UI );
+
+   if( _vg_engine_hooks.frame_ui ) _vg_engine_hooks.frame_ui();
+   _engine_console_ui();
    _audio_gui();
    _engine_ui_post_render();
 
@@ -333,7 +359,7 @@ void _vg_engine_frame(void)
       if( e.type == SDL_EVENT_QUIT )
       {
          SDL_DestroyWindow( _engine.window_handle );
-         EVENT_CALL( END );
+         if( _vg_engine_hooks.end ) _vg_engine_hooks.end();
          _task_queue_stop_normal( k_thread_async );
          SDL_WaitThread( _async_thread_handle, NULL );
          SDL_Quit();
index 8f6e563f9cb70bad384b199eafc0913e05081394..ea1c8f437f75fd810b6cd1432e89d117a654325c 100644 (file)
@@ -23,3 +23,15 @@ struct _engine
    struct mt_random random;
 }
 extern _engine;
+
+struct _vg_engine_hooks
+{
+   void (*options)( void );
+   void (*start)( void );
+   void (*frame_start)( void );
+   void (*fixed_frame)( void );
+   void (*frame_render)( void );
+   void (*frame_ui)( void );
+   void (*end)( void );
+}
+extern _vg_engine_hooks;
diff --git a/source/engine/vg_engine_core.kv b/source/engine/vg_engine_core.kv
deleted file mode 100644 (file)
index d639c16..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-{
-   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
diff --git a/source/engine/vg_entity.c b/source/engine/vg_entity.c
new file mode 100644 (file)
index 0000000..99aa597
--- /dev/null
@@ -0,0 +1,15 @@
+#include "vg_entity.h"
+#include "generated/entities.c"
+
+i32 _vg_entity_link_function( u16 entity_type, const c8 *alias )
+{
+   struct entity_info *info = _vg_entity_info( entity_type );
+   for( u16 i=0; i<info->function_count; i ++ )
+   {
+      struct entity_function_info *func_info = &_entity_function_infos[ info->function_start + i ];
+      if( compare_buffers( alias, 0, func_info->name, 0 ) )
+         return (i32)i;
+   }
+   $log( $error, {"Failed to link function from alias '"}, {alias}, {"'"} );
+   return -1;
+}
diff --git a/source/engine/vg_entity.h b/source/engine/vg_entity.h
new file mode 100644 (file)
index 0000000..40e835b
--- /dev/null
@@ -0,0 +1,757 @@
+#pragma once
+#include "vg_model.h"
+
+#include "generated/entities.h"
+
+#if 0
+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_flame       = 200, // FIXME: Move thiese into own file per-game.
+   k_ent_waterfall   = 201,
+   k_ent_rb2         = 202,
+   k_ent_heatpipe    = 203,
+   k_ent_waterlevel  = 204,
+   k_ent_viewstone   = 205,
+   k_ent_gauge       = 206,
+   k_ent_particle    = 207,
+   k_ent_deep_npc    = 208,
+   k_ent_max
+};
+#endif
+
+const c8 *_vg_entity_name( u32 entity_id );
+
+#if 0
+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;
+};
+#endif
+
+enum light_type
+{
+   k_light_type_point = 0,
+   k_light_type_spot = 1
+};
+
+#define ENT_LIGHT_FLAG_DAYTIME 0x1
+#define ENT_LIGHT_FLAG_OFF     0x2
+
+#if 0
+struct ent_light
+{
+   struct mdl_transform transform;
+   u32 flags,
+       type;
+   f32 colour[4];
+   f32 angle,
+       range;
+
+   union
+   {
+      /* These are temporary, we could remove them from the file structure. */
+      struct
+      {
+         f32 inverse_world[4][3];
+         f32 angle_sin_cos[2];
+      };
+
+      struct
+      {
+         f32 lerp_start[3], lerp_end[3];
+         f32 lerp_duration, timer;
+      };
+   };
+};
+#endif
+
+/* 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
+{
+   union entity_id entity;
+   u32 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
+{
+   u32 blank1;
+   i32 blank;
+};
+
+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 filter;
+   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,
+   k_entity_event_result_invalid_parameter,
+};
+
+enum ent_event_flags
+{
+   k_ent_event_data_void = 0x0,
+   k_ent_event_data_const_i32 = 0x1,
+   k_ent_event_data_const_f32 = 0x2,
+   k_ent_event_data_const_entity_id = 0x4,
+   k_ent_event_data_const_string = 0x8,
+   k_ent_event_data_data_alias = 0x10,
+   k_ent_event_data_v3f = 0x20,
+};
+
+struct ent_event
+{
+   u32 pstr_source_event;
+   u32 pstr_recieve_event; 
+   
+   union entity_id source;
+   union entity_id reciever;
+   u32 flags;
+
+   f32 delay;
+   u8 function_index, activator, unused0, unused1; // FIXME: REPLACES PSTR!!!!!!!!!!!
+
+   union
+   {
+      i32 const_i32;
+      f32 const_f32;
+      union entity_id const_entity_id;
+      u32 const_pstr;
+      u32 pstr_data_alias;
+   }
+   data;
+};
+
+struct entity_info
+{
+   u16 id, unused0;
+   u16 function_start, function_count;
+   const c8 *name;
+};
+struct entity_function_info
+{
+   const c8 *name;
+};
+
+struct entity_info *_vg_entity_info( u16 entity_type );
+enum entity_event_result _vg_entity_event_dispatch( struct ent_event *event );
+i32 _vg_entity_link_function( u16 entity_type, const c8 *alias );
index a086efa15e91d0fb244acc859ac7af68af47c0ed..ed3b41faf2d87a5b17599d6fb5f3ae3250f36a33 100644 (file)
@@ -56,6 +56,8 @@ struct framebuffer
    attachments[];
 };
 
+void _framebuffer_resize(void);
+
 struct framebuffer *_framebuffer_allocate( struct stack_allocator *stack, u32 attachment_count, b8 track );
 
 /* 
index c26b6693925a65abc62670072e469f0ead5de45c..8fadff234aa5ecdcf200f060a639da7fcb06fe6b 100644 (file)
@@ -33,6 +33,9 @@ enum input_button_action
 };
 void _input_callback_buttonlike( enum input_device device, i32 id, enum input_button_action action, u32 mods );
 
+void _input_init(void);
+void _input_update(void);
+
 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 );
index 62fb78145d325ada87a040a009dd3ca2071c7f43..6a2a356629dba24bc69ec11e63aa4b785a255c7b 100644 (file)
@@ -1,3 +1,4 @@
+#pragma once
 #define LINE_RED   0xff0000ff
 #define LINE_GREEN 0xff00ff00
 #define LINE_BLUE  0xffff0000
@@ -9,6 +10,7 @@
 #define LINE_CYAN  0xffffff00
 #define LINE_NONE  0x00000000
 
+void _vg_lines_init(void);
 void vg_lines_clear(void);
 void vg_lines_draw( f32 pv[4][4] );
 
diff --git a/source/engine/vg_metascene.c b/source/engine/vg_metascene.c
new file mode 100644 (file)
index 0000000..783a965
--- /dev/null
@@ -0,0 +1,499 @@
+#include "vg_engine.h"
+#include "vg_metascene.h"
+#include "common_maths.h"
+#include "vg_model.h"
+#include "vg_entity.h"
+#include "shader_props.h"
+#include "vg_af.h"
+#include "vg_async.h"
+
+void metascene_load( struct metascene *ms, const c8 *path, struct stack_allocator *stack )
+{
+   ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
+   zero_buffer( ms, sizeof(struct metascene) );
+
+   struct stream stream;
+   ASSERT_CRITICAL( stream_open_file( &stream, path, k_stream_read ) );
+
+   u32 temp_frame = _start_temporary_frame();
+   {
+      struct array_file_context af;
+      ASSERT_CRITICAL( af_open_stream( &af, &stream, MS_VERSION_MIN, MS_VERSION_NR, _temporary_stack_allocator() ) );
+
+      struct array_file_ptr strings;
+      af_load_array( &af, &strings, "strings", stack, 1 );
+      ms->packed_strings = strings.data;
+      af_load_array( &af, &ms->infos, "ms_scene_info", stack, sizeof(struct ms_scene_info) );
+      af_load_array( &af, &ms->instances, "ms_instance", stack, sizeof(struct ms_instance) );
+      af_load_array( &af, &ms->overrides, "ms_override", stack, sizeof(struct ms_override) );
+      af_load_array( &af, &ms->strips, "ms_strip", stack, sizeof(struct ms_strip) );
+      af_load_array( &af, &ms->tracks, "ms_track", stack, sizeof(struct ms_track) );
+      af_load_array( &af, &ms->keyframes, "ms_keyframe", stack, sizeof(struct ms_keyframe) );
+      af_load_array( &af, &ms->cameras, "ent_camera", stack, sizeof(struct ent_camera) );
+      af_load_array( &af, &ms->audios, "ent_audio", stack, sizeof(struct ent_audio) );
+      af_load_array( &af, &ms->audio_clips, "ent_audio_clip", stack, sizeof(struct ent_audio_clip) );
+      af_load_array( &af, &ms->curves, "ms_curves", stack, sizeof(struct ms_curve_keyframe) );
+
+      ASSERT_CRITICAL( af_arrcount( &ms->infos ) );
+      struct ms_scene_info *src_inf = af_arritm( &ms->infos, 0 );
+      ms->info = *src_inf;
+
+      stream_close( &stream );
+   }
+   _end_temporary_frame( temp_frame );
+}
+
+u32 skeleton_bone_id( struct ms_skeleton *skele, const c8 *name )
+{
+   for( u32 i=1; i<skele->bone_count; i++ )
+      if( compare_buffers( skele->bones[i].name, 0, name, 0 ) )
+         return i;
+
+   $log( $fatal, {"skeleton_bone_id( *, "}, {name}, {" ); -> Bone does not exist"} );
+   _fatal_exit();
+   return 0;
+}
+
+void keyframe_copy_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, i32 num )
+{
+   for( i32 i=0; i<num; i++ )
+      kfb[i] = kfa[i];
+}
+
+
+/* apply a rotation from the perspective of root */
+void keyframe_rotate_around( struct ms_keyframe *kf, f32 origin[3], f32 offset[3], f32 q[4] )
+{
+   f32 v0[3], co[3];
+   v3_add( kf->co, offset, co );
+   v3_sub( co, origin, v0 );
+   q_mulv( q, v0, v0 );
+   v3_add( v0, origin, co );
+   v3_sub( co, offset, kf->co );
+   q_mul( q, kf->q, kf->q );
+   q_normalize( kf->q );
+}
+
+void keyframe_lerp( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd )
+{
+   v3_lerp( kfa->co, kfb->co, t, kfd->co );
+   q_nlerp( kfa->q,  kfb->q,  t, kfd->q );
+   v3_lerp( kfa->s,  kfb->s,  t, kfd->s );
+}
+
+/*
+ * Lerp between two sets of keyframes and store in dest. Rotations use Nlerp.
+ */
+void keyframe_lerp_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd, i32 count )
+{
+   if( t <= 0.0001f )
+   {
+      keyframe_copy_pose( kfa, kfd, count );
+      return;
+   }
+   else if( t >= 0.9999f )
+   {
+      keyframe_copy_pose( kfb, kfd, count );
+      return;
+   }
+
+   for( i32 i=0; i<count; i++ )
+      keyframe_lerp( kfa+i, kfb+i, t, kfd+i );
+}
+
+void skeleton_lerp_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd )
+{
+   keyframe_lerp_pose( kfa, kfb, t, kfd, skele->bone_count-1 );
+}
+
+void skeleton_copy_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfd )
+{
+   keyframe_copy_pose( kfa, kfd, skele->bone_count-1 );
+}
+
+/*
+ * Sample animation between 2 closest frames using time value. Output is a
+ * keyframe buffer that is allocated with an appropriate size
+ *
+ * Time is in SECONDS
+ */
+void skeleton_sample_anim( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output )
+{
+   struct ms_strip *strip = anim->strip;
+   f32 animtime  = fmodf( time*anim->framerate, (f32)strip->strip.length ),
+       animframe = floorf( animtime ),
+       t = animtime - animframe;
+
+   u32 frame = (u32)animframe % strip->strip.length,
+       next  = (frame+1) % strip->strip.length;
+
+   struct ms_keyframe *base  = anim->keyframes_base + strip->strip.count*frame,
+                      *nbase = anim->keyframes_base + strip->strip.count*next;
+   skeleton_lerp_pose( skele, base, nbase, t, output );
+}
+
+/* time is in SECONDS */
+i32 skeleton_sample_anim_clamped( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output )
+{
+   struct ms_strip *strip = anim->strip;
+   f32 end = (strip->strip.length-1)/anim->framerate;
+   skeleton_sample_anim( skele, anim, f32_min( end, time ), output );
+   if( time > end ) return 0;
+   else             return 1;
+}
+
+static i32 should_apply_bone( struct ms_skeleton *skele, u32 id, enum anim_apply type )
+{
+   struct ms_skeleton_bone *sb = &skele->bones[ id ],
+                           *sp = &skele->bones[ sb->parent ];
+   if( type == k_anim_apply_defer_ik )
+   {
+      if( ((sp->flags & k_bone_flag_ik) && !(sb->flags & k_bone_flag_ik)) || sp->defer )
+      {
+         sb->defer = 1;
+         return 0;
+      }
+      else
+      {
+         sb->defer = 0;
+         return 1;
+      }
+   }
+   else if( type == k_anim_apply_deffered_only )
+   {
+      if( sb->defer )
+         return 1;
+      else
+         return 0;
+   }
+
+   return 1;
+}
+
+/*
+ * Apply block of keyframes to skeletons final pose
+ */
+void skeleton_apply_pose( struct ms_skeleton *skele, struct ms_keyframe *pose, enum anim_apply passtype, f32 final_mtx[][4][3] )
+{
+   if( passtype == k_anim_apply_absolute )
+   {
+      for( u32 i=1; i<skele->bone_count; i++ )
+      {
+         struct ms_keyframe *kf = &pose[i-1];
+         q_m3x3( kf->q, final_mtx[i] );
+         m3x3_scale( final_mtx[i], kf->s );
+         v3_copy( kf->co, final_mtx[i][3] );
+      }
+      return;
+   }
+
+   m4x3_identity( final_mtx[0] );
+   skele->bones[0].defer = 0;
+   skele->bones[0].flags &= ~k_bone_flag_ik;
+
+   for( u32 i=1; i<skele->bone_count; i++ )
+   {
+      struct ms_skeleton_bone *sb = &skele->bones[i];
+      if( !should_apply_bone( skele, i, passtype ) )
+         continue;
+
+      sb->defer = 0;
+
+      /* process pose */
+      f32 posemtx[4][3];
+      f32 temp_delta[3];
+      v3_sub( skele->bones[i].co, skele->bones[sb->parent].co, temp_delta );
+
+      /* pose matrix */
+      struct ms_keyframe *kf = &pose[i-1];
+      q_m3x3( kf->q, posemtx );
+      m3x3_scale( posemtx, kf->s );
+      v3_copy( kf->co, posemtx[3] );
+      v3_add( temp_delta, posemtx[3], posemtx[3] );
+
+      /* final matrix */
+      m4x3_mul( final_mtx[ sb->parent ], posemtx, final_mtx[i] );
+   }
+}
+
+/* 
+ * Take the final matrices and decompose it into an absolute positioned anim
+ */
+void skeleton_decompose_mtx_absolute( struct ms_skeleton *skele, struct ms_keyframe *anim, f32 final_mtx[][4][3] )
+{
+   for( u32 i=1; i<skele->bone_count; i++ )
+   {
+      struct ms_keyframe *kf = &anim[i-1];
+      m4x3_decompose( final_mtx[i], kf->co, kf->q, kf->s );
+   }
+}
+
+/* 
+ * creates the reference inverse matrix for an IK bone, as it has an initial 
+ * intrisic rotation based on the direction that the IK is setup..
+ */
+void skeleton_inverse_for_ik( struct ms_skeleton *skele, f32 ivaxis[3], u32 id, f32 inverse[3][3] )
+{
+   v3_copy( ivaxis, inverse[0] );
+   v3_copy( skele->bones[id].end, inverse[1] );
+   v3_normalize( inverse[1] );
+   v3_cross( inverse[0], inverse[1], inverse[2] );
+   m3x3_transpose( inverse, inverse );
+}
+
+/*
+ * Creates inverse rotation matrices which the IK system uses.
+ */
+void skeleton_create_inverses( struct ms_skeleton *skele )
+{
+   /* IK: inverse 'plane-bone space' axis '(^axis,^bone,...)[base] */
+   for( u32 i=0; i<skele->ik_count; i++ )
+   {
+      struct ms_skeleton_ik *ik = &skele->ik[i];
+      f32 iv0[3], iv1[3], ivaxis[3];
+      v3_sub( skele->bones[ik->target].co, skele->bones[ik->lower].co, iv0 );
+      v3_sub( skele->bones[ik->pole].co,   skele->bones[ik->lower].co, iv1 );
+      v3_cross( iv0, iv1, ivaxis );
+      v3_normalize( ivaxis );
+
+      skeleton_inverse_for_ik( skele, ivaxis, ik->lower, ik->ia );
+      skeleton_inverse_for_ik( skele, ivaxis, ik->upper, ik->ib );
+   }
+}
+
+/*
+ * Apply a model matrix to all bones, should be done last
+ */
+void skeleton_apply_transform( struct ms_skeleton *skele, f32 transform[4][3], f32 final_mtx[][4][3] )
+{
+   for( u32 i=0; i<skele->bone_count; i++ )
+      m4x3_mul( transform, final_mtx[i], final_mtx[i] );
+}
+
+/*
+ * Apply an inverse matrix to all bones which maps vertices from bind space into
+ * bone relative positions
+ */
+void skeleton_apply_inverses( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
+{
+   for( u32 i=0; i<skele->bone_count; i++ )
+   {
+      struct ms_skeleton_bone *sb = &skele->bones[i];
+      f32 inverse[4][3];
+      m3x3_identity( inverse );
+      v3_negate( sb->co, inverse[3] );
+      m4x3_mul( final_mtx[i], inverse, final_mtx[i] );
+   }
+}
+
+/*
+ * Apply all IK modifiers (2 bone ik reference from blender is supported)
+ */
+void skeleton_apply_ik_pass( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
+{
+   for( u32 i=0; i<skele->ik_count; i++ )
+   {
+      struct ms_skeleton_ik *ik = &skele->ik[i];
+      f32 v0[3], /* base -> target */
+          v1[3], /* base -> pole */
+          vaxis[3];
+      f32 co_base[3],
+          co_target[3],
+          co_pole[3];
+
+      v3_copy( final_mtx[ik->lower][3], co_base );
+      v3_copy( final_mtx[ik->target][3], co_target );
+      v3_copy( final_mtx[ik->pole][3], co_pole );
+
+      v3_sub( co_target, co_base, v0 );
+      v3_sub( co_pole, co_base, v1 );
+      v3_cross( v0, v1, vaxis );
+      v3_normalize( vaxis );
+      v3_normalize( v0 );
+      v3_cross( vaxis, v0, v1 );
+
+      /* localize problem into [x:v0,y:v1] 2d plane */
+      f32 base[2] = { v3_dot( v0, co_base   ), v3_dot( v1, co_base   ) },
+          end[2]  = { v3_dot( v0, co_target ), v3_dot( v1, co_target ) },
+          knee[2];
+
+      /* Compute angles (basic trig)*/
+      f32 delta[2];
+      v2_sub( end, base, delta );
+
+      f32 l1 = v3_length( skele->bones[ik->lower].end ),
+          l2 = v3_length( skele->bones[ik->upper].end ),
+          d = f32_clamp( v2_length(delta), fabsf(l1 - l2), l1+l2-0.00001f ),
+          c = acosf( (l1*l1 + d*d - l2*l2) / (2.0f*l1*d) ),
+          rot = atan2f( delta[1], delta[0] ) + c - VG_PIf/2.0f;
+
+      knee[0] = sinf(-rot) * l1;
+      knee[1] = cosf(-rot) * l1;
+
+      m4x3_identity( final_mtx[ik->lower] );
+      m4x3_identity( final_mtx[ik->upper] );
+
+      /* create rotation matrix */
+      f32 co_knee[3];
+      v3_muladds( co_base, v0, knee[0], co_knee );
+      v3_muladds( co_knee, v1, knee[1], co_knee );
+      // vg_line( co_base, co_knee, 0xff00ff00 );
+
+      f32 transform[4][3];
+      v3_copy( vaxis, transform[0] );
+      v3_muls( v0, knee[0], transform[1] );
+      v3_muladds( transform[1], v1, knee[1], transform[1] );
+      v3_normalize( transform[1] );
+      v3_cross( transform[0], transform[1], transform[2] );
+      v3_copy( co_base, transform[3] );
+
+      m3x3_mul( transform, ik->ia, transform );
+      m4x3_copy( transform, final_mtx[ik->lower] );
+
+      /* upper/knee bone */
+      v3_copy( vaxis, transform[0] );
+      v3_sub( co_target, co_knee, transform[1] );
+      v3_normalize( transform[1] );
+      v3_cross( transform[0], transform[1], transform[2] );
+      v3_copy( co_knee, transform[3] );
+
+      m3x3_mul( transform, ik->ib, transform );
+      m4x3_copy( transform, final_mtx[ik->upper] );
+   }
+}
+
+/*
+ * Applies the typical operations that you want for an IK rig: 
+ *    Pose, IK, Pose(deferred), Inverses, Transform
+ */
+void skeleton_apply_standard( struct ms_skeleton *skele, struct ms_keyframe *pose, f32 transform[4][3], f32 final_mtx[][4][3] )
+{
+   skeleton_apply_pose( skele, pose, k_anim_apply_defer_ik, final_mtx );
+   skeleton_apply_ik_pass( skele, final_mtx );
+   skeleton_apply_pose( skele, pose, k_anim_apply_deffered_only, final_mtx );
+   skeleton_apply_inverses( skele, final_mtx );
+   skeleton_apply_transform( skele, transform, final_mtx );
+}
+
+void skeleton_alloc_from( struct ms_skeleton *skele, struct stack_allocator *stack, 
+                          struct vg_model *model, struct mdl_armature *armature )
+{
+   skele->bone_count     = armature->bone_count+1;
+   skele->ik_count       = 0;
+   skele->collider_count = 0;
+
+   for( u32 i=0; i<armature->bone_count; i++ )
+   {
+      struct mdl_bone *bone = &model->bones[ armature->bone_start+i ];
+      if( bone->flags & k_bone_flag_ik )
+         skele->ik_count ++;
+      if( bone->collider )
+         skele->collider_count ++;
+   }
+
+   u32 bone_size = sizeof(struct ms_skeleton_bone) * skele->bone_count,
+       ik_size   = sizeof(struct ms_skeleton_ik)   * skele->ik_count;
+
+   skele->bones = stack_allocate( stack, bone_size, 8, NULL );
+   skele->ik    = stack_allocate( stack, ik_size, 8, NULL );
+
+   zero_buffer( skele->bones, bone_size );
+   zero_buffer( skele->ik, ik_size );
+}
+
+/* Setup a skeleton from model. mdl's metadata should stick around */
+void skeleton_setup( struct ms_skeleton *skele, struct vg_model *model, u32 index, struct stack_allocator *stack )
+{
+   u32 ik_count = 0, collider_count = 0;
+   skele->bone_count = 0;
+   skele->bones = NULL;
+
+   if( !model->armature_count )
+   {
+      $log( $fatal, {"No skeleton in model"} );
+      _fatal_exit();
+   }
+
+   struct mdl_armature *armature = &model->armatures[ index ];
+   skeleton_alloc_from( skele, stack, model, armature );
+
+   for( u32 i=0; i<armature->bone_count; i++ )
+   {
+      struct mdl_bone *bone = &model->bones[ armature->bone_start+i ];
+      struct ms_skeleton_bone *sb = &skele->bones[i+1];
+
+      v3_copy( bone->co, sb->co );
+      v3_copy( bone->end, sb->end );
+
+      sb->parent = bone->parent;
+      sb->name   = af_str( model->packed_strings, bone->pstr_name );
+      sb->flags  = bone->flags;
+      sb->collider = bone->collider;
+      sb->orig_bone = bone;
+
+      if( sb->flags & k_bone_flag_ik )
+      {
+         skele->bones[ sb->parent ].flags |= k_bone_flag_ik;
+         
+         if( ik_count == skele->ik_count )
+         {
+            $log( $fatal, {"Too many ik bones, corrupt model file"} );
+            _fatal_exit();
+         }
+
+         struct ms_skeleton_ik *ik = &skele->ik[ ik_count ++ ];
+         ik->upper = i+1;
+         ik->lower = bone->parent;
+         ik->target = bone->ik_target;
+         ik->pole = bone->ik_pole;
+      }
+
+      box_copy( bone->hitbox, sb->hitbox );
+      if( bone->collider )
+      {
+         if( collider_count == skele->collider_count )
+         {
+            $log( $fatal, {"Too many collider bones"} );
+            _fatal_exit();
+         }
+         collider_count ++;
+      }
+   }
+
+   /* fill in implicit root bone */
+   v3_fill( skele->bones[0].co, 0 );
+   v3_copy( (f32[3]){0.0f,1.0f,0.0f}, skele->bones[0].end );
+   skele->bones[0].parent = 0xffffffff;
+   skele->bones[0].flags = 0;
+   skele->bones[0].name = "[root]";
+
+   skeleton_create_inverses( skele );
+   $log( $ok, {"Loaded skeleton with "}, $unsigned( skele->bone_count ), {" bones"} );
+   $log( $ok, {"                     "}, $unsigned( skele->collider_count), {" colliders"} );
+}
+
+#if 0
+void skeleton_debug( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
+{
+   for( u32 i=1; i<skele->bone_count; i ++ )
+   {
+      struct ms_skeleton_bone *sb = &skele->bones[i];
+      f32 p0[3], p1[3];
+      v3_copy( sb->co, p0 );
+      v3_add( p0, sb->end, p1 );
+
+      m4x3_mulv( final_mtx[i], p0, p0 );
+      m4x3_mulv( final_mtx[i], p1, p1 );
+
+      if( sb->flags & k_bone_flag_deform )
+      {
+         if( sb->flags & k_bone_flag_ik )
+            vg_line( p0, p1, 0xff0000ff );
+         else
+            vg_line( p0, p1, 0xffcccccc );
+      }
+      else
+         vg_line( p0, p1, 0xff00ffff );
+   }
+}
+#endif
diff --git a/source/engine/vg_metascene.h b/source/engine/vg_metascene.h
new file mode 100644 (file)
index 0000000..6b3c1ac
--- /dev/null
@@ -0,0 +1,186 @@
+#pragma once
+#include "foundation.h"
+#include "vg_af.h"
+#include "vg_model.h"
+#include "vg_entity.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
+      {
+         union entity_id entity;
+      }
+      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
+};
+
+void skeleton_apply_pose( struct ms_skeleton *skele, struct ms_keyframe *pose, enum anim_apply passtype, f32 final_mtx[][4][3] );
+void skeleton_decompose_mtx_absolute( struct ms_skeleton *skele, struct ms_keyframe *anim, f32 final_mtx[][4][3] );
+void skeleton_inverse_for_ik( struct ms_skeleton *skele, f32 ivaxis[3], u32 id, f32 inverse[3][3] );
+void skeleton_create_inverses( struct ms_skeleton *skele );
+void skeleton_apply_transform( struct ms_skeleton *skele, f32 transform[4][3], f32 final_mtx[][4][3] );
+void skeleton_apply_inverses( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
+void skeleton_apply_ik_pass( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
+void skeleton_apply_standard( struct ms_skeleton *skele, struct ms_keyframe *pose, f32 transform[4][3], f32 final_mtx[][4][3] );
+void skeleton_setup( struct ms_skeleton *skele, struct vg_model *model, u32 index, struct stack_allocator *stack );
+void skeleton_debug( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
diff --git a/source/engine/vg_model.c b/source/engine/vg_model.c
new file mode 100644 (file)
index 0000000..fb1787a
--- /dev/null
@@ -0,0 +1,562 @@
+#include "foundation.h"
+#include "common_maths.h"
+#include "vg_async.h"
+
+#include "vg_model.h"
+#include "vg_af.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 ||
+             mat->shader == k_shader_pipe )
+         {
+            keyvalues_read_u32s( &kvs, root, "tex_diffuse", (u32[]){0}, &props->standard.tex_diffuse, 1 );
+            keyvalues_read_u32s( &kvs, root, "tex_normal", (u32[]){0}, &props->standard.tex_normal, 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_LINEAR | 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;
+}
+
+void vg_model_bind_texture( struct vg_model *model, GLuint target, u32 tex_id, u32 slot )
+{
+   if( tex_id )
+   {
+      union mdl_texture *mdl_tex = &model->textures[ tex_id -1 ];
+      vg_tex_bind( target, &mdl_tex->tex, slot );
+   }
+   else
+      vg_tex_bind( target, NULL, slot );
+}
diff --git a/source/engine/vg_model.h b/source/engine/vg_model.h
new file mode 100644 (file)
index 0000000..936e065
--- /dev/null
@@ -0,0 +1,288 @@
+#pragma once
+#include "vg_tex.h"
+#include "shader_props.h"
+#include "vg_af.h"
+
+#define VG_MODEL_VERSION_MIN 110
+#define VG_MODEL_VERSION_NR 110
+
+union entity_id
+{
+   u32 id;
+   struct
+   {
+      u16 index;
+      u16 type;
+   };
+};
+
+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_pipe                    = 12,
+   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,
+
+   k_material_flag_spritesheet      = 0x1000
+};
+
+#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;
+   union entity_id entity;
+   union entity_id armature;
+};
+
+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_bind_texture( struct vg_model *model, GLuint target, u32 tex_id, u32 slot );
+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] );
index f052b0a1f85daaba489a8cfd0d9fcbc5adaf872b..84946a5a66754c015b69e01b9996fce98f3a83aa 100644 (file)
@@ -1,2 +1,4 @@
 #pragma once
+
+void _vg_render_init(void);
 void _render_fullscreen_quad(void);
index 76ee8e737beb0c77daabfe78773f3f5cbe19704a..1f2c925c7c4306cee57b3ac232a65efe0e0cd370 100644 (file)
@@ -1,6 +1,7 @@
 #include "vg_opengl.h"
 #include "generated/shaders.h"
 
+void _shader_init(void);
 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 );
index 89c14c4874a13c5ac0f9cd1bf773bec9a65fb525..df597d4a0b947c82b5c4adcfc89cd89805cfb23e 100644 (file)
@@ -28,6 +28,8 @@ struct qoi_desc
 # define QOI_SRGB   0
 # define QOI_LINEAR 1
 
+void _vg_tex_init(void);
+
 /* 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 );
index 22d8b25797697a561ebb0017eb68c75cecfc0e74..fe8fec381cede1a6bc7f626ff05a783cb02c2b99 100644 (file)
@@ -7,6 +7,7 @@ struct _engine_ui
 }
 extern _engine_ui;
 
+void _engine_ui_init(void);
 void _engine_ui_pre_render(void);
 void _engine_ui_post_render(void);
 b8 _engine_ui_want_text_input(void);
index ad108a47c3d8838750ea604074be15ab84e3ff38..fe6fb811d42dfb9372b543d2a569e3b397bdecff 100644 (file)
@@ -1,12 +1,14 @@
 /* Voyager common application interface */
 #pragma once
 
+#if 0
 #define VG_PRE_MAIN \
    _exit_init(); \
    _log_init(); \
    _options_init( argc, argv ); \
    EVENT_CALL( OPTIONS ); \
    _options_check_end(); 
+#endif
 
 #define BYTES_KB( X ) X*1024
 #define BYTES_MB( X ) X*1024*1024
index 614526950a800ec7a71f5f51dd67a92bfdebfb9e..c4c49912b51fd0f230d458e20081fc202782d21c 100644 (file)
@@ -1,28 +1,6 @@
 include ""
-
-event
-{
-   name OPTIONS
-   prototype "void"
-}
-event
-{
-   name START
-   prototype "void"
-}
-event
-{
-   name END
-   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
index dc0984ae7a27f30a80f2c0a0a9a1fb56f06726d9..39898fd90a8d5413e77cdca4fd1208b33536a7c1 100644 (file)
@@ -393,10 +393,11 @@ void keyvalues_parse_stream( struct keyvalues *kvs, u32 root_offset, struct stre
    while( stream_read( in_stream, &c, 1 ) )
    {
       parser.stat_source_characters ++;
-      if( c == '\0' )
-         break;
 
       b8 is_control_character = 0;
+      if( c == 0 )
+         is_control_character = 1;
+
       if( parser.token0_deliminator )
       {
          if( c == parser.token0_deliminator )
@@ -439,40 +440,38 @@ void keyvalues_parse_stream( struct keyvalues *kvs, u32 root_offset, struct stre
             parser.token0_length = 0;
          }
 
-         if( c=='{'||c=='}'||c=='\n' )
+         if( c == '{' )
          {
-            if( c == '{' )
+            struct keyvalue *kv = keyvalues_new( parser.kvs );
+            if( parser.token1_length )
             {
-               struct keyvalue *kv = keyvalues_new( parser.kvs );
-               if( parser.token1_length )
-               {
-                  kv->key_info = (0xFFFFF & parser.token1_hash) | (0x3FF & parser.token1_length) << 20;
-                  kv->key_offset = parser.token1_start_offset;
-               }
-               else
-                  kv->key_info = 5381;
-
-               u32 id = stack_offset( parser.kvs->stack, kv ),
-                   depth = parser.depth;
-
-               parser.depth ++;
-               parser.frame_stack[ parser.depth ].latest_child_offset = 0;
-               parser.frame_stack[ parser.depth ].frame_offset = id;
-               keyvalue_parser_link( &parser, id, depth );
-               parser.token1_length = 0;
+               kv->key_info = (0xFFFFF & parser.token1_hash) | (0x3FF & parser.token1_length) << 20;
+               kv->key_offset = parser.token1_start_offset;
             }
-            else if( c == '}' )
+            else
+               kv->key_info = 5381;
+
+            u32 id = stack_offset( parser.kvs->stack, kv ),
+                depth = parser.depth;
+
+            parser.depth ++;
+            parser.frame_stack[ parser.depth ].latest_child_offset = 0;
+            parser.frame_stack[ parser.depth ].frame_offset = id;
+            keyvalue_parser_link( &parser, id, depth );
+            parser.token1_length = 0;
+         }
+         else if( c == '}' )
+         {
+            if( parser.depth )
+               parser.depth --;
+            else
             {
-               if( parser.depth )
-                  parser.depth --;
-               else
-               {
-                  /* We specify that a depth of -1 by way of closing bracket is just an END marker and not an error */
-                  return;
-               }
-               parser.token1_length = 0;
+               /* We specify that a depth of -1 by way of closing bracket is just an END marker and not an error */
+               return;
             }
+            parser.token1_length = 0;
          }
+
          parser.token0_deliminator = 0;
       }
       else
@@ -506,6 +505,9 @@ void keyvalues_parse_stream( struct keyvalues *kvs, u32 root_offset, struct stre
             }
          }
       }
+
+      if( c == '\0' )
+         break;
    }
 } 
 
index 8d0c6e0b42eddac5a7d17086ad2ef89b7629d67b..860116549482efcd7c872cab0f9b9d8244b41cda 100644 (file)
@@ -13,6 +13,12 @@ u32 _option_optimise = 0;
 struct stream _assemble_file;
 struct keyvalues _assembly;
 
+b8 _option_codegen_shaders = 0;
+b8 _option_codegen_inputs  = 0;
+b8 _option_codegen_console = 0;
+b8 _option_codegen_threads = 0;
+b8 _option_codegen_entities= 0;
+
 enum libc_version 
 {
    k_libc_version_native = 0,
@@ -58,13 +64,6 @@ const c8 *platform_names[] =
    [k_platform_web]         = "web"
 };
 
-struct
-{
-   struct stream name, tripple, folder;
-   const c8 *enabled_features[ 64 ];
-}
-static _metacompiler;
-
 struct shell_command
 {
    u32 temp_frame;
@@ -87,27 +86,79 @@ void _shell_run( struct shell_command *command )
    _end_temporary_frame( command->temp_frame );
 }
 
-u32 _assembly_kv_file_context( u32 kv )
+struct
+{
+   struct stream name, tripple, folder;
+   const c8 *enabled_features[ 64 ];
+}
+static _metacompiler;
+
+struct stack_allocator _source_text;
+struct file_context
 {
-   u32 vgc_block = keyvalues_get( &_assembly, 0, "vgc", 0 );
-   ASSERT_CRITICAL( vgc_block );
+   const c8 *full_path;
+   u32 assembly_kv_start, assembly_kv_end;
+};
 
-   u32 narrowest = 0;
-   u32 it = 0;
-   while( keyvalues_foreach( &_assembly, &it, vgc_block, "file_context" ) )
+struct stretchy_allocator _files;
+
+struct file_context *_assembly_kv_file_context( u32 kv )
+{
+   i32 narrowest = -1;
+   for( i32 i=0; i<stretchy_count(&_files); i ++ )
    {
-      u32 min = 0, max = 0;
-      keyvalues_read_u32s( &_assembly, it, "kv_start", NULL, &min, 1 );
-      keyvalues_read_u32s( &_assembly, it, "kv_end", NULL, &max, 1 );
+      struct file_context *file = stretchy_get( &_files, i );
+      if( file->assembly_kv_start && (kv >= file->assembly_kv_start) && (kv < file->assembly_kv_end) )
+         narrowest = i;
+   }
+
+   ASSERT_CRITICAL( narrowest != -1 );
+   return stretchy_get( &_files, narrowest );
+}
+
+struct file_context *_create_file_context( const c8 *path )
+{
+   c8 *full_path = realpath( path, NULL );
+
+   /* Add it */
+   struct file_context *file_context = stretchy_append( &_files );
+   u32 path_length = buffer_last_index(full_path,0,0);
+   c8 *dest = stack_allocate( &_source_text, path_length+1, 8, "" );
+   buffer_copy( full_path, 0, dest, path_length+1 );
+   file_context->full_path = dest;
+   file_context->assembly_kv_start = 0;
+   file_context->assembly_kv_end = 0;
+   free( full_path );
+   return file_context;
+}
+
+/* This is for getting a path for external tools */
+void _cannonicalize( struct file_context *file_context, const c8 *path, struct stream *dest_string )
+{
+   c8 path_buf[4096];
+   struct stream cannon_path;
+   stream_open_buffer_write( &cannon_path, path_buf, sizeof(path_buf), k_stream_null_terminate );
 
-      if( (kv >= min) && (kv <= max) )
-         narrowest = it;
+   if( path[0] != '/' )
+   {
+      i32 context_path_folder_seperator = buffer_last_index( file_context->full_path, '/', 0 );
+      ASSERT_CRITICAL( context_path_folder_seperator > -1 );
+      string_append( &cannon_path, file_context->full_path, context_path_folder_seperator );
+      string_append( &cannon_path, "/", 0 );
+   }
+   string_append( &cannon_path, path, 0 );
+   c8 *source_file_path = realpath( string_get( &cannon_path ), NULL );
+   if( !source_file_path )
+   {
+      $log( $error, {string_get(&cannon_path)}, {"... "}, $errno() );
+      ASSERT_CRITICAL( source_file_path );
    }
-   return narrowest;
+   string_append( dest_string, source_file_path, 0 );
+   free( source_file_path );
 }
 
-void _assemble_kv_file( const c8 *path, u32 assembly_block, u32 feature_count );
-void _parse_kv_block( struct keyvalues *kvs, u32 block, u32 file_context, u32 assembly_block, u32 feature_count )
+void _assemble_unit_file( const c8 *path, u32 assembly_block, u32 feature_count );
+void _parse_kv_block( struct keyvalues *kvs, u32 block, struct file_context *file_context, u32 assembly_block, u32 feature_count )
 {
    u32 it = 0;
    while( keyvalues_foreach( kvs, &it, block, NULL ) )
@@ -150,12 +201,8 @@ void _parse_kv_block( struct keyvalues *kvs, u32 block, u32 file_context, u32 as
             {
                struct stream path_string;
                stream_open_stack( &path_string, _temporary_stack_allocator(), k_stream_null_terminate );
-   
-               const c8 *folder = keyvalues_read_string( &_assembly, file_context, "folder", NULL );
-               ASSERT_CRITICAL( folder );
-
-               $v_string( &path_string, {folder}, {"/"}, {it_v} );
-               _assemble_kv_file( string_get( &path_string ), assembly_block, feature_count );
+               _cannonicalize( file_context, it_v, &path_string );
+               _assemble_unit_file( string_get( &path_string ), assembly_block, feature_count );
             }
             _end_temporary_frame( temp_frame );
          }
@@ -167,38 +214,19 @@ void _parse_kv_block( struct keyvalues *kvs, u32 block, u32 file_context, u32 as
    }
 }
 
-void _assemble_kv_file( const c8 *path, u32 assembly_block, u32 feature_count )
+void _assemble_unit_file( const c8 *path, u32 assembly_block, u32 feature_count )
 {
-   /* Append context block */
-   u32 vgc_block = keyvalues_get( &_assembly, 0, "vgc", 0 );
-   ASSERT_CRITICAL( vgc_block );
-   u32 context_block = keyvalues_append_frame( &_assembly, vgc_block, "file_context" );
-
-   u32 start_offset = keyvalues_current_offset( &_assembly );
-
    u32 temp_frame = _start_temporary_frame();
-   c8 *folder = realpath( path, NULL );
-   if( !folder )
    {
-      $log( $fatal, {"'"}, {path}, {"' "}, $errno() );
-      _fatal_exit();
-   }
-
-   i32 s = buffer_last_index( folder, '/', 0 );
-   if( s != -1 )
-      folder[s] = '\0';
+      struct file_context *file_context = _create_file_context( path );
+      file_context->assembly_kv_start = keyvalues_current_offset( &_assembly );
 
-   keyvalues_append_string( &_assembly, context_block, "folder", folder );
-   free( folder );
+      struct keyvalues kvs;
+      ASSERT_CRITICAL( keyvalues_read_file( &kvs, path, _temporary_stack_allocator() ) );
+      _parse_kv_block( &kvs, 0, file_context, assembly_block, feature_count );
 
-   /* read file and pre-process it into the assembly */
-   struct keyvalues kvs;
-   ASSERT_CRITICAL( keyvalues_read_file( &kvs, path, _temporary_stack_allocator() ) );
-   _parse_kv_block( &kvs, 0, context_block, assembly_block, feature_count );
-
-   u32 end_offset = keyvalues_current_offset( &_assembly );
-   keyvalues_append_u32s( &_assembly, context_block, "kv_start", &start_offset, 1 );
-   keyvalues_append_u32s( &_assembly, context_block, "kv_end", &end_offset, 1 );
+      file_context->assembly_kv_end = keyvalues_current_offset( &_assembly );
+   }
    _end_temporary_frame( temp_frame );
 }
 
@@ -254,6 +282,14 @@ void _metacompiler_options(void)
    if( _option_flag( 'O', "Run optimisers" ) )
       _option_optimise = 3;
 
+   b8 all = 0;
+   if( _option_long( "meta-all",      "All metadata programs" ) ) all = 1;
+   if( _option_long( "meta-shaders",  "Make shader headers" ) || all ) _option_codegen_shaders = 1;
+   if( _option_long( "meta-inputs",   "Make input headers" )  || all ) _option_codegen_inputs = 1;
+   if( _option_long( "meta-console",  "Make console headers" )|| all ) _option_codegen_console = 1;
+   if( _option_long( "meta-threads",  "Make thread headers" ) || all ) _option_codegen_threads = 1;
+   if( _option_long( "meta-entities", "Make entity headers" ) || all ) _option_codegen_entities = 1;
+
    if( (arg = _option_argument( 'E', "Assemble target KV's only, and output it to file" )) )
    {
       _option_assemble_only = 1;
@@ -261,7 +297,6 @@ void _metacompiler_options(void)
    }
    
    _option_file_path = _option();
-   ASSERT_CRITICAL( _option_file_path );
 }
 
 void (*_event_OPTIONS_subscribers[])( void ) = 
@@ -280,60 +315,6 @@ void index_sort( struct sort_index *indices, u32 indice_count )
    qsort( indices, indice_count, sizeof(struct sort_index), compar );
 }
 
-void _codegen_hooks(void)
-{
-   $log( $info, {"[CODEGEN] hooks.h/hooks.c"} );
-   struct stream hooks_c, hooks_h;
-   stream_open_file( &hooks_h, "generated/hooks.h", k_stream_write );
-   stream_open_file( &hooks_c, "generated/hooks.c", k_stream_write );
-
-   u32 it = 0;
-   while( keyvalues_foreach( &_assembly, &it, 0, "event" ) )
-   {
-      const c8 *name = keyvalues_read_string( &_assembly, it, "name", NULL );
-      const c8 *prototype = keyvalues_read_string( &_assembly, it, "prototype", NULL );
-      const c8 *returns = keyvalues_read_string( &_assembly, it, "returns", "void" );
-      ASSERT_CRITICAL( name && prototype && returns );
-
-      $v_string( &hooks_h, {"extern "}, {returns}, {" (*_event_"}, {name}, {"_subscribers[])( "}, {prototype}, {" );\n"} );
-      
-      struct sort_index indices[ 128 ];
-      u32 indice_count = 0;
-      
-      u32 user_it = 0;
-      while( keyvalues_foreach( &_assembly, &user_it, 0, "hook" ) )
-      {
-         if( compare_buffers( keyvalues_read_string( &_assembly, user_it, "event", NULL ), 0, name, 0 ) )
-         {
-            i32 affinity = 0;
-            keyvalues_read_i32s( &_assembly, user_it, "affinity", NULL, &affinity, 1 );
-
-            ASSERT_CRITICAL( indice_count < ARRAY_COUNT( indices ) );
-            indices[ indice_count ].index = user_it;
-            indices[ indice_count ++ ].value = affinity;
-
-            const c8 *function = keyvalues_read_string( &_assembly, user_it, "function", NULL );
-            ASSERT_CRITICAL( function );
-            $v_string( &hooks_c, {returns}, {" "}, {function}, {"( "}, {prototype}, {" );\n"} );
-         }
-      }
-
-      index_sort( indices, indice_count );
-
-      $v_string( &hooks_c, {returns}, {" (*_event_"}, {name}, {"_subscribers[])( "}, {prototype}, {" ) = \n{\n"} );
-      for( u32 i=0; i<indice_count; i ++ )
-      {
-         const c8 *function = keyvalues_read_string( &_assembly, indices[i].index, "function", NULL );
-         ASSERT_CRITICAL( function );
-         $v_string( &hooks_c, {"   "}, {function}, {",\n"} );
-      }
-      $v_string( &hooks_c, {"   NULL\n};\n\n"} );
-   }
-
-   stream_close( &hooks_h );
-   stream_close( &hooks_c );
-}
-
 void _codegen_shaders(void)
 {
    $log( $info, {"[CODEGEN] shaders.h/shaders.c"} );
@@ -412,9 +393,9 @@ void _codegen_shaders(void)
             ASSERT_CRITICAL( source_count < ARRAY_COUNT( sources ) );
 
             const c8 *path = keyvalues_value( &_assembly, source_it, NULL );
-            u32 context_block = _assembly_kv_file_context( source_it );
-            $v_string( &C, {"               \""}, {keyvalues_read_string( &_assembly, context_block, "folder", NULL )}, 
-                           {"/"}, {path}, {"\",\n"} );
+            $v_string( &C, {"               \""} );
+            _cannonicalize( _assembly_kv_file_context( source_it ), path, &C );
+            $v_string( &C, {"\",\n"} );
 
             sources[ source_count ++ ] = source_it;
          }
@@ -425,22 +406,23 @@ void _codegen_shaders(void)
 
          for( u32 i=0; i<source_count; i ++ )
          {
-            u32 context_block = _assembly_kv_file_context( sources[i] );
             u32 temp_frame = _start_temporary_frame();
             {
                struct stream path_string;
                stream_open_stack( &path_string, _temporary_stack_allocator(), k_stream_null_terminate );
-               $v_string( &path_string, {keyvalues_read_string( &_assembly, context_block, "folder", NULL )}, {"/"}
-                                        {keyvalues_value( &_assembly, sources[i], NULL )} );
+               _cannonicalize( _assembly_kv_file_context( sources[i] )
+                               keyvalues_value( &_assembly, sources[i], NULL ), &path_string );
 
                struct stream source_file;
                ASSERT_CRITICAL( stream_open_file( &source_file, string_get( &path_string ), k_stream_read ) );
                u32 l;
                const c8 *source = stream_read_all( &source_file, _temporary_stack_allocator(), 4, &l );
+               stream_close( &source_file );
+
                for( u32 k=0; k<l; k ++ )
                {
                   c8 c = source[k];
-                  if( c == '\n' ) $v_string( &C, {"\\n"} );
+                  if( c == '\n' ) string_append( &C, "\\n", 0 );
                   else            string_append_c8( &C, c );
                }
             }
@@ -663,6 +645,240 @@ void _codegen_threads(void)
    stream_close( &C );
 }
 
+void _codegen_entities(void)
+{
+   $log( $info, {"[CODEGEN] entities.h/entities.c/entities.py"} );
+   struct stream C, H, PY;
+   stream_open_file( &H, "generated/entities.h", k_stream_write );
+   stream_open_file( &C, "generated/entities.c", k_stream_write );
+   stream_open_file( &PY, "generated/entities.py", k_stream_write );
+
+   struct entity
+   {
+      u32 block;
+      const c8 *name;
+      u32 id;
+
+      u16 function_start, function_count;
+   }
+   entities[256];
+   u32 entity_count = 0;
+
+   struct sort_index entity_ids[256];
+
+   u32 entity_it = 0;
+   while( keyvalues_foreach( &_assembly, &entity_it, 0, "entity" ) )
+   {
+      ASSERT_CRITICAL( entity_count < ARRAY_COUNT(entities) );
+
+      entity_ids[ entity_count ].index = entity_count;
+      entities[ entity_count ].block = entity_it;
+      entities[ entity_count ].name = keyvalues_read_string( &_assembly, entity_it, "name", NULL );
+      entities[ entity_count ].function_start = 0;
+      entities[ entity_count ].function_count = 0;
+
+      ASSERT_CRITICAL( entities[ entity_count ].name );
+
+      i32 id;
+      keyvalues_read_i32s( &_assembly, entity_it, "id", (i32[]){-1}, &id, 1 );
+      ASSERT_CRITICAL( (id > 0) && (id < 0xffff) );
+      entity_ids[ entity_count ].value = id;
+
+      for( u32 i=0; i<entity_count; i ++ )
+         ASSERT_CRITICAL( entity_ids[i].value != entity_ids[entity_count].value );
+
+      entity_count ++;
+   }
+
+   index_sort( entity_ids, entity_count );
+
+   $v_string( &H, {"enum entity_type\n{\n   k_ent_none = 0,\n"} );
+   for( u32 i=0; i<entity_count; i ++ )
+      $v_string( &H, {"   k_"}, {entities[entity_ids[i].index].name}, {" = "}, $signed(entity_ids[i].value), {",\n"} );
+   $v_string( &H, {"};\n"} );
+
+   u32 function_count = 0;
+
+   $v_string( &H, {"/* serialized entity structures */\n"} );
+   for( u32 i=0; i<entity_count; i ++ )
+   {
+      struct entity *ent = &entities[ entity_ids[i].index ];
+      ent->function_start = function_count;
+
+      if( !keyvalues_get( &_assembly, ent->block, "parameter", 0 ) )
+      {
+         $log( $warning, {"Entity '"}, {ent->name}, {"' has no parameters. Structure wont be created!"} );
+      }
+      else
+      {
+         /* Parameter flags */
+         {
+            u32 parameter_it = 0;
+            while( keyvalues_foreach( &_assembly, &parameter_it, ent->block, "parameter" ) )
+            {
+               if( keyvalues_get( &_assembly, parameter_it, "flag", 0 ) )
+               {
+                  const c8 *param_name = keyvalues_read_string( &_assembly, parameter_it, "name", NULL );
+                  ASSERT_CRITICAL( param_name );
+                  $v_string( &H, {"enum "}, {ent->name}, {"_"}, {param_name}, {"\n{\n"} );
+                  u32 flag_it = 0;
+                  while( keyvalues_foreach( &_assembly, &flag_it, parameter_it, "flag" ) )
+                  {
+                     const c8 *flag_name = keyvalues_read_string( &_assembly, flag_it, "name", NULL );
+                     ASSERT_CRITICAL( flag_name );
+                     const c8 *value = keyvalues_read_string( &_assembly, flag_it, "value", NULL );
+                     ASSERT_CRITICAL( value );
+                     $v_string( &H, {"   k_"}, {ent->name}, {"_"}, {param_name}, {"_"}, {flag_name}, {" = "}, {value}, {",\n"} );
+                  }
+                  $v_string( &H, {"};\n"} );
+               }
+            }
+         }
+         
+         /* structure definition */
+         {
+            $v_string( &H, {"struct "}, {ent->name}, {"\n{\n"} );
+            u32 parameter_it = 0;
+            while( keyvalues_foreach( &_assembly, &parameter_it, ent->block, "parameter" ) )
+            {
+               const c8 *name = keyvalues_read_string( &_assembly, parameter_it, "name", NULL );
+               ASSERT_CRITICAL( name );
+
+               const c8 *type = keyvalues_read_string( &_assembly, parameter_it, "type", NULL );
+               ASSERT_CRITICAL( type );
+               struct type_trans
+               {
+                  const c8 *alias,
+                           *c_type,
+                           *c_post,
+                           *py_type;
+               }
+               const types[] = 
+               {
+                  { "transform", "struct mdl_transform", "",    "mdl_transform" },
+                  { "pstr",      "u32",                  "",    "c_uint32" },
+                  { "vec2",      "f32",                  "[2]", "c_float*2" },
+                  { "vec3",      "f32",                  "[3]", "c_float*3" },
+                  { "vec4",      "f32",                  "[4]", "c_float*4" },
+                  { "quat",      "f32",                  "[4]", "c_float*4" },
+                  { "bbx",       "f32",                  "[2][3]","(c_float*3)*2" },
+                  { "mat3x3",    "f32",                  "[3][3]","(c_float*3)*3" },
+                  { "mat4x3",    "f32",                  "[4][3]","(c_float*4)*3" },
+                  { "mat4x4",    "f32",                  "[4][4]","(c_float*4)*4" },
+
+                  { "u8",        "u8",                   "","c_uint8" },
+                  { "u16",       "u16",                  "","c_uint16" },
+                  { "u32",       "u32",                  "","c_uint32" },
+                  { "u64",       "u64",                  "","c_uint64" },
+                  { "i8",        "i8",                   "","c_int8" },
+                  { "i16",       "i16",                  "","c_int16" },
+                  { "i32",       "i32",                  "","c_int32" },
+                  { "i64",       "i64",                  "","c_int64" },
+                  
+                  { "f32",       "f32",                  "","c_float" },
+                  { "f64",       "f64",                  "","c_double" },
+               };
+
+               i32 type_index = -1;
+               for( i32 i=0; i<ARRAY_COUNT( types ); i ++ )
+               {
+                  if( compare_buffers( types[i].alias, 0, type, 0 ) )
+                  {
+                     type_index = i;
+                     break;
+                  }
+               }
+
+               ASSERT_CRITICAL( type_index != -1 );
+
+               $v_string( &H, {"   "}, {types[type_index].c_type}, {" "}, {name}, {types[type_index].c_post}, {";\n"} );
+               $v_string( &PY, {"   (\""}, {name}, {"\","}, {types[type_index].py_type}, {"),\n"} );
+            }
+            $v_string( &H, {"};\n"} );
+         }
+      }
+
+      u32 function_it = 0;
+      while( keyvalues_foreach( &_assembly, &function_it, ent->block, "function" ) )
+      {
+         const c8 *name = keyvalues_read_string( &_assembly, function_it, "name", NULL );
+         ASSERT_CRITICAL( name );
+         $v_string( &C, {"enum entity_event_result "}, {ent->name}, {"_event_"}, {name}, {"( struct ent_event *event );\n"} );
+         function_count ++;
+         ent->function_count ++;
+      }
+   }
+
+   /* entity info structures */
+   $v_string( &C, {"/* Enitity information */\n"} );
+   $v_string( &C, {"struct entity_info _entity_infos[] = {\n"} );
+   for( u32 i=0; i<entity_count; i ++ )
+   {
+      struct entity *ent = &entities[ entity_ids[i].index ];
+      i32 ent_id = entity_ids[i].value;
+      $v_string( &C, {"   { .id = "}, $signed(ent_id), {", .name = \""}, {ent->name}, {"\", .function_start="}, 
+                  $unsigned( ent->function_start ), {", .function_count = "}, 
+                  $unsigned( ent->function_count ), {" },\n"} );
+   }
+   $v_string( &C, {"};\n"} );
+
+
+   /* function info structures (indexed by entity structures) */
+   $v_string( &C, {"/* Function information */\n"} );
+   $v_string( &C, {"struct entity_function_info _entity_function_infos[] = {\n"} );
+   for( u32 i=0; i<entity_count; i ++ )
+   {
+      struct entity *ent = &entities[ entity_ids[i].index ];
+      u32 function_it = 0;
+      while( keyvalues_foreach( &_assembly, &function_it, ent->block, "function" ) )
+      {
+         const c8 *name = keyvalues_read_string( &_assembly, function_it, "name", NULL );
+         ASSERT_CRITICAL( name );
+         $v_string( &C, {"   { .name = \""}, {name}, {"\" },\n"} );
+      }
+   }
+   $v_string( &C, {"};\n"} );
+
+   /* Entity info, and function to get it by entity ID */
+   $v_string( &C, {"struct entity_info *_vg_entity_info( u16 entity_type )\n{\n"} );
+   for( u32 i=0; i<entity_count; i ++ )
+   {
+      struct entity *ent = &entities[ entity_ids[i].index ];
+      i32 ent_id = entity_ids[i].value;
+      $v_string( &C, {"   if( entity_type == "}, $signed(ent_id), {" ) return &_entity_infos["}, $unsigned(i), {"];\n"} );
+   }
+   $v_string( &C, {"   ASSERT_CRITICAL(0); return NULL;\n"} );
+   $v_string( &C, {"}\n"} );
+
+   /* Entity functions, and dispatch */
+   $v_string( &C, {"/* Enitity functions */\n"} );
+   $v_string( &C, {"enum entity_event_result _vg_entity_event_dispatch( struct ent_event *event )\n{\n"} );
+   for( u32 i=0; i<entity_count; i ++ )
+   {
+      struct entity *ent = &entities[ entity_ids[i].index ];
+      i32 ent_id = entity_ids[i].value;
+
+      $v_string( &C, {"   if( event->reciever.type == "}, $signed(ent_id), {" ){\n"} );
+      u32 function_it = 0;
+      u32 j = 0;
+      while( keyvalues_foreach( &_assembly, &function_it, ent->block, "function" ) )
+      {
+         const c8 *name = keyvalues_read_string( &_assembly, function_it, "name", NULL );
+         ASSERT_CRITICAL( name );
+         $v_string( &C, {"      if( event->function_index == "}, $unsigned(j), {" ) return "}, 
+                        {ent->name}, {"_event_"}, {name}, {"( event );\n"} );
+         j ++;
+      }
+      $v_string( &C, {"   }\n"} );
+   }
+   $v_string( &C, {"   return k_entity_event_result_invalid;\n"} );
+   $v_string( &C, {"}\n"} );
+
+   stream_close( &H );
+   stream_close( &C );
+   stream_close( &PY );
+}
+
 void _default_config_gen(void)
 {
    $log( $info, {"[CODEGEN] default.cfg"} );
@@ -691,13 +907,19 @@ void _default_config_gen(void)
 
 i32 main( i32 argc, const c8 *argv[] )
 {
-   VG_PRE_MAIN;
+   _exit_init();
+   _log_init();
+   _options_init( argc, argv );
+   _metacompiler_options();
+   _options_check_end();
+   ASSERT_CRITICAL( _option_file_path );
 
    /* Get name of target and do a few checks */
    i32 name_start = buffer_last_index( _option_file_path, '/', 0 );
    if( name_start == -1 ) name_start = 0;
    i32 name_end = buffer_last_index( _option_file_path+name_start, '.', 0 );
 
+#if 0
    b8 valid_kv = 1;
    if( name_end == -1 )
       valid_kv = 0;
@@ -709,6 +931,7 @@ i32 main( i32 argc, const c8 *argv[] )
       $log( $fatal, {"Target file must be a text KV file"} );
       _fatal_exit();
    }
+#endif
 
    struct stream open_test;
    if( stream_open_file( &open_test, _option_file_path, k_stream_read ) )
@@ -763,6 +986,9 @@ i32 main( i32 argc, const c8 *argv[] )
    stack_init( &stack, NULL, BYTES_MB(16), "Assembly KV" );
    keyvalues_init( &_assembly, &stack );
 
+   stack_init( &_source_text, NULL, BYTES_MB(32), "Source text" );
+   stretchy_init( &_files, sizeof(struct file_context) );
+
    u32 info_block = keyvalues_append_frame( &_assembly, 0, "vgc" );
    keyvalues_append_string( &_assembly, info_block, "version", "0.2" );
    keyvalues_append_string( &_assembly, info_block, "name", string_get( &_metacompiler.name ) );
@@ -775,7 +1001,7 @@ i32 main( i32 argc, const c8 *argv[] )
    if( _option_platform == k_platform_windows ) _metacompiler.enabled_features[start_features ++] = "windows";
 
    if( _option_platform == k_platform_web   )   _metacompiler.enabled_features[start_features ++] = "web";
-   _assemble_kv_file( _option_file_path, 0, start_features );
+   _assemble_unit_file( _option_file_path, 0, start_features );
    
    if( _option_assemble_only )
    {
@@ -796,11 +1022,11 @@ i32 main( i32 argc, const c8 *argv[] )
       _shell_run( cmd );
    }
 
-   _codegen_hooks();
-   _codegen_shaders();
-   _codegen_inputs();
-   _codegen_console();
-   _codegen_threads();
+   if( _option_codegen_shaders ) _codegen_shaders();
+   if( _option_codegen_inputs )  _codegen_inputs();
+   if( _option_codegen_console ) _codegen_console();
+   if( _option_codegen_threads ) _codegen_threads();
+   if( _option_codegen_entities )_codegen_entities();
    
    {
       struct shell_command *cmd = _shell_command();
@@ -888,12 +1114,11 @@ i32 main( i32 argc, const c8 *argv[] )
 
          if( include || link_path || add )
          {
-            u32 context_block = _assembly_kv_file_context( compile_it );
             if( include )   $v_string( &cmd->line, {"     -I"} );
             if( link_path ) $v_string( &cmd->line, {"     -L"} );
             if( add )       $v_string( &cmd->line, {"      "} );
-            $v_string( &cmd->line, {keyvalues_read_string( &_assembly, context_block, "folder", NULL )}, 
-                                   {"/"}, {keyvalues_value( &_assembly, compile_it, NULL )}, {" \\\n"} );
+            _cannonicalize( _assembly_kv_file_context( compile_it ), keyvalues_value( &_assembly, compile_it, NULL ), &cmd->line );
+            string_append( &cmd->line, " \\\n", 0 );
          }
          else if( define || link )
          {