+++ /dev/null
-#include "foundation.h"
-#include "array_file.h"
-
-u32 af_str_hash( const void *packed_strings, u32 pstr )
-{
- if( pstr & 0x3 )
- {
- $log( $fatal, {"ALIGNMENT ERROR, PSTR INDEX: "}, $unsigned(pstr) );
- }
- return *((u32 *)(packed_strings + pstr));
-}
-
-const c8 *af_str( const void *packed_strings, u32 pstr )
-{
- return packed_strings + pstr + 4;
-}
-
-b8 af_str_eq( const void *packed_strings, u32 pstr, const c8 *str, u32 str_hash )
-{
- if( af_str_hash( packed_strings, pstr ) == str_hash )
- if( compare_buffers( str, 0, af_str( packed_strings, pstr ), 0 ))
- return 1;
- return 0;
-}
-
-void af_load_array_file_buffer( struct array_file_context *ctx, struct array_file_meta *arr, void *buffer, u32 stride )
-{
-#if 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;
-}
+++ /dev/null
-#pragma once
-
-struct array_file_ptr
-{
- void *data;
- u32 count, stride;
-};
-
-struct array_file_meta
-{
- u32 file_offset,
- item_count,
- item_size;
-
- c8 name[16];
-};
-
-struct array_file_header
-{
- u32 version;
- struct array_file_meta index;
-};
-
-struct array_file_context
-{
- struct stream *stream;
- struct array_file_header header;
- struct array_file_ptr index;
-};
-
-/* array loading */
-struct array_file_meta *af_find_array( struct array_file_context *ctx, const c8 *name );
-
-void af_load_array_file( struct array_file_context *ctx, struct array_file_ptr *out_ptr,
- struct array_file_meta *arr, struct stack_allocator *stack, u32 stride );
-
-b8 af_load_array( struct array_file_context *ctx, struct array_file_ptr *ptr, const c8 *name,
- struct stack_allocator *stack, u32 stride );
-void af_load_array_file_buffer( struct array_file_context *ctx, struct array_file_meta *arr, void *buffer, u32 stride );
-
-/* array access */
-void *af_arritm( struct array_file_ptr *arr, u32 index );
-u32 af_arrcount( struct array_file_ptr *arr );
-
-/* packed string buffer access (with djb2 hash prefix) */
-const c8 *af_str( const void *packed_strings, u32 pstr );
-u32 af_str_hash( const void *packed_strings, u32 pstr );
-b8 af_str_eq( const void *packed_strings, u32 pstr, const char *str, u32 str_hash );
-
-#define AF_STR_EQ( 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 );
+++ /dev/null
-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;
-}
--- /dev/null
+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;
+}
+++ /dev/null
-#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';
- }
-}
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
}
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
}
}
-hook
-{
- event START
- function "_shader_init"
-}
ccmd
{
name reload_shaders
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
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
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
+ }
+ }
+}
+++ /dev/null
-#include "vg_engine.h"
-#include "metascene.h"
-#include "common_maths.h"
-#include "model.h"
-#include "model_entity.h"
-#include "shader_props.h"
-#include "array_file.h"
-#include "vg_async.h"
-
-void metascene_load( struct metascene *ms, const c8 *path, struct stack_allocator *stack )
-{
- ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) );
- zero_buffer( ms, sizeof(struct metascene) );
-
- struct stream stream;
- ASSERT_CRITICAL( stream_open_file( &stream, path, k_stream_read ) );
-
- u32 temp_frame = _start_temporary_frame();
- {
- struct array_file_context af;
- ASSERT_CRITICAL( af_open_stream( &af, &stream, MS_VERSION_MIN, MS_VERSION_NR, _temporary_stack_allocator() ) );
-
- struct array_file_ptr strings;
- af_load_array( &af, &strings, "strings", stack, 1 );
- ms->packed_strings = strings.data;
- af_load_array( &af, &ms->infos, "ms_scene_info", stack, sizeof(struct ms_scene_info) );
- af_load_array( &af, &ms->instances, "ms_instance", stack, sizeof(struct ms_instance) );
- af_load_array( &af, &ms->overrides, "ms_override", stack, sizeof(struct ms_override) );
- af_load_array( &af, &ms->strips, "ms_strip", stack, sizeof(struct ms_strip) );
- af_load_array( &af, &ms->tracks, "ms_track", stack, sizeof(struct ms_track) );
- af_load_array( &af, &ms->keyframes, "ms_keyframe", stack, sizeof(struct ms_keyframe) );
- af_load_array( &af, &ms->cameras, "ent_camera", stack, sizeof(struct ent_camera) );
- af_load_array( &af, &ms->audios, "ent_audio", stack, sizeof(struct ent_audio) );
- af_load_array( &af, &ms->audio_clips, "ent_audio_clip", stack, sizeof(struct ent_audio_clip) );
- af_load_array( &af, &ms->curves, "ms_curves", stack, sizeof(struct ms_curve_keyframe) );
-
- ASSERT_CRITICAL( af_arrcount( &ms->infos ) );
- struct ms_scene_info *src_inf = af_arritm( &ms->infos, 0 );
- ms->info = *src_inf;
-
- stream_close( &stream );
- }
- _end_temporary_frame( temp_frame );
-}
-
-u32 skeleton_bone_id( struct ms_skeleton *skele, const c8 *name )
-{
- for( u32 i=1; i<skele->bone_count; i++ )
- if( compare_buffers( skele->bones[i].name, 0, name, 0 ) )
- return i;
-
- $log( $fatal, {"skeleton_bone_id( *, "}, {name}, {" ); -> Bone does not exist"} );
- _fatal_exit();
- return 0;
-}
-
-void keyframe_copy_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, i32 num )
-{
- for( i32 i=0; i<num; i++ )
- kfb[i] = kfa[i];
-}
-
-
-/* apply a rotation from the perspective of root */
-void keyframe_rotate_around( struct ms_keyframe *kf, f32 origin[3], f32 offset[3], f32 q[4] )
-{
- f32 v0[3], co[3];
- v3_add( kf->co, offset, co );
- v3_sub( co, origin, v0 );
- q_mulv( q, v0, v0 );
- v3_add( v0, origin, co );
- v3_sub( co, offset, kf->co );
- q_mul( q, kf->q, kf->q );
- q_normalize( kf->q );
-}
-
-void keyframe_lerp( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd )
-{
- v3_lerp( kfa->co, kfb->co, t, kfd->co );
- q_nlerp( kfa->q, kfb->q, t, kfd->q );
- v3_lerp( kfa->s, kfb->s, t, kfd->s );
-}
-
-/*
- * Lerp between two sets of keyframes and store in dest. Rotations use Nlerp.
- */
-void keyframe_lerp_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd, i32 count )
-{
- if( t <= 0.0001f )
- {
- keyframe_copy_pose( kfa, kfd, count );
- return;
- }
- else if( t >= 0.9999f )
- {
- keyframe_copy_pose( kfb, kfd, count );
- return;
- }
-
- for( i32 i=0; i<count; i++ )
- keyframe_lerp( kfa+i, kfb+i, t, kfd+i );
-}
-
-void skeleton_lerp_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd )
-{
- keyframe_lerp_pose( kfa, kfb, t, kfd, skele->bone_count-1 );
-}
-
-void skeleton_copy_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfd )
-{
- keyframe_copy_pose( kfa, kfd, skele->bone_count-1 );
-}
-
-/*
- * Sample animation between 2 closest frames using time value. Output is a
- * keyframe buffer that is allocated with an appropriate size
- *
- * Time is in SECONDS
- */
-void skeleton_sample_anim( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output )
-{
- struct ms_strip *strip = anim->strip;
- f32 animtime = fmodf( time*anim->framerate, (f32)strip->strip.length ),
- animframe = floorf( animtime ),
- t = animtime - animframe;
-
- u32 frame = (u32)animframe % strip->strip.length,
- next = (frame+1) % strip->strip.length;
-
- struct ms_keyframe *base = anim->keyframes_base + strip->strip.count*frame,
- *nbase = anim->keyframes_base + strip->strip.count*next;
- skeleton_lerp_pose( skele, base, nbase, t, output );
-}
-
-/* time is in SECONDS */
-i32 skeleton_sample_anim_clamped( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output )
-{
- struct ms_strip *strip = anim->strip;
- f32 end = (strip->strip.length-1)/anim->framerate;
- skeleton_sample_anim( skele, anim, f32_min( end, time ), output );
- if( time > end ) return 0;
- else return 1;
-}
-
-static i32 should_apply_bone( struct ms_skeleton *skele, u32 id, enum anim_apply type )
-{
- struct ms_skeleton_bone *sb = &skele->bones[ id ],
- *sp = &skele->bones[ sb->parent ];
- if( type == k_anim_apply_defer_ik )
- {
- if( ((sp->flags & k_bone_flag_ik) && !(sb->flags & k_bone_flag_ik)) || sp->defer )
- {
- sb->defer = 1;
- return 0;
- }
- else
- {
- sb->defer = 0;
- return 1;
- }
- }
- else if( type == k_anim_apply_deffered_only )
- {
- if( sb->defer )
- return 1;
- else
- return 0;
- }
-
- return 1;
-}
-
-/*
- * Apply block of keyframes to skeletons final pose
- */
-void skeleton_apply_pose( struct ms_skeleton *skele, struct ms_keyframe *pose, enum anim_apply passtype, f32 final_mtx[][4][3] )
-{
- if( passtype == k_anim_apply_absolute )
- {
- for( u32 i=1; i<skele->bone_count; i++ )
- {
- struct ms_keyframe *kf = &pose[i-1];
- q_m3x3( kf->q, final_mtx[i] );
- m3x3_scale( final_mtx[i], kf->s );
- v3_copy( kf->co, final_mtx[i][3] );
- }
- return;
- }
-
- m4x3_identity( final_mtx[0] );
- skele->bones[0].defer = 0;
- skele->bones[0].flags &= ~k_bone_flag_ik;
-
- for( u32 i=1; i<skele->bone_count; i++ )
- {
- struct ms_skeleton_bone *sb = &skele->bones[i];
- if( !should_apply_bone( skele, i, passtype ) )
- continue;
-
- sb->defer = 0;
-
- /* process pose */
- f32 posemtx[4][3];
- f32 temp_delta[3];
- v3_sub( skele->bones[i].co, skele->bones[sb->parent].co, temp_delta );
-
- /* pose matrix */
- struct ms_keyframe *kf = &pose[i-1];
- q_m3x3( kf->q, posemtx );
- m3x3_scale( posemtx, kf->s );
- v3_copy( kf->co, posemtx[3] );
- v3_add( temp_delta, posemtx[3], posemtx[3] );
-
- /* final matrix */
- m4x3_mul( final_mtx[ sb->parent ], posemtx, final_mtx[i] );
- }
-}
-
-/*
- * Take the final matrices and decompose it into an absolute positioned anim
- */
-void skeleton_decompose_mtx_absolute( struct ms_skeleton *skele, struct ms_keyframe *anim, f32 final_mtx[][4][3] )
-{
- for( u32 i=1; i<skele->bone_count; i++ )
- {
- struct ms_keyframe *kf = &anim[i-1];
- m4x3_decompose( final_mtx[i], kf->co, kf->q, kf->s );
- }
-}
-
-/*
- * creates the reference inverse matrix for an IK bone, as it has an initial
- * intrisic rotation based on the direction that the IK is setup..
- */
-void skeleton_inverse_for_ik( struct ms_skeleton *skele, f32 ivaxis[3], u32 id, f32 inverse[3][3] )
-{
- v3_copy( ivaxis, inverse[0] );
- v3_copy( skele->bones[id].end, inverse[1] );
- v3_normalize( inverse[1] );
- v3_cross( inverse[0], inverse[1], inverse[2] );
- m3x3_transpose( inverse, inverse );
-}
-
-/*
- * Creates inverse rotation matrices which the IK system uses.
- */
-void skeleton_create_inverses( struct ms_skeleton *skele )
-{
- /* IK: inverse 'plane-bone space' axis '(^axis,^bone,...)[base] */
- for( u32 i=0; i<skele->ik_count; i++ )
- {
- struct ms_skeleton_ik *ik = &skele->ik[i];
- f32 iv0[3], iv1[3], ivaxis[3];
- v3_sub( skele->bones[ik->target].co, skele->bones[ik->lower].co, iv0 );
- v3_sub( skele->bones[ik->pole].co, skele->bones[ik->lower].co, iv1 );
- v3_cross( iv0, iv1, ivaxis );
- v3_normalize( ivaxis );
-
- skeleton_inverse_for_ik( skele, ivaxis, ik->lower, ik->ia );
- skeleton_inverse_for_ik( skele, ivaxis, ik->upper, ik->ib );
- }
-}
-
-/*
- * Apply a model matrix to all bones, should be done last
- */
-void skeleton_apply_transform( struct ms_skeleton *skele, f32 transform[4][3], f32 final_mtx[][4][3] )
-{
- for( u32 i=0; i<skele->bone_count; i++ )
- m4x3_mul( transform, final_mtx[i], final_mtx[i] );
-}
-
-/*
- * Apply an inverse matrix to all bones which maps vertices from bind space into
- * bone relative positions
- */
-void skeleton_apply_inverses( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
-{
- for( u32 i=0; i<skele->bone_count; i++ )
- {
- struct ms_skeleton_bone *sb = &skele->bones[i];
- f32 inverse[4][3];
- m3x3_identity( inverse );
- v3_negate( sb->co, inverse[3] );
- m4x3_mul( final_mtx[i], inverse, final_mtx[i] );
- }
-}
-
-/*
- * Apply all IK modifiers (2 bone ik reference from blender is supported)
- */
-void skeleton_apply_ik_pass( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
-{
- for( u32 i=0; i<skele->ik_count; i++ )
- {
- struct ms_skeleton_ik *ik = &skele->ik[i];
- f32 v0[3], /* base -> target */
- v1[3], /* base -> pole */
- vaxis[3];
- f32 co_base[3],
- co_target[3],
- co_pole[3];
-
- v3_copy( final_mtx[ik->lower][3], co_base );
- v3_copy( final_mtx[ik->target][3], co_target );
- v3_copy( final_mtx[ik->pole][3], co_pole );
-
- v3_sub( co_target, co_base, v0 );
- v3_sub( co_pole, co_base, v1 );
- v3_cross( v0, v1, vaxis );
- v3_normalize( vaxis );
- v3_normalize( v0 );
- v3_cross( vaxis, v0, v1 );
-
- /* localize problem into [x:v0,y:v1] 2d plane */
- f32 base[2] = { v3_dot( v0, co_base ), v3_dot( v1, co_base ) },
- end[2] = { v3_dot( v0, co_target ), v3_dot( v1, co_target ) },
- knee[2];
-
- /* Compute angles (basic trig)*/
- f32 delta[2];
- v2_sub( end, base, delta );
-
- f32 l1 = v3_length( skele->bones[ik->lower].end ),
- l2 = v3_length( skele->bones[ik->upper].end ),
- d = f32_clamp( v2_length(delta), fabsf(l1 - l2), l1+l2-0.00001f ),
- c = acosf( (l1*l1 + d*d - l2*l2) / (2.0f*l1*d) ),
- rot = atan2f( delta[1], delta[0] ) + c - VG_PIf/2.0f;
-
- knee[0] = sinf(-rot) * l1;
- knee[1] = cosf(-rot) * l1;
-
- m4x3_identity( final_mtx[ik->lower] );
- m4x3_identity( final_mtx[ik->upper] );
-
- /* create rotation matrix */
- f32 co_knee[3];
- v3_muladds( co_base, v0, knee[0], co_knee );
- v3_muladds( co_knee, v1, knee[1], co_knee );
- // vg_line( co_base, co_knee, 0xff00ff00 );
-
- f32 transform[4][3];
- v3_copy( vaxis, transform[0] );
- v3_muls( v0, knee[0], transform[1] );
- v3_muladds( transform[1], v1, knee[1], transform[1] );
- v3_normalize( transform[1] );
- v3_cross( transform[0], transform[1], transform[2] );
- v3_copy( co_base, transform[3] );
-
- m3x3_mul( transform, ik->ia, transform );
- m4x3_copy( transform, final_mtx[ik->lower] );
-
- /* upper/knee bone */
- v3_copy( vaxis, transform[0] );
- v3_sub( co_target, co_knee, transform[1] );
- v3_normalize( transform[1] );
- v3_cross( transform[0], transform[1], transform[2] );
- v3_copy( co_knee, transform[3] );
-
- m3x3_mul( transform, ik->ib, transform );
- m4x3_copy( transform, final_mtx[ik->upper] );
- }
-}
-
-/*
- * Applies the typical operations that you want for an IK rig:
- * Pose, IK, Pose(deferred), Inverses, Transform
- */
-void skeleton_apply_standard( struct ms_skeleton *skele, struct ms_keyframe *pose, f32 transform[4][3], f32 final_mtx[][4][3] )
-{
- skeleton_apply_pose( skele, pose, k_anim_apply_defer_ik, final_mtx );
- skeleton_apply_ik_pass( skele, final_mtx );
- skeleton_apply_pose( skele, pose, k_anim_apply_deffered_only, final_mtx );
- skeleton_apply_inverses( skele, final_mtx );
- skeleton_apply_transform( skele, transform, final_mtx );
-}
-
-void skeleton_alloc_from( struct ms_skeleton *skele, struct stack_allocator *stack,
- struct vg_model *model, struct mdl_armature *armature )
-{
- skele->bone_count = armature->bone_count+1;
- skele->ik_count = 0;
- skele->collider_count = 0;
-
- for( u32 i=0; i<armature->bone_count; i++ )
- {
- struct mdl_bone *bone = &model->bones[ armature->bone_start+i ];
- if( bone->flags & k_bone_flag_ik )
- skele->ik_count ++;
- if( bone->collider )
- skele->collider_count ++;
- }
-
- u32 bone_size = sizeof(struct ms_skeleton_bone) * skele->bone_count,
- ik_size = sizeof(struct ms_skeleton_ik) * skele->ik_count;
-
- skele->bones = stack_allocate( stack, bone_size, 8, NULL );
- skele->ik = stack_allocate( stack, ik_size, 8, NULL );
-
- zero_buffer( skele->bones, bone_size );
- zero_buffer( skele->ik, ik_size );
-}
-
-/* Setup a skeleton from model. mdl's metadata should stick around */
-void skeleton_setup( struct ms_skeleton *skele, struct vg_model *model, u32 index, struct stack_allocator *stack )
-{
- u32 ik_count = 0, collider_count = 0;
- skele->bone_count = 0;
- skele->bones = NULL;
-
- if( !model->armature_count )
- {
- $log( $fatal, {"No skeleton in model"} );
- _fatal_exit();
- }
-
- struct mdl_armature *armature = &model->armatures[ index ];
- skeleton_alloc_from( skele, stack, model, armature );
-
- for( u32 i=0; i<armature->bone_count; i++ )
- {
- struct mdl_bone *bone = &model->bones[ armature->bone_start+i ];
- struct ms_skeleton_bone *sb = &skele->bones[i+1];
-
- v3_copy( bone->co, sb->co );
- v3_copy( bone->end, sb->end );
-
- sb->parent = bone->parent;
- sb->name = af_str( model->packed_strings, bone->pstr_name );
- sb->flags = bone->flags;
- sb->collider = bone->collider;
- sb->orig_bone = bone;
-
- if( sb->flags & k_bone_flag_ik )
- {
- skele->bones[ sb->parent ].flags |= k_bone_flag_ik;
-
- if( ik_count == skele->ik_count )
- {
- $log( $fatal, {"Too many ik bones, corrupt model file"} );
- _fatal_exit();
- }
-
- struct ms_skeleton_ik *ik = &skele->ik[ ik_count ++ ];
- ik->upper = i+1;
- ik->lower = bone->parent;
- ik->target = bone->ik_target;
- ik->pole = bone->ik_pole;
- }
-
- box_copy( bone->hitbox, sb->hitbox );
- if( bone->collider )
- {
- if( collider_count == skele->collider_count )
- {
- $log( $fatal, {"Too many collider bones"} );
- _fatal_exit();
- }
- collider_count ++;
- }
- }
-
- /* fill in implicit root bone */
- v3_fill( skele->bones[0].co, 0 );
- v3_copy( (f32[3]){0.0f,1.0f,0.0f}, skele->bones[0].end );
- skele->bones[0].parent = 0xffffffff;
- skele->bones[0].flags = 0;
- skele->bones[0].name = "[root]";
-
- skeleton_create_inverses( skele );
- $log( $ok, {"Loaded skeleton with "}, $unsigned( skele->bone_count ), {" bones"} );
- $log( $ok, {" "}, $unsigned( skele->collider_count), {" colliders"} );
-}
-
-#if 0
-void skeleton_debug( struct ms_skeleton *skele, f32 final_mtx[][4][3] )
-{
- for( u32 i=1; i<skele->bone_count; i ++ )
- {
- struct ms_skeleton_bone *sb = &skele->bones[i];
- f32 p0[3], p1[3];
- v3_copy( sb->co, p0 );
- v3_add( p0, sb->end, p1 );
-
- m4x3_mulv( final_mtx[i], p0, p0 );
- m4x3_mulv( final_mtx[i], p1, p1 );
-
- if( sb->flags & k_bone_flag_deform )
- {
- if( sb->flags & k_bone_flag_ik )
- vg_line( p0, p1, 0xff0000ff );
- else
- vg_line( p0, p1, 0xffcccccc );
- }
- else
- vg_line( p0, p1, 0xff00ffff );
- }
-}
-#endif
+++ /dev/null
-#pragma once
-#include "foundation.h"
-#include "array_file.h"
-#include "model.h"
-
-#define MS_VERSION_NR 2
-#define MS_VERSION_MIN 2
-
-struct ms_scene_info
-{
- f32 framerate;
- u32 end_frame;
-};
-
-struct metascene
-{
- const void *packed_strings;
- struct ms_scene_info info;
- struct array_file_ptr infos,
- instances,
- overrides,
- strips,
- tracks,
- keyframes,
- curves,
-
- audios, /* kinda temp? */
- audio_clips,
- cameras;
-};
-
-struct ms_instance
-{
- u32 pstr_name,
- override_start,
- override_count;
-};
-
-struct ms_override
-{
- u32 entity_type, pstr_name;
- struct mdl_transform transform;
-};
-
-enum ms_strip_mode
-{
- k_ms_strip_mode_keyframes = 0x1,
- k_ms_strip_mode_curves = 0x2,
- k_ms_strip_mode_animation = 0x1|0x2,
-
- k_ms_strip_mode_camera = 0x10,
- k_ms_strip_mode_event = 0x20,
- k_ms_strip_mode_subtitle = 0x40,
- k_ms_strip_mode_fadeout = 0x80,
-};
-
-struct ms_strip
-{
- u8 mode;
- i32 offset;
-
- union
- {
- struct
- {
- u32 start, count, length,
- pstr_name,
- pstr_internal_name,
- instance_id, object_id;
- f32 timing_offset;
- }
- strip;
-
- struct
- {
- u32 entity_id;
- }
- camera;
-
- struct
- {
- u32 pstr_en, res0, res1, res2;
- u8 character;
- }
- subtitle;
-
- struct
- {
- u32 pstr_string;
- }
- event;
- };
-};
-
-struct ms_track
-{
- u32 keyframe_start,
- keyframe_count,
- pstr_datapath,
- semantic_type;
-};
-
-struct ms_curve_keyframe
-{
- f32 co[2], l[2], r[2];
-};
-
-struct ms_keyframe
-{
- f32 co[3], s[3];
- f32 q[4];
-};
-
-/* skeletons
- * ------------------------------------------------------------------------------------------------------------------ */
-
-struct ms_skeleton
-{
- struct ms_skeleton_bone
- {
- f32 co[3], end[3];
- u32 parent;
-
- u32 flags;
- int defer;
-
- //ms_keyframe kf;
- struct mdl_bone *orig_bone;
- u32 collider; // TODO: SOA
- f32 hitbox[2][3]; // TODO: SOA
- const char *name; // TODO: SOA
- }
- *bones;
- u32 bone_count;
-
- struct ms_skeleton_ik
- {
- u32 lower, upper, target, pole;
- f32 ia[3][3], ib[3][3];
- }
- *ik;
- u32 ik_count;
- u32 collider_count,
- bindable_count;
-};
-
-void metascene_load( struct metascene *ms, const c8 *path, struct stack_allocator *stack );
-void keyframe_copy_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, i32 num );
-void keyframe_rotate_around( struct ms_keyframe *kf, f32 origin[3], f32 offset[3], f32 q[4] );
-void keyframe_lerp( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd );
-void keyframe_lerp_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd, i32 count );
-void skeleton_alloc_from( struct ms_skeleton *skele, struct stack_allocator *stack,
- struct vg_model *model, struct mdl_armature *armature );
-u32 skeleton_bone_id( struct ms_skeleton *skele, const c8 *name );
-void skeleton_lerp_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd );
-void skeleton_copy_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfd );
-
-struct ms_skeletal_animation
-{
- struct ms_strip *strip;
- struct ms_keyframe *keyframes_base;
- f32 framerate;
-};
-
-void skeleton_sample_anim( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output );
-int skeleton_sample_anim_clamped( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output );
-
-enum anim_apply
-{
- k_anim_apply_always,
- k_anim_apply_defer_ik,
- k_anim_apply_deffered_only,
- k_anim_apply_absolute
-};
-
-void skeleton_apply_pose( struct ms_skeleton *skele, struct ms_keyframe *pose, enum anim_apply passtype, f32 final_mtx[][4][3] );
-void skeleton_decompose_mtx_absolute( struct ms_skeleton *skele, struct ms_keyframe *anim, f32 final_mtx[][4][3] );
-void skeleton_inverse_for_ik( struct ms_skeleton *skele, f32 ivaxis[3], u32 id, f32 inverse[3][3] );
-void skeleton_create_inverses( struct ms_skeleton *skele );
-void skeleton_apply_transform( struct ms_skeleton *skele, f32 transform[4][3], f32 final_mtx[][4][3] );
-void skeleton_apply_inverses( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
-void skeleton_apply_ik_pass( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
-void skeleton_apply_standard( struct ms_skeleton *skele, struct ms_keyframe *pose, f32 transform[4][3], f32 final_mtx[][4][3] );
-void skeleton_setup( struct ms_skeleton *skele, struct vg_model *model, u32 index, struct stack_allocator *stack );
-void skeleton_debug( struct ms_skeleton *skele, f32 final_mtx[][4][3] );
+++ /dev/null
-#include "foundation.h"
-#include "common_maths.h"
-#include "vg_async.h"
-
-#include "model.h"
-#include "array_file.h"
-#include "vg_opengl.h"
-#include "shader_props.h"
-#include <stddef.h>
-
-struct stream *vg_model_stream_pack_stream( struct vg_model_stream_context *ctx, struct mdl_file *file )
-{
- if( !file->pack_size )
- {
- $log( $fatal, {"Packed file is only a header; it is not packed\n"},
- {"Path: "}, {af_str( &ctx->model->packed_strings, file->pstr_path )} );
- _fatal_exit();
- }
-
- stream_seek( &ctx->stream, ctx->model->pack_base_offset + file->pack_offset );
-
- // WE dont ever read backwards so this just sets us up an uppper bound to read against
- ctx->stream.buffer_length = ctx->model->pack_base_offset + file->pack_offset + file->pack_size;
- return &ctx->stream;
-}
-
-/* This also compiles them */
-static void vg_model_stream_materials( struct vg_model_stream_context *ctx, struct stack_allocator *stack )
-{
- struct array_file_ptr mats_ptr;
- af_load_array( &ctx->af, &mats_ptr, "mdl_material", stack, sizeof(struct mdl_material) );
- ctx->model->materials = mats_ptr.data;
- ctx->model->material_count = mats_ptr.count;
-
- u32 size = sizeof(union shader_props) * mats_ptr.count;
- ctx->model->shader_props = stack_allocate( stack, size, 8, "Compiled shader properties" );
-
- /*
- * Step 0:
- * Acquiring the data source
- *
- * Step 1:
- * Converting into formal KV structure
- * We have 3 different modes;
- * v101+: old simple binary structures, requires 'generating' correct kvs
- * v106+: deprecated 'vg_msg' similar to kvs, only requires conversion
- * v110+: text KV's, direct parsing into vg_kvs
- *
- * Step 2:
- * Formal KV structure is then compiled into the binary union
- */
-
- /* step0 ----------------------------- */
- u32 temp_frame = _start_temporary_frame();
- {
-#if (VG_MODEL_VERSION_MIN <= 101)
- struct array_file_ptr v101_materials;
-#endif
-#if (VG_MODEL_VERSION_MIN <= 106)
- struct array_file_ptr v106_data;
-#endif
- struct array_file_ptr v110_data;
-
- if( ctx->model->version <= 105 )
-#if (VG_MODEL_VERSION_MIN <= 105)
- af_load_array( &ctx->af, &v101_materials, "mdl_material", _temporary_stack_allocator(), sizeof(struct mdl_material_v101) );
-#else
- {
- $log( $fatal, {"Unsupported model version: "}, $unsigned( ctx->model->version ) );
- }
-#endif
-#if (VG_MODEL_VERSION_MIN <= 109)
- else if( ctx->model->version <= 109 )
- af_load_array( &ctx->af, &v106_data, "shader_data", _temporary_stack_allocator(), 1 );
-#endif
- else
- af_load_array( &ctx->af, &v110_data, "shader_props", _temporary_stack_allocator(), 1 );
-
- struct keyvalues kvs;
- keyvalues_init( &kvs, _temporary_stack_allocator() );
-
- /* step1 ----------------------------- */
- if( ctx->model->version <= 105 )
- {
-#if (VG_MODEL_VERSION_MIN <= 105)
- for( u32 i=0; i<ctx->model->material_count; i ++ )
- {
- struct mdl_material *mat = &ctx->model->materials[ i ];
- struct mdl_material_v101 *old = af_arritm( &v101_materials, i );
-
- mat->props.kv_root = keyvalues_append_frame( &kvs, 0, NULL );
-
- keyvalues_append_string( &kvs, mat->props.kv_root, "version", "101" );
- keyvalues_append_u32s( &kvs, mat->props.kv_root, "tex_diffuse", &old->tex_diffuse, 1 );
-
- if( mat->shader == k_shader_cubemap )
- {
- keyvalues_append_u32s( &kvs, mat->props.kv_root, "cubemap", &old->tex_none0, 1 );
- keyvalues_append_f32s( &kvs, mat->props.kv_root, "tint", old->colour, 4 );
- }
- else if( mat->shader == k_shader_terrain_blend )
- {
- keyvalues_append_f32s( &kvs, mat->props.kv_root, "sand_colour", old->colour, 4 );
- keyvalues_append_f32s( &kvs, mat->props.kv_root, "blend_offset", old->colour1, 2 );
- }
- else if( mat->shader == k_shader_standard_vertex_blend )
- {
- keyvalues_append_f32s( &kvs, mat->props.kv_root, "blend_offset", old->colour1, 2 );
- }
- else if( mat->shader == k_shader_water )
- {
- keyvalues_append_f32s( &kvs, mat->props.kv_root, "shore_colour", old->colour, 4 );
- keyvalues_append_f32s( &kvs, mat->props.kv_root, "deep_colour", old->colour1, 4 );
- }
- }
-#else
- ASSERT_CRITICAL( 0 );
-#endif
- }
- else if( ctx->model->version <= 109 )
- {
-#if (VG_MODEL_VERSION_MIN <= 109)
- for( u32 i=0; i<ctx->model->material_count; i ++ )
- {
- struct mdl_material *mat = &ctx->model->materials[ i ];
- u32 root = keyvalues_append_frame( &kvs, 0, NULL );
- keyvalues_append_string( &kvs, root, "version", "106" );
-
- void *buffer = NULL;
- if( v106_data.data )
- buffer = v106_data.data + mat->props.kvs.offset;
-
- vg_kvs_append_from_legacy_msg2( &kvs, root, buffer, mat->props.kvs.size );
- mat->props.kv_root = root;
- }
-#else
- ASSERT_CRITICAL( 0 );
-#endif
- }
- else
- {
- for( u32 i=0; i<ctx->model->material_count; i ++ )
- {
- struct mdl_material *mat = &ctx->model->materials[ i ];
- u32 root = keyvalues_append_frame( &kvs, 0, NULL );
- keyvalues_append_string( &kvs, root, "version", "110" );
-
- const c8 *buffer = NULL;
- if( v110_data.data )
- {
- buffer = v110_data.data + mat->props.kvs.offset;
- struct stream kv_stream;
- stream_open_buffer_read( &kv_stream, buffer, mat->props.kvs.size, 0 );
- keyvalues_parse_stream( &kvs, root, &kv_stream );
- }
- mat->props.kv_root = root;
- }
- }
-
- /* step2 ----------------------------- */
- for( u32 i=0; i<ctx->model->material_count; i ++ )
- {
- struct mdl_material *mat = &ctx->model->materials[ i ];
- u32 root = mat->props.kv_root;
- union shader_props *props = &ctx->model->shader_props[ i ];
-
- if( mat->shader == k_shader_standard ||
- mat->shader == k_shader_standard_cutout ||
- mat->shader == k_shader_foliage ||
- mat->shader == k_shader_fxglow ||
- 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 );
-}
+++ /dev/null
-#pragma once
-#include "vg_tex.h"
-#include "shader_props.h"
-#include "array_file.h"
-
-#define VG_MODEL_VERSION_MIN 110
-#define VG_MODEL_VERSION_NR 110
-
-enum mdl_shader
-{
- k_shader_standard = 0,
- k_shader_standard_cutout = 1,
- k_shader_terrain_blend = 2,
- k_shader_standard_vertex_blend = 3,
- k_shader_water = 4,
- k_shader_invisible = 5,
- k_shader_boundary = 6,
- k_shader_fxglow = 7,
- k_shader_cubemap = 8,
- k_shader_walking = 9,
- k_shader_foliage = 10,
- k_shader_workshop = 11,
- k_shader_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] );
+++ /dev/null
-#pragma once
-#include "model.h"
-
-enum entity_alias
-{
- k_ent_none = 0,
- k_ent_gate = 1,
- k_ent_spawn = 2,
- k_ent_route_node = 3,
- k_ent_route = 4,
- k_ent_water = 5,
- k_ent_volume = 6,
- k_ent_audio = 7,
- k_ent_marker = 8,
- k_ent_font = 9,
- k_ent_font_variant= 10,
- k_ent_traffic = 11,
- k_ent_skateshop = 12,
- k_ent_camera = 13,
- k_ent_swspreview = 14,
- k_ent_deleted1 = 15, //k_ent_menuitem = 15,
- k_ent_worldinfo = 16,
- k_ent_ccmd = 17,
- k_ent_objective = 18,
- k_ent_challenge = 19,
- k_ent_deleted0 = 20, //k_ent_relay = 20,
- k_ent_cubemap = 21,
- k_ent_miniworld = 22,
- k_ent_prop = 23,
- k_ent_list = 24,
- k_ent_region = 25,
- k_ent_glider = 26,
- k_ent_npc = 27,
- k_ent_armature = 28,
- k_ent_script = 29,
- k_ent_atom = 30,
- k_ent_cutscene = 31,
- k_ent_light = 32,
- k_mdl_mesh = 33,
- k_editer_property = 34,
- k_editer_item = 35,
-
- k_ent_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;
-};
+++ /dev/null
-#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 );
-}
--- /dev/null
+#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 );
+}
+++ /dev/null
-struct vg_steam_api _steam_api;
-
-static void cb_steam_warning( i32 severity, const c8 *pchMessage )
-{
- if( severity == 0 )
- vg_logsteam( "[message]" KBLK " '%s'\n", pchMessage );
- else
- vg_logsteam( "[message]" KYEL " '%s'\n", pchMessage );
-}
-
-#if defined( VG_ENGINE )
-static void cb_auth_ticket_recieved( void *result, void *context )
-{
- EncryptedAppTicketResponse_t *response = result;
-
- if( response->m_eResult == k_EResultOK )
- vg_logsteam( " New app ticket ready\n" );
- else
- vg_logsteam( KYEL " Could not request new encrypted app ticket (%u)\n", response->m_eResult );
-
- if( SteamAPI_ISteamUser_GetEncryptedAppTicket( _steam_api.pSteamUser, _steam_api.app_symmetric_key,
- VG_ARRAY_LEN(_steam_api.app_symmetric_key), &_steam_api.app_key_length ))
- {
- vg_logsteam( KGRN " Loaded app ticket\n" );
- }
- else
- {
- vg_logsteam( KRED " No ticket availible\n" );
- _steam_api.app_key_length = 0;
- }
-}
-#endif
-
-#if defined( VG_SERVER )
-static u8 vg_char_base16( c8 c )
-{
- if( c >= '0' && c <= '9' )
- return c-'0';
- if( c >= 'a' && c <= 'f' )
- return (c-'a') + 10;
- return 0;
-}
-#endif
-
-#if defined( VG_SERVER )
-VG_API b8 _vg_steam_init( u32 unIP, u16 usGamePort, u16 usQueryPort, EServerMode eServerMode,
- const c8 *pchVersionString, const c8 *appid_str )
-#else
-VG_API b8 _vg_steam_init(void)
-#endif
-{
- if( _steam_api.disabled )
- return 0;
-
- SteamErrMsg err;
-
- /* Steamworks init step
- * ---------------------------------------------------------------------------- */
-#if defined( VG_ENGINE )
- VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_STEAM ) );
-
- const char *pszInternalCheckInterfaceVersions =
- STEAMUTILS_INTERFACE_VERSION "\0"
- STEAMNETWORKINGUTILS_INTERFACE_VERSION "\0"
- STEAMAPPS_INTERFACE_VERSION "\0"
- STEAMCONTROLLER_INTERFACE_VERSION "\0"
- STEAMFRIENDS_INTERFACE_VERSION "\0"
- STEAMGAMESEARCH_INTERFACE_VERSION "\0"
- STEAMHTMLSURFACE_INTERFACE_VERSION "\0"
- STEAMHTTP_INTERFACE_VERSION "\0"
- STEAMINPUT_INTERFACE_VERSION "\0"
- STEAMINVENTORY_INTERFACE_VERSION "\0"
- STEAMMATCHMAKINGSERVERS_INTERFACE_VERSION "\0"
- STEAMMATCHMAKING_INTERFACE_VERSION "\0"
- STEAMMUSICREMOTE_INTERFACE_VERSION "\0"
- STEAMMUSIC_INTERFACE_VERSION "\0"
- STEAMNETWORKINGMESSAGES_INTERFACE_VERSION "\0"
- STEAMNETWORKINGSOCKETS_INTERFACE_VERSION "\0"
- STEAMNETWORKING_INTERFACE_VERSION "\0"
- STEAMPARENTALSETTINGS_INTERFACE_VERSION "\0"
- STEAMPARTIES_INTERFACE_VERSION "\0"
- STEAMREMOTEPLAY_INTERFACE_VERSION "\0"
- STEAMREMOTESTORAGE_INTERFACE_VERSION "\0"
- STEAMSCREENSHOTS_INTERFACE_VERSION "\0"
- STEAMUGC_INTERFACE_VERSION "\0"
- STEAMUSERSTATS_INTERFACE_VERSION "\0"
- STEAMUSER_INTERFACE_VERSION "\0"
- STEAMVIDEO_INTERFACE_VERSION "\0"
-
- "\0";
-
- if( SteamInternal_SteamAPI_Init( pszInternalCheckInterfaceVersions, &err ) != k_ESteamAPIInitResult_OK )
- {
- _steam_api.disabled = 1;
- vg_logsteam( KRED "SteamInternal_SteamAPI_Init() failed: '%s'\nAll steam interactions disabled for this session\n", err );
- return 0;
- }
-#endif
-
-#if defined( VG_SERVER )
- FILE *txt = fopen( "steam_appid.txt", "w" );
- fputs( appid_str, txt );
- fclose( txt );
-
- // FIXME: Add no-auth launch option (hoist from skaterift, as we have it there, to vg steam module?)
- vg_stack_allocator stack;
- vg_stack_init( &stack, NULL, VG_KB(256), NULL );
- u32 size;
- c8 *src = vg_file_read( &stack, "application_key", &size, 0 );
- if( src )
- {
- if( size < k_nSteamEncryptedAppTicketSymmetricKeyLen )
- {
- vg_error( "Application key was invalid size\n" );
- return 0;
- }
-
- for( int i=0; i<k_nSteamEncryptedAppTicketSymmetricKeyLen; i++ )
- _steam_api.server_symmetric_key[i] = (vg_char_base16( src[i*2+0] ) << 4) | vg_char_base16( src[i*2+1] );
- }
- else
- {
- vg_error( "No application_key file\n" );
- return 0;
- }
-
- const char *pszInternalCheckInterfaceVersions =
- STEAMUTILS_INTERFACE_VERSION "\0"
- STEAMNETWORKINGUTILS_INTERFACE_VERSION "\0"
-
- STEAMGAMESERVER_INTERFACE_VERSION "\0"
- STEAMGAMESERVERSTATS_INTERFACE_VERSION "\0"
- STEAMHTTP_INTERFACE_VERSION "\0"
- STEAMINVENTORY_INTERFACE_VERSION "\0"
- STEAMNETWORKING_INTERFACE_VERSION "\0"
- STEAMNETWORKINGMESSAGES_INTERFACE_VERSION "\0"
- STEAMNETWORKINGSOCKETS_INTERFACE_VERSION "\0"
- STEAMUGC_INTERFACE_VERSION "\0"
- "\0";
-
- ESteamAPIInitResult init_result = SteamInternal_GameServer_Init_V2(
- unIP, usGamePort, usQueryPort, eServerMode, pchVersionString, pszInternalCheckInterfaceVersions, &err );
-
- if( init_result != k_ESteamAPIInitResult_OK )
- {
- _steam_api.disabled = 1;
- vg_logsteam( KRED "SteamInternal_GameServer_Init_V2() failed: '%s'\n", err );
- return 0;
- }
-#endif
-
- /* Manual dispatch step
- * ----------------------------------------------------------------------------- */
- SteamAPI_ManualDispatch_Init();
-
- /*
- * Interface Init
- * ----------------------------------------------------------------------------- */
-
-#if defined( VG_ENGINE )
- _steam_api.hPipe = SteamAPI_GetHSteamPipe();
- _steam_api.pSteamUtils = SteamAPI_SteamUtils_v010();
- _steam_api.pSteamFriends = SteamAPI_SteamFriends_v018();
- _steam_api.pSteamUGC = SteamAPI_SteamUGC_v021();
- _steam_api.pSteamUser = SteamAPI_SteamUser_v023();
- _steam_api.pSteamUserStats = SteamAPI_SteamUserStats_v013();
- _steam_api.pSteamNetworkingSockets = SteamAPI_SteamNetworkingSockets_SteamAPI_v012();
-#endif
-
-#if defined( VG_SERVER )
- _steam_api.hPipe = SteamGameServer_GetHSteamPipe();
- _steam_api.pSteamGameServer = SteamAPI_SteamGameServer_v015();
- _steam_api.pSteamUtils = SteamAPI_SteamGameServerUtils_v010();
- _steam_api.pSteamUGC = SteamAPI_SteamGameServerUGC_v021();
- _steam_api.pSteamNetworkingSockets = SteamAPI_SteamGameServerNetworkingSockets_SteamAPI_v012();
-#endif
-
- _steam_api.pSteamNetworkingUtils = SteamAPI_SteamNetworkingUtils_SteamAPI_v004();
-
- SteamAPI_ISteamUtils_SetWarningMessageHook( _steam_api.pSteamUtils, cb_steam_warning );
-
-#if defined( VG_SERVER )
- SteamAPI_ISteamGameServer_LogOnAnonymous( _steam_api.pSteamGameServer );
-#endif
-
-
-#if defined( VG_ENGINE )
- strcpy( _steam_api.username_at_startup, "[unassigned]" );
- const c8 *username = SteamAPI_ISteamFriends_GetPersonaName( _steam_api.pSteamFriends );
- str_utf8_collapse( username, _steam_api.username_at_startup, VG_ARRAY_LEN(_steam_api.username_at_startup) );
-
-# if defined( VG_MULTIPLAYER )
- vg_logsteam( "Requesting new authorization ticket\n" );
-
- vg_steam_api_call *call = vg_alloc_async_steam_api_call();
- if( call )
- {
- call->userdata = NULL;
- call->cb = cb_auth_ticket_recieved;
- call->id = SteamAPI_ISteamUser_RequestEncryptedAppTicket( _steam_api.pSteamUser, NULL, 0 );
- }
-# endif
-#endif
-
- return 1;
-}
-
-static const c8 *string_ESteamNetworkingConnectionState( ESteamNetworkingConnectionState s )
-{
- if( s == k_ESteamNetworkingConnectionState_None ) return "None";
- if( s == k_ESteamNetworkingConnectionState_Connecting) return "Connecting";
- if( s == k_ESteamNetworkingConnectionState_FindingRoute) return "Finding route";
- if( s == k_ESteamNetworkingConnectionState_Connected) return "Connected";
- if( s == k_ESteamNetworkingConnectionState_ClosedByPeer) return "Closed By Peer";
- if( s == k_ESteamNetworkingConnectionState_ProblemDetectedLocally) return "Problem detected locally";
- if( s == k_ESteamNetworkingConnectionState_FinWait) return "Finwait";
- if( s == k_ESteamNetworkingConnectionState_Linger) return "Linger";
- if( s == k_ESteamNetworkingConnectionState_Dead) return "Dead";
- return "enum-out-of-range";
-}
-
-static const c8 *string_ESteamAPICallFailure( ESteamAPICallFailure e )
-{
- if( e == k_ESteamAPICallFailureNone ) return "None";
- if( e == k_ESteamAPICallFailureSteamGone ) return "Steam Gone";
- if( e == k_ESteamAPICallFailureNetworkFailure ) return "Network Failure";
- if( e == k_ESteamAPICallFailureInvalidHandle ) return KBLK "Invalid Handle";
- if( e == k_ESteamAPICallFailureMismatchedCallback ) return "Mismatched Callback";
- return "enum-out-of-range";
-}
-
-void vg_steam_frame(void)
-{
- if( _steam_api.disabled )
- return;
-
- SteamAPI_ManualDispatch_RunFrame( _steam_api.hPipe );
-
- CallbackMsg_t callback;
- while( SteamAPI_ManualDispatch_GetNextCallback( _steam_api.hPipe, &callback ) )
- {
- /* Check for dispatching API call results */
- i32 type = callback.m_iCallback;
- if( type == k_iSteamUtils_SteamAPICallCompleted )
- {
- SteamAPICallCompleted_t *inf = callback.m_pubParam;
-
- b8 bFailed;
- void *call_data = alloca( inf->m_cubParam );
-
- if( SteamAPI_ManualDispatch_GetAPICallResult( _steam_api.hPipe, inf->m_hAsyncCall,
- call_data, inf->m_cubParam,
- inf->m_iCallback, &bFailed ) )
- {
- vg_logsteam( "api_call_completed %lu\n", inf->m_hAsyncCall );
-
- int j=0;
- for( int i=0; i<_steam_api.api_call_count; i++ )
- {
- if( _steam_api.api_calls[i].id != inf->m_hAsyncCall )
- _steam_api.api_calls[j ++] = _steam_api.api_calls[i];
- else
- _steam_api.api_calls[i].cb( call_data, _steam_api.api_calls[i].userdata );
- }
-
- if( _steam_api.api_call_count == j )
- vg_error( "No tracker was register for API call\n" );
-
- _steam_api.api_call_count = j;
- }
- else
- {
- enum ESteamAPICallFailure e =
- SteamAPI_ISteamUtils_GetAPICallFailureReason( _steam_api.pSteamUtils, inf->m_hAsyncCall );
- const c8 *fail_str = string_ESteamAPICallFailure( e );
- vg_logsteam( KRED "Error getting call result on %lu (code %d)\n", inf->m_hAsyncCall, fail_str );
- }
- }
- else
- {
- /*
- * Look at callback.m_iCallback to see what kind of callback it is,
- * and dispatch to appropriate handler(s)
- * void *data = callback.m_pubParam;
- */
- if( type == k_iSteamUser_SteamServersConnected )
- vg_success( "Steam servers connected" );
- else if( type == k_iSteamUser_SteamConnectFailure )
- {
- SteamServerConnectFailure_t *inf = callback.m_pubParam;
- vg_logsteam( KRED "Steam server connect failure, code %d, retrying: %d\n", inf->m_eResult, inf->m_bStillRetrying );
- }
- else if( type == k_iSteamUser_SteamServersDisconnected )
- {
- SteamServersDisconnected_t *inf = callback.m_pubParam;
- vg_logsteam( "Steam servers disconnect, code %d\n", inf->m_eResult );
- }
- else if( type == k_iSteamNetworkingSockets_SteamNetConnectionStatusChangedCallback )
- {
- SteamNetConnectionStatusChangedCallback_t *inf = callback.m_pubParam;
- const c8 *status_string = string_ESteamNetworkingConnectionState( inf->m_info.m_eState );
- vg_logsteam( "Connection status changed: %x -> %s\n Debug: '%s'\n EndDebug: '%s'\n",
- inf->m_hConn, status_string, inf->m_info.m_szConnectionDescription,
- inf->m_info.m_szEndDebug );
-
- if( _steam_api.cb_connection_changed )
- _steam_api.cb_connection_changed( inf );
- }
- else if( type == k_iSteamNetworkingSockets_SteamNetAuthenticationStatus )
- {
- SteamNetAuthenticationStatus_t *inf = callback.m_pubParam;
- vg_logsteam( "Steam Authentication Status: %d\n Debug: '%s'\n", inf->m_eAvail, inf->m_debugMsg );
- }
- }
-
- SteamAPI_ManualDispatch_FreeLastCallback( _steam_api.hPipe );
- }
-}
-
-VG_API void _vg_steam_shutdown(void)
-{
-#if defined( VG_SERVER )
- if( _steam_api.is_connected )
- {
- SteamAPI_ISteamGameServer_LogOff( _steam_api.pSteamGameServer );
- _steam_api.is_connected = 0;
- }
- SteamGameServer_Shutdown();
-#else
- SteamAPI_Shutdown();
-#endif
-}
-
-vg_steam_api_call *vg_alloc_async_steam_api_call(void)
-{
- if( _steam_api.api_call_count == VG_ARRAY_LEN(_steam_api.api_calls) )
- return NULL;
- return &_steam_api.api_calls[ _steam_api.api_call_count ++ ];
-}
-
-#if defined( VG_ENGINE )
-void vg_steam_set_achievement( const c8 *name, b8 yes )
-{
- if( _steam_api.disabled || _steam_api.demo_mode )
- return;
-
- if( yes )
- {
- if( SteamAPI_ISteamUserStats_SetAchievement( _steam_api.pSteamUserStats, name ) )
- {
- vg_logsteam( KGRN "Set achievement '%s'\n", name );
- SteamAPI_ISteamUserStats_StoreStats( _steam_api.pSteamUserStats );
- }
- }
- else
- {
- if( SteamAPI_ISteamUserStats_ClearAchievement( _steam_api.pSteamUserStats, name ) )
- {
- vg_logsteam( KBLK "Clear achievement '%s'\n", name );
- SteamAPI_ISteamUserStats_StoreStats( _steam_api.pSteamUserStats );
- }
- }
-}
-#endif
+++ /dev/null
-#include "foundation.h"
-#include "vg_engine.h"
-#include "vg_graphics.h"
-#include "vg_render.h"
-#include "vg_opengl.h"
-#include "vg_shader.h"
-
-struct graphics_target _ui_surface =
-{
- .mode = k_graphics_mode_software,
- .size = { 1920, 1080 },
-};
-
-static GLuint _ui_surface_texture;
-
-#include <unistd.h>
-void _engine_ui_init(void)
-{
- _ui_surface.buffer = _heap_allocate( 1920*1080*4 );
-
- glGenTextures( 1, &_ui_surface_texture );
- glActiveTexture( GL_TEXTURE0 );
- glBindTexture( GL_TEXTURE_2D, _ui_surface_texture );
- glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
-}
-
-void _engine_ui_pre_render(void)
-{
- ASSERT_CRITICAL( _engine.w <= 1920 );
- ASSERT_CRITICAL( _engine.h <= 1080 );
-
- f64 x = 0, y = 0;
-#if 0
- glfwGetCursorPos( _engine.window_handle, &x, &y );
-#endif
- _ui_set_mouse( x, y );
- _ui_update();
-
- _graphics_set_target( &_ui_surface );
- _graphics_viewport( 0, 0, _engine.w, _engine.h );
- //_graphics_fill_rect( (i16[]){ 0, 0, _engine.w, _engine.h }, (union colour){{0,0,0,255}} );
-}
-
-void _engine_ui_input_character( u32 codepoint )
-{
- if( (codepoint >= (u32)' ') && (codepoint <= (u32)'~') )
- _ui_input_text( (const c8[]){ codepoint, 0 } );
-}
-
-void _engine_ui_post_render(void)
-{
- glEnable( GL_BLEND );
- glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
- glBlendEquation( GL_FUNC_ADD );
-
- glActiveTexture( GL_TEXTURE0 );
- glBindTexture( GL_TEXTURE_2D, _ui_surface_texture );
- glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 1920, 1080, GL_BGRA, GL_UNSIGNED_BYTE, _ui_surface.buffer );
-
- _shader_bind( k_shader_blit );
- _shader_blit_uTexMain( 0 );
- _shader_blit_uFlip( 1 );
- _shader_blit_uInverseRatio( (f32[2]){ (f64)_engine.w/1920.0, (f64)_engine.h/1080.0 } );
-
- _render_fullscreen_quad();
- glDisable( GL_BLEND );
-}
--- /dev/null
+#include "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;
+}
--- /dev/null
+#pragma once
+
+struct array_file_ptr
+{
+ void *data;
+ u32 count, stride;
+};
+
+struct array_file_meta
+{
+ u32 file_offset,
+ item_count,
+ item_size;
+
+ c8 name[16];
+};
+
+struct array_file_header
+{
+ u32 version;
+ struct array_file_meta index;
+};
+
+struct array_file_context
+{
+ struct stream *stream;
+ struct array_file_header header;
+ struct array_file_ptr index;
+};
+
+/* array loading */
+struct array_file_meta *af_find_array( struct array_file_context *ctx, const c8 *name );
+
+void af_load_array_file( struct array_file_context *ctx, struct array_file_ptr *out_ptr,
+ struct array_file_meta *arr, struct stack_allocator *stack, u32 stride );
+
+b8 af_load_array( struct array_file_context *ctx, struct array_file_ptr *ptr, const c8 *name,
+ struct stack_allocator *stack, u32 stride );
+void af_load_array_file_buffer( struct array_file_context *ctx, struct array_file_meta *arr, void *buffer, u32 stride );
+
+/* array access */
+void *af_arritm( struct array_file_ptr *arr, u32 index );
+u32 af_arrcount( struct array_file_ptr *arr );
+
+/* packed string buffer access (with djb2 hash prefix) */
+const c8 *af_str( const void *packed_strings, u32 pstr );
+u32 af_str_hash( const void *packed_strings, u32 pstr );
+b8 af_str_eq( const void *packed_strings, u32 pstr, const char *str, u32 str_hash );
+
+#define AF_STR_EQ( 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 );
};
};
+void _audio_init(void);
void vg_audio_device_init(void);
void vg_audio_begin(void);
//#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 );
--- /dev/null
+#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';
+ }
+}
--- /dev/null
+#pragma once
+
+void _engine_console_init(void);
+void _engine_console_update(void);
+void _engine_console_ui(void);
#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>
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 )
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__ )
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;
_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;
}
//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();
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();
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;
+++ /dev/null
-{
- add vg_engine.c
- hook
- {
- event OPTIONS
- function _engine_options
- }
- event
- {
- name ENGINE_NEW_FRAME
- prototype "void"
- }
- event
- {
- name ENGINE_FIXED_UPDATE
- prototype "void"
- }
- event
- {
- name ENGINE_RENDER
- prototype "void"
- }
- event
- {
- name ENGINE_UI
- prototype "void"
- }
- event
- {
- name ENGINE_WINDOW_RESIZE
- prototype "void"
- }
-}
-
-{
- add ui.c
- hook
- {
- event START
- function _engine_ui_init
- }
-}
-
-{
- add input.c
- hook
- {
- event START
- function _input_init
- }
- hook
- {
- event ENGINE_NEW_FRAME
- function _input_update
- }
- ccmd
- {
- name bind
- function _input_bind_ccmd
- description "Bind device input to a button, action or axis"
-
- parameter
- {
- description "Device input alias"
- }
-
- parameter
- {
- description "button,action,axis name"
- }
- }
- ccmd
- {
- name unbind
- description "Unbind all bindings that match"
- function _input_unbind_ccmd
-
- parameter
- {
- description "binding"
- }
- }
-}
-
-{
- add console.c
- hook
- {
- event START
- function _engine_console_init
- }
- hook
- {
- event ENGINE_NEW_FRAME
- function _engine_console_update
- }
- hook
- {
- event ENGINE_UI
- function _engine_console_ui
- affinity 9999
- }
-}
-
-{
- add vg_framebuffer.c
- hook
- {
- event ENGINE_WINDOW_RESIZE
- function _framebuffer_resize
- }
-}
-
-{
-add source/engine/shader.c
-input_layer
-{
- name console
-}
-input_layer
-{
- name ui
-}
-input
-{
- name console
- type action
- layer_mask console
-}
-
-append async.kv
-thread
-{
- name main
- queue_size_m 40
- flag MAIN
- flag OPENGL
-}
-thread
-{
- name async
- queue_size_m 40
- flag ASYNC
-}
-thread
-{
- name audio
-}
-thread
-{
- name exitor
- queue_size_m 1
-}
-
-append graphics.kv
-
-ccmd
-{
- name exec
- function _console_exec_ccmd
- description "Execute a configuration file"
-
- parameter
- {
- description "The path to the config"
- }
-}
-config "bind ALT+GRAVE_ACCENT console"
-
-config "bind BACKSPACE ui_backspace"
-config "bind DELETE ui_delete"
-config "bind ENTER ui_enter"
-config "bind TAB ui_indent"
-config "bind HOME ui_home"
-config "bind END ui_end"
-config "bind LEFT ui_left"
-config "bind RIGHT ui_right"
-config "bind UP ui_up"
-config "bind DOWN ui_down"
-config "bind MOUSE_LEFT ui_click"
-
-config "bind LEFT_SHIFT ui_select"
-config "bind RIGHT_SHIFT ui_select"
-
-input
-{
- name ui_delete
- type action
- layer_mask ui
-}
-input
-{
- name ui_backspace
- type action
- layer_mask ui
-}
-input
-{
- name ui_enter
- type action
- layer_mask ui
-}
-input
-{
- name ui_indent
- type action
- layer_mask ui
-}
-
-input
-{
- name ui_select
- type button
- layer_mask ui
-}
-input
-{
- name ui_home
- type button
- layer_mask ui
-}
-input
-{
- name ui_end
- type button
- layer_mask ui
-}
-input
-{
- name ui_left
- type button
- layer_mask ui
-}
-input
-{
- name ui_right
- type button
- layer_mask ui
-}
-input
-{
- name ui_up
- type button
- layer_mask ui
-}
-input
-{
- name ui_down
- type button
- layer_mask ui
-}
-input
-{
- name ui_click
- type action
- layer_mask ui
-}
-
-shader
-{
- name blit
-
- subshader
- {
- type vertex
- add shaders/blit.vs
-
- uniform
- {
- type vec2
- alias uInverseRatio
- }
- uniform
- {
- type int
- alias uFlip
- }
- }
-
- subshader
- {
- type fragment
- add shaders/blit_tex.fs
-
- uniform
- {
- type sampler2D
- alias uTexMain
- }
- }
-}
-
-cvar
-{
- name test
- type u32
- default 5
- cheat 1
- description "This is just a test variable!"
-}
-
-hook
-{
- event START
- function "_shaders_compile"
-}
-}
-
-
-
-
-
-
-add vg_camera.c
-
-
-add vg_tex.c
-hook
-{
- event START
- function _vg_tex_init
-}
-
-
-add vg_lines.c
-hook
-{
- event START
- function _vg_lines_init
-}
-cvar
-{
- name debug_lines
- type u32
- default 0
- cheat 1
- description "Show line debuggers"
-}
-shader
-{
- name debug_lines
- subshader
- {
- type vertex
- add shaders/debug_lines.vs
- uniform
- {
- type mat4
- alias uPv
- }
- }
- subshader
- {
- type fragment
- add shaders/debug_lines.fs
- }
-}
-
-
-add vg_audio.c
-add vg_audio_dsp.c
-add vorbis.c
-hook
-{
- affinity -3008
- event START
- function _audio_init
-}
-hook
-{
- affinity -6004
- event START
- function _dsp_init
-}
-
-
-add model.c
-add metascene.c
-add array_file.c
--- /dev/null
+#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;
+}
--- /dev/null
+#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 );
attachments[];
};
+void _framebuffer_resize(void);
+
struct framebuffer *_framebuffer_allocate( struct stack_allocator *stack, u32 attachment_count, b8 track );
/*
};
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 );
+#pragma once
#define LINE_RED 0xff0000ff
#define LINE_GREEN 0xff00ff00
#define LINE_BLUE 0xffff0000
#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] );
--- /dev/null
+#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
--- /dev/null
+#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] );
--- /dev/null
+#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 );
+}
--- /dev/null
+#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] );
#pragma once
+
+void _vg_render_init(void);
void _render_fullscreen_quad(void);
#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 );
# 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 );
}
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);
/* 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
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
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 )
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
}
}
}
+
+ if( c == '\0' )
+ break;
}
}
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,
[k_platform_web] = "web"
};
-struct
-{
- struct stream name, tripple, folder;
- const c8 *enabled_features[ 64 ];
-}
-static _metacompiler;
-
struct shell_command
{
u32 temp_frame;
_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 ) )
{
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 );
}
}
}
-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 );
}
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;
}
_option_file_path = _option();
- ASSERT_CRITICAL( _option_file_path );
}
void (*_event_OPTIONS_subscribers[])( void ) =
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"} );
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;
}
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 );
}
}
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, ¶meter_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, ¶meter_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"} );
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;
$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 ) )
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 ) );
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 )
{
_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();
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 )
{