From: hgn Date: Sat, 29 Nov 2025 19:26:38 +0000 (+0000) Subject: fffffffffff X-Git-Url: https://skaterift.com/git/?a=commitdiff_plain;h=527d7db897a2dd8de98d137623565bb76b4caca6;p=vg.git fffffffffff --- diff --git a/source/engine/array_file.c b/source/engine/array_file.c deleted file mode 100644 index 3c34f72..0000000 --- a/source/engine/array_file.c +++ /dev/null @@ -1,356 +0,0 @@ -#include "foundation.h" -#include "array_file.h" - -u32 af_str_hash( const void *packed_strings, u32 pstr ) -{ - if( pstr & 0x3 ) - { - $log( $fatal, {"ALIGNMENT ERROR, PSTR INDEX: "}, $unsigned(pstr) ); - } - return *((u32 *)(packed_strings + pstr)); -} - -const c8 *af_str( const void *packed_strings, u32 pstr ) -{ - return packed_strings + pstr + 4; -} - -b8 af_str_eq( const void *packed_strings, u32 pstr, const c8 *str, u32 str_hash ) -{ - if( af_str_hash( packed_strings, pstr ) == str_hash ) - if( compare_buffers( str, 0, af_str( packed_strings, pstr ), 0 )) - return 1; - return 0; -} - -void af_load_array_file_buffer( struct array_file_context *ctx, struct array_file_meta *arr, void *buffer, u32 stride ) -{ -#if defined( __EMSCRIPTEN__ ) - if( arr->item_count ) - { - stream_seek( ctx->stream, arr->file_offset ); - if( stride != arr->item_size ) - { - zero_buffer( buffer, stride*arr->item_count ); - u32 temp_frame = _start_temporary_frame(); - { - u8 *temp = _temporary_allocate( arr->item_count * arr->item_size, 8 ); - stream_read( ctx->stream, temp, arr->item_count * arr->item_size ); - - u32 read_size = u32_min( stride, arr->item_size ); - for( u32 i=0; iitem_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; iitem_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; iindex); 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; idata)[i] = 0xab; - - if( index->last ) - index->last->next = entry; - index->last = entry; - - if( !index->first ) - index->first = entry; - - return entry; -} - -static void af_compiler_init_index( struct af_compiler_index *index, const c8 *alias, u32 element_size ) -{ - ASSERT_CRITICAL( element_size ); - if( !buffer_copy( alias, 0, index->name, sizeof(index->name) ) ) - { - $log( $fatal, {"Index name overflowed: "}, {alias} ); - _fatal_exit(); - } - index->element_size = element_size; - index->element_count = 0; - index->first = NULL; - index->last = NULL; -} - -struct af_compiler_index *af_compiler_create_index( struct af_compiler *compiler, const c8 *alias, u32 element_size ) -{ - struct af_compiler_item *item = af_compiler_allocate_items( compiler, &compiler->index, 1 ); - struct af_compiler_index *index = item->data; - af_compiler_init_index( index, alias, element_size ); - return index; -} - -u32 af_compile_string( struct af_compiler *compiler, const c8 *string ) -{ - u32 string_hash = buffer_djb2( string, 0 ); - - // TODO: Hash table against existing strings (low priority) - u32 offset = offset = compiler->strings_index->element_count; - u32 bytes = PAD_TO_4( buffer_first_index( string, 0,0 )+1 + 4 ); - struct af_compiler_item *item = af_compiler_allocate_items( compiler, compiler->strings_index, bytes ); - *((u32 *)item->data) = string_hash; - buffer_copy( string, 0, item->data+4, 0 ); - return offset; -} - -void af_compiler_init( struct af_compiler *compiler, struct stack_allocator *stack ) -{ - compiler->stack = stack; - af_compiler_init_index( &compiler->index, "index", sizeof(struct af_compiler_index) ); - compiler->strings_index = af_compiler_create_index( compiler, "strings", 1 ); - af_compile_string( compiler, "nul" ); -} - -static void af_write_bin( struct af_compiler *compiler, void *data, u32 data_len, u32 padding ) -{ - if( data ) - { - stream_write( &compiler->stream, data, data_len ); - compiler->file_offset += data_len; - } - - if( padding ) - { - while( compiler->file_offset % padding ) - { - const u8 pad_byte = 0xac; - stream_write( &compiler->stream, &pad_byte, 1 ); - compiler->file_offset ++; - } - } -} - -struct af_compiler_index *af_get_or_make_index( struct af_compiler *compiler, const c8 *alias, u32 element_size ) -{ - struct af_compiler_iter iter; - af_init_iterator( &iter, &compiler->index ); - while( af_next( &iter ) ) - { - struct af_compiler_index *index = iter.data; - if( compare_buffers( index->name, 0, alias, 0 ) ) - return index; - } - - return af_compiler_create_index( compiler, alias, element_size ); -} - -b8 af_write( struct af_compiler *compiler, const c8 *path, u32 version ) -{ - u32 indices_to_write = 0; - - struct af_compiler_iter iter; - af_init_iterator( &iter, &compiler->index ); - while( af_next( &iter ) ) - { - struct af_compiler_index *index = iter.data; - if( index->element_count ) - indices_to_write ++; - } - - u32 header_size = PAD_TO_8( sizeof( struct array_file_header ) ); - u32 index_size = PAD_TO_8( sizeof( struct array_file_meta ) * indices_to_write ); - - if( !stream_open_file( &compiler->stream, path, k_stream_write ) ) - return 0; - - compiler->file_offset = 0; - - struct array_file_header header; - header.version = version; - header.index.file_offset = header_size; - header.index.item_count = indices_to_write; - header.index.item_size = sizeof(struct array_file_meta); - buffer_copy( "index", 0, header.index.name, sizeof(header.index.name) ); - af_write_bin( compiler, &header, sizeof(struct array_file_header), 8 ); - - /* write index */ - u32 file_offset = header_size + index_size; - af_init_iterator( &iter, &compiler->index ); - while( af_next( &iter ) ) - { - struct af_compiler_index *index = iter.data; - if( index->element_count ) - { - struct array_file_meta meta; - buffer_copy( index->name, sizeof(index->name), meta.name, sizeof(meta.name) ); - meta.item_count = index->element_count; - meta.item_size = index->element_size; - meta.file_offset = file_offset; - file_offset += PAD_TO_8( meta.item_size*meta.item_count ); - af_write_bin( compiler, &meta, sizeof(struct array_file_meta), 0 ); - } - } - af_write_bin( compiler, NULL, 0, 8 ); - - af_init_iterator( &iter, &compiler->index ); - while( af_next( &iter ) ) - { - struct af_compiler_index *index = iter.data; - - if( index->element_count ) - { - struct af_compiler_iter item_iter; - af_init_iterator( &item_iter, index ); - - while( af_next( &item_iter ) ) - af_write_bin( compiler, item_iter.data, index->element_size, 0 ); - af_write_bin( compiler, NULL, 0, 8 ); - } - } - - stream_close( &compiler->stream ); - return 1; -} diff --git a/source/engine/array_file.h b/source/engine/array_file.h deleted file mode 100644 index 5abd164..0000000 --- a/source/engine/array_file.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -struct array_file_ptr -{ - void *data; - u32 count, stride; -}; - -struct array_file_meta -{ - u32 file_offset, - item_count, - item_size; - - c8 name[16]; -}; - -struct array_file_header -{ - u32 version; - struct array_file_meta index; -}; - -struct array_file_context -{ - struct stream *stream; - struct array_file_header header; - struct array_file_ptr index; -}; - -/* array loading */ -struct array_file_meta *af_find_array( struct array_file_context *ctx, const c8 *name ); - -void af_load_array_file( struct array_file_context *ctx, struct array_file_ptr *out_ptr, - struct array_file_meta *arr, struct stack_allocator *stack, u32 stride ); - -b8 af_load_array( struct array_file_context *ctx, struct array_file_ptr *ptr, const c8 *name, - struct stack_allocator *stack, u32 stride ); -void af_load_array_file_buffer( struct array_file_context *ctx, struct array_file_meta *arr, void *buffer, u32 stride ); - -/* array access */ -void *af_arritm( struct array_file_ptr *arr, u32 index ); -u32 af_arrcount( struct array_file_ptr *arr ); - -/* packed string buffer access (with djb2 hash prefix) */ -const c8 *af_str( const void *packed_strings, u32 pstr ); -u32 af_str_hash( const void *packed_strings, u32 pstr ); -b8 af_str_eq( const void *packed_strings, u32 pstr, const char *str, u32 str_hash ); - -#define AF_STR_EQ( PACKED, PSTR, CONSTR ) \ - af_str_eq( PACKED, PSTR, CONSTR, buffer_djb2( CONSTR, 0 ) ) - -/* COmpiler - * ------------------------------------ */ - -struct af_compiler_item -{ - void *data; - u32 count; - struct af_compiler_item *next; -}; - -struct af_compiler_index -{ - c8 name[16]; - u32 element_size, element_count; - struct af_compiler_item *first, *last; -}; - -struct af_compiler -{ - struct stack_allocator *stack; - struct af_compiler_index index, - *strings_index; - struct af_compiler_item *most_recent_item; - - struct stream stream; - u32 file_offset; -}; - -void af_compiler_init( struct af_compiler *compiler, struct stack_allocator *stack ); -struct af_compiler_item *af_compiler_allocate_items( struct af_compiler *compiler, struct af_compiler_index *index, u32 count ); -struct af_compiler_index *af_compiler_create_index( struct af_compiler *compiler, const c8 *alias, u32 element_size ); -b8 af_write( struct af_compiler *compiler, const c8 *path, u32 version ); -struct af_compiler_index *af_get_or_make_index( struct af_compiler *compiler, const c8 *alias, u32 element_size ); -u32 af_compile_string( struct af_compiler *compiler, const c8 *string ); - -b8 af_open_stream( struct array_file_context *afc, struct stream *stream, u32 min_version, u32 max_version, - struct stack_allocator *stack ); diff --git a/source/engine/audio_mixer.c b/source/engine/audio_mixer.c deleted file mode 100644 index 0209e62..0000000 --- a/source/engine/audio_mixer.c +++ /dev/null @@ -1,1455 +0,0 @@ -struct vg_audio _vg_audio = -{ - .master_volume_ui = 1.0f, - .dsp_enabled_ui = 1 -}; - -_Thread_local static b8 _vg_audio_thread_has_lock = 0; - -void vg_audio_lock(void) -{ - SDL_LockMutex( _vg_audio.mutex ); - _vg_audio_thread_has_lock = 1; -} - -void vg_audio_unlock(void) -{ - _vg_audio_thread_has_lock = 0; - SDL_UnlockMutex( _vg_audio.mutex ); -} - -static void vg_audio_assert_lock(void) -{ - if( _vg_audio_thread_has_lock == 0 ) - { - vg_error( "vg_audio function requires locking\n" ); - abort(); - } -} - -/* clip loading from disk - * ------------------------------------------------------------------------------- - */ -VG_TIER_2 b8 vg_audio_clip_load( audio_clip *clip, vg_stack_allocator *stack ) -{ - VG_ASSERT( _vg_thread_has_flags( VG_THREAD_BACKGROUND ) ); - - /* load in directly */ - u32 format = clip->flags & AUDIO_FLAG_FORMAT; - if( format == k_audio_format_vorbis ) - { - if( clip->path ) - { - clip->any_data = vg_file_read( stack, clip->path, &clip->size, 0 ); - if( clip->any_data ) - { - float mb = (float)(clip->size) / (1024.0f*1024.0f); - vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb ); - return 1; - } - else - { - vg_error( "Audio failed to load '%s'\n", clip->path ); - return 0; - } - } - else - { - vg_error( "No path specified, embeded vorbis unsupported\n" ); - return 0; - } - } - else if( format == k_audio_format_stereo ) - { - vg_error( "Unsupported format (Stereo uncompressed)\n" ); - return 0; - } - else if( format == k_audio_format_bird ) - { - if( !clip->any_data ) - { - vg_error( "No data, external birdsynth unsupported\n" ); - return 0; - } - - struct synth_bird *bird = vg_stack_allocate( stack, sizeof(struct synth_bird), 8, NULL ); - bird->settings = clip->any_data; - clip->any_data = bird; - vg_info( "Loaded bird synthesis pattern (%u bytes)\n", clip->size ); - return 1; - } - else - { - if( !clip->path ) - { - vg_error( "No path specified, embeded mono unsupported\n" ); - return 0; - } - - u32 temp_frame = _vg_start_temp_frame(); - stb_vorbis_alloc alloc = - { - .alloc_buffer = vg_stack_allocate( _vg_temp_stack(), AUDIO_DECODE_SIZE, 8, "Vorbis alloc buffer (temp)" ), - .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE - }; - - u32 fsize; - void *filedata = vg_file_read( _vg_temp_stack(), clip->path, &fsize, 0 ); - int err; - stb_vorbis *decoder = stb_vorbis_open_memory( filedata, fsize, &err, &alloc ); - if( !decoder ) - { - vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", clip->path, err ); - _vg_end_temp_frame( temp_frame ); - return 0; - } - - /* only mono is supported in uncompressed */ - u32 length_samples = stb_vorbis_stream_length_in_samples( decoder ), - data_size = length_samples * sizeof(i16); - - clip->any_data = vg_stack_allocate( stack, data_size, 8, NULL ); - clip->size = length_samples; - int read_samples = stb_vorbis_get_samples_i16_downmixed( decoder, clip->any_data, length_samples ); - _vg_end_temp_frame( temp_frame ); - - if( read_samples == length_samples ) - return 1; - else - { - vg_error( "Decode error, read_samples did not match length_samples\n" ); - return 0; - } - } -} - -VG_TIER_2 u32 vg_audio_clip_loadn( audio_clip *arr, u32 count, vg_stack_allocator *stack ) -{ - u32 succeeded = 0; - for( u32 i=0; i 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; istage == 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; idecoder_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; idecoder_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; jvolume, 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; istage == 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; iactivity == 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; iperiod_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; iactivity == 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; iui_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= wk[0])) && - ((wr[1] <= wk[1]+wk[3]) && (wr[1]+wr[3] >= wk[1])) ) - { - collide = 1; - break; - } - } - - if( !collide ) - break; - else - wr[1] += 18; - } - - ui_text( ctx, wr, perf, 1, k_ui_align_middle_left, 0 ); - rect_copy( wr, overlap_buffer[ overlap_length ++ ] ); - } - } -#endif -#endif - } - - vg_audio_unlock(); -} - -static void cb_vg_audio_close( struct vg_magi_panel *me ) -{ - vg_audio_lock(); - _vg_audio.inspector_open = 0; - vg_audio_unlock(); - free( me->data ); -} - -static int cmd_vg_audio( int argc, const char *argv[] ) -{ - vg_audio_lock(); - if( _vg_audio.inspector_open ) - { - vg_error( "Only 1 audio inspector at a time.\n" ); - vg_audio_unlock(); - return 0; - } - - _vg_audio.inspector_open = 1; - vg_audio_unlock(); - ui_px w = 800, h=8*18; - struct vg_magi_panel *magi = _vg_magi_open( w,h, VG_MAGI_ALL ); - magi->title = "Audio inspector"; - - struct vg_audio_view_data *vd = malloc(sizeof(struct vg_audio_view_data)); - vd->view_3d = 0; - - magi->data = vd; - magi->ui_cb = cb_vg_audio_view; - magi->close_cb = cb_vg_audio_close; - return 1; -} - -VG_API void _vg_audio_register(void) -{ - vg_console_reg_cmd( "vg_audio", cmd_vg_audio, NULL ); - vg_console_reg_var( "volume", &_vg_audio.master_volume_ui, k_var_dtype_f32, VG_VAR_PERSISTENT ); - vg_console_reg_var( "vg_dsp", &_vg_audio.dsp_enabled_ui, k_var_dtype_i32, VG_VAR_PERSISTENT ); - vg_console_reg_var( "vg_audio_device", &_vg_audio.device_choice, k_var_dtype_str, VG_VAR_PERSISTENT ); -} - -void vg_audio_device_init(void) -{ - SDL_AudioSpec spec_desired, spec_got; - spec_desired.callback = _vg_audio_mixer; - spec_desired.channels = 2; - spec_desired.format = AUDIO_F32; - spec_desired.freq = 44100; - spec_desired.padding = 0; - spec_desired.samples = AUDIO_FRAME_SIZE; - spec_desired.silence = 0; - spec_desired.size = 0; - spec_desired.userdata = NULL; - - _vg_audio.sdl_output_device = SDL_OpenAudioDevice( _vg_audio.device_choice.buffer, 0, - &spec_desired, &spec_got, 0 ); - - vg_info( "Start audio device (%u, F32, %u) @%s\n", spec_desired.freq, AUDIO_FRAME_SIZE, - _vg_audio.device_choice.buffer ); - - if( _vg_audio.sdl_output_device ) - { - SDL_PauseAudioDevice( _vg_audio.sdl_output_device, 0 ); - vg_success( "Unpaused device %d.\n", _vg_audio.sdl_output_device ); - _vg_audio.working = 1; - } - else - { - _vg_audio.working = 0; - vg_error( - "SDL_OpenAudioDevice failed. Your default audio device must support (or transcode from):\n" - " Sample rate: 44100 hz\n" - " Buffer size: 512 samples\n" - " Channels: 2\n" - " Format: s16 or f32\n" ); - } -} - -static void vg_audio_free(void) -{ - SDL_CloseAudioDevice( _vg_audio.sdl_output_device ); - _vg_audio.sdl_output_device = 0; -} - -VG_API void _vg_audio_init(void) -{ - VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_SDL ) ); - - _vg_audio.profiler = _vg_profiler_create( "vg.audio", 1000.0f/20.0f ); - - /* fixed */ - u32 decode_size = AUDIO_DECODE_SIZE * AUDIO_CHANNELS; - _vg_audio.decoding_buffer = vg_stack_allocate( NULL, decode_size, 8, "Decoding buffers" ); - - struct audio_master_controls *master_controls = &_vg_audio.controls; - master_controls->dsp_enabled = _vg_audio.dsp_enabled_ui; - master_controls->volume_target = (f64)_vg_audio.master_volume_ui * (f64)AUDIO_VOLUME_100; - v3_copy( (v3f){1,0,0}, master_controls->listener_right_ear_direction ); - v3_zero( master_controls->listener_velocity ); - v3_zero( master_controls->listener_position ); - _vg_dsp_init(); - - _vg_audio.mutex = SDL_CreateMutex(); - if( _vg_audio.mutex == NULL ) - vg_fatal_error( "SDL2: %s\n", SDL_GetError() ); - vg_audio_device_init(); - - _vg_add_exit_function( vg_audio_free ); -} - -void vg_audio_preupdate(void) -{ - b8 before_working = _vg_audio.working; - _vg_audio.working = 1; - if( _vg_audio.sdl_output_device == 0 ) - _vg_audio.working = 0; - else - if( SDL_GetAudioDeviceStatus( _vg_audio.sdl_output_device ) == SDL_AUDIO_STOPPED ) - _vg_audio.working = 0; -} diff --git a/source/engine/audio_mixer.c_UNUSED b/source/engine/audio_mixer.c_UNUSED new file mode 100644 index 0000000..0209e62 --- /dev/null +++ b/source/engine/audio_mixer.c_UNUSED @@ -0,0 +1,1455 @@ +struct vg_audio _vg_audio = +{ + .master_volume_ui = 1.0f, + .dsp_enabled_ui = 1 +}; + +_Thread_local static b8 _vg_audio_thread_has_lock = 0; + +void vg_audio_lock(void) +{ + SDL_LockMutex( _vg_audio.mutex ); + _vg_audio_thread_has_lock = 1; +} + +void vg_audio_unlock(void) +{ + _vg_audio_thread_has_lock = 0; + SDL_UnlockMutex( _vg_audio.mutex ); +} + +static void vg_audio_assert_lock(void) +{ + if( _vg_audio_thread_has_lock == 0 ) + { + vg_error( "vg_audio function requires locking\n" ); + abort(); + } +} + +/* clip loading from disk + * ------------------------------------------------------------------------------- + */ +VG_TIER_2 b8 vg_audio_clip_load( audio_clip *clip, vg_stack_allocator *stack ) +{ + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_BACKGROUND ) ); + + /* load in directly */ + u32 format = clip->flags & AUDIO_FLAG_FORMAT; + if( format == k_audio_format_vorbis ) + { + if( clip->path ) + { + clip->any_data = vg_file_read( stack, clip->path, &clip->size, 0 ); + if( clip->any_data ) + { + float mb = (float)(clip->size) / (1024.0f*1024.0f); + vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb ); + return 1; + } + else + { + vg_error( "Audio failed to load '%s'\n", clip->path ); + return 0; + } + } + else + { + vg_error( "No path specified, embeded vorbis unsupported\n" ); + return 0; + } + } + else if( format == k_audio_format_stereo ) + { + vg_error( "Unsupported format (Stereo uncompressed)\n" ); + return 0; + } + else if( format == k_audio_format_bird ) + { + if( !clip->any_data ) + { + vg_error( "No data, external birdsynth unsupported\n" ); + return 0; + } + + struct synth_bird *bird = vg_stack_allocate( stack, sizeof(struct synth_bird), 8, NULL ); + bird->settings = clip->any_data; + clip->any_data = bird; + vg_info( "Loaded bird synthesis pattern (%u bytes)\n", clip->size ); + return 1; + } + else + { + if( !clip->path ) + { + vg_error( "No path specified, embeded mono unsupported\n" ); + return 0; + } + + u32 temp_frame = _vg_start_temp_frame(); + stb_vorbis_alloc alloc = + { + .alloc_buffer = vg_stack_allocate( _vg_temp_stack(), AUDIO_DECODE_SIZE, 8, "Vorbis alloc buffer (temp)" ), + .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE + }; + + u32 fsize; + void *filedata = vg_file_read( _vg_temp_stack(), clip->path, &fsize, 0 ); + int err; + stb_vorbis *decoder = stb_vorbis_open_memory( filedata, fsize, &err, &alloc ); + if( !decoder ) + { + vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", clip->path, err ); + _vg_end_temp_frame( temp_frame ); + return 0; + } + + /* only mono is supported in uncompressed */ + u32 length_samples = stb_vorbis_stream_length_in_samples( decoder ), + data_size = length_samples * sizeof(i16); + + clip->any_data = vg_stack_allocate( stack, data_size, 8, NULL ); + clip->size = length_samples; + int read_samples = stb_vorbis_get_samples_i16_downmixed( decoder, clip->any_data, length_samples ); + _vg_end_temp_frame( temp_frame ); + + if( read_samples == length_samples ) + return 1; + else + { + vg_error( "Decode error, read_samples did not match length_samples\n" ); + return 0; + } + } +} + +VG_TIER_2 u32 vg_audio_clip_loadn( audio_clip *arr, u32 count, vg_stack_allocator *stack ) +{ + u32 succeeded = 0; + for( u32 i=0; i 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; istage == 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; idecoder_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; idecoder_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; jvolume, 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; istage == 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; iactivity == 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; iperiod_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; iactivity == 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; iui_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= wk[0])) && + ((wr[1] <= wk[1]+wk[3]) && (wr[1]+wr[3] >= wk[1])) ) + { + collide = 1; + break; + } + } + + if( !collide ) + break; + else + wr[1] += 18; + } + + ui_text( ctx, wr, perf, 1, k_ui_align_middle_left, 0 ); + rect_copy( wr, overlap_buffer[ overlap_length ++ ] ); + } + } +#endif +#endif + } + + vg_audio_unlock(); +} + +static void cb_vg_audio_close( struct vg_magi_panel *me ) +{ + vg_audio_lock(); + _vg_audio.inspector_open = 0; + vg_audio_unlock(); + free( me->data ); +} + +static int cmd_vg_audio( int argc, const char *argv[] ) +{ + vg_audio_lock(); + if( _vg_audio.inspector_open ) + { + vg_error( "Only 1 audio inspector at a time.\n" ); + vg_audio_unlock(); + return 0; + } + + _vg_audio.inspector_open = 1; + vg_audio_unlock(); + ui_px w = 800, h=8*18; + struct vg_magi_panel *magi = _vg_magi_open( w,h, VG_MAGI_ALL ); + magi->title = "Audio inspector"; + + struct vg_audio_view_data *vd = malloc(sizeof(struct vg_audio_view_data)); + vd->view_3d = 0; + + magi->data = vd; + magi->ui_cb = cb_vg_audio_view; + magi->close_cb = cb_vg_audio_close; + return 1; +} + +VG_API void _vg_audio_register(void) +{ + vg_console_reg_cmd( "vg_audio", cmd_vg_audio, NULL ); + vg_console_reg_var( "volume", &_vg_audio.master_volume_ui, k_var_dtype_f32, VG_VAR_PERSISTENT ); + vg_console_reg_var( "vg_dsp", &_vg_audio.dsp_enabled_ui, k_var_dtype_i32, VG_VAR_PERSISTENT ); + vg_console_reg_var( "vg_audio_device", &_vg_audio.device_choice, k_var_dtype_str, VG_VAR_PERSISTENT ); +} + +void vg_audio_device_init(void) +{ + SDL_AudioSpec spec_desired, spec_got; + spec_desired.callback = _vg_audio_mixer; + spec_desired.channels = 2; + spec_desired.format = AUDIO_F32; + spec_desired.freq = 44100; + spec_desired.padding = 0; + spec_desired.samples = AUDIO_FRAME_SIZE; + spec_desired.silence = 0; + spec_desired.size = 0; + spec_desired.userdata = NULL; + + _vg_audio.sdl_output_device = SDL_OpenAudioDevice( _vg_audio.device_choice.buffer, 0, + &spec_desired, &spec_got, 0 ); + + vg_info( "Start audio device (%u, F32, %u) @%s\n", spec_desired.freq, AUDIO_FRAME_SIZE, + _vg_audio.device_choice.buffer ); + + if( _vg_audio.sdl_output_device ) + { + SDL_PauseAudioDevice( _vg_audio.sdl_output_device, 0 ); + vg_success( "Unpaused device %d.\n", _vg_audio.sdl_output_device ); + _vg_audio.working = 1; + } + else + { + _vg_audio.working = 0; + vg_error( + "SDL_OpenAudioDevice failed. Your default audio device must support (or transcode from):\n" + " Sample rate: 44100 hz\n" + " Buffer size: 512 samples\n" + " Channels: 2\n" + " Format: s16 or f32\n" ); + } +} + +static void vg_audio_free(void) +{ + SDL_CloseAudioDevice( _vg_audio.sdl_output_device ); + _vg_audio.sdl_output_device = 0; +} + +VG_API void _vg_audio_init(void) +{ + VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_SDL ) ); + + _vg_audio.profiler = _vg_profiler_create( "vg.audio", 1000.0f/20.0f ); + + /* fixed */ + u32 decode_size = AUDIO_DECODE_SIZE * AUDIO_CHANNELS; + _vg_audio.decoding_buffer = vg_stack_allocate( NULL, decode_size, 8, "Decoding buffers" ); + + struct audio_master_controls *master_controls = &_vg_audio.controls; + master_controls->dsp_enabled = _vg_audio.dsp_enabled_ui; + master_controls->volume_target = (f64)_vg_audio.master_volume_ui * (f64)AUDIO_VOLUME_100; + v3_copy( (v3f){1,0,0}, master_controls->listener_right_ear_direction ); + v3_zero( master_controls->listener_velocity ); + v3_zero( master_controls->listener_position ); + _vg_dsp_init(); + + _vg_audio.mutex = SDL_CreateMutex(); + if( _vg_audio.mutex == NULL ) + vg_fatal_error( "SDL2: %s\n", SDL_GetError() ); + vg_audio_device_init(); + + _vg_add_exit_function( vg_audio_free ); +} + +void vg_audio_preupdate(void) +{ + b8 before_working = _vg_audio.working; + _vg_audio.working = 1; + if( _vg_audio.sdl_output_device == 0 ) + _vg_audio.working = 0; + else + if( SDL_GetAudioDeviceStatus( _vg_audio.sdl_output_device ) == SDL_AUDIO_STOPPED ) + _vg_audio.working = 0; +} diff --git a/source/engine/console.c b/source/engine/console.c deleted file mode 100644 index 735580f..0000000 --- a/source/engine/console.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "foundation.h" -#include "console_system.h" -#include "vg_graphics.h" -#include "vg_engine.h" -#include "vg_input.h" - -struct -{ - struct queue_allocator input_history, log_history; - b8 open; -} -_console; - -struct log_history_item -{ - u32 type; - c8 buffer[]; -}; - -static c8 _input_buffer[ 1024 ]; - -static void _log_listener( const c8 *line, u32 length, u32 type ) -{ -again:; - struct log_history_item *item = queue_alloc( &_console.log_history, sizeof( struct log_history_item ) + length+1 ); - if( !item ) - { - queue_pop( &_console.log_history ); - goto again; - } - - item->type = type; - buffer_copy( line, 0, item->buffer, length+1 ); -} - -void _engine_console_init(void) -{ - _log_add_listener( _log_listener, ($info | $ok | $warning | $error | $fatal | $shell), 0 ); - queue_init( &_console.input_history, _heap_allocate( BYTES_KB(2) ), BYTES_KB(2) ); - queue_init( &_console.log_history, _heap_allocate( BYTES_KB(4) ), BYTES_KB(4) ); -} - -void _engine_console_update(void) -{ - for( u8 j=0; j<_input_button_down( k_input_action_console, 0 ); j ++ ) - { - _console.open ^= 0x1; - u32 console_mask = (1 << k_input_layer_console) | (1 << k_input_layer_ui); - _input_layer_whitelist( _console.open? console_mask: 0 ); - } -} - -void _engine_console_ui(void) -{ - if( !_console.open ) - return; - - i16 kerning[3]; - _font_get_kerning( kerning ); - i16 history_box[4] = { 0, 0, _engine.w, kerning[1] * 32 }; - - if( _console.log_history.allocation_count ) - { - u32 item_offset = _console.log_history.head_offset; - for( u32 i=0; i<32; i ++ ) - { - struct log_history_item *item = queue_data( &_console.log_history, item_offset ); - i16 line_rect[4] = (i16[]){ 0, kerning[1] * (31-i), _engine.w, kerning[1] }; - _ui_text( line_rect, item->buffer, k_ui_align_x_left, (union colour){{128,128,128,255}} ); - - if( !queue_previous( &_console.log_history, item_offset, &item_offset ) ) - break; - } - } - - _graphics_line_rect( history_box, (union colour){{0,255,255,255}} ); - - enum textbox_action action = _ui_textbox( (i16[]){ 0, kerning[1]*32, _engine.w, kerning[1] }, - _input_buffer, sizeof(_input_buffer), 1, UI_AUTOFOCUS ); - - if( action == k_textbox_enter ) - { - _console_execute( _input_buffer, 0, 0 ); - _input_buffer[0] = '\0'; - } -} diff --git a/source/engine/engine.kv b/source/engine/engine.kv index 4c570b3..0561b73 100644 --- a/source/engine/engine.kv +++ b/source/engine/engine.kv @@ -23,55 +23,9 @@ enable SDL append ../foundation/foundation.kv add vg_engine.c -hook -{ - event OPTIONS - function _engine_options -} -event -{ - name ENGINE_NEW_FRAME - prototype "void" -} -event -{ - name ENGINE_FIXED_UPDATE - prototype "void" -} -event -{ - name ENGINE_RENDER - prototype "void" -} -event -{ - name ENGINE_UI - prototype "void" -} -event -{ - name ENGINE_WINDOW_RESIZE - prototype "void" -} - add vg_ui.c -hook -{ - event START - function _engine_ui_init -} - add vg_input.c -hook -{ - event START - function _input_init -} -hook -{ - event ENGINE_NEW_FRAME - function _input_update -} + ccmd { name bind @@ -101,38 +55,9 @@ ccmd } append ../console/console_system.kv -add console.c -hook -{ - event START - function _engine_console_init -} -hook -{ - event ENGINE_NEW_FRAME - function _engine_console_update -} -hook -{ - event ENGINE_UI - function _engine_console_ui - affinity 9999 -} - +add vg_console.c add vg_framebuffer.c -hook -{ - event ENGINE_WINDOW_RESIZE - function _framebuffer_resize -} - add vg_render.c -hook -{ - event START - function _vg_render_init -} - add vg_shader.c input_layer @@ -337,11 +262,6 @@ shader } } -hook -{ - event START - function "_shader_init" -} ccmd { name reload_shaders @@ -349,25 +269,9 @@ ccmd description "Recompile shaders (DEVELOPER ONLY!)" } - - add vg_camera.c - - add vg_tex.c -hook -{ - event START - function _vg_tex_init -} - - add vg_lines.c -hook -{ - event START - function _vg_lines_init -} cvar { name vg_lines_enable @@ -400,18 +304,6 @@ shader add vg_audio.c add vg_audio_dsp.c add vg_audio_vorbis.c -hook -{ - affinity -3008 - event START - function _audio_init -} -hook -{ - affinity -6004 - event START - function _dsp_init -} cvar { name vg_audio @@ -421,6 +313,253 @@ cvar description "Show audio info" } -add model.c -add metascene.c -add array_file.c +add vg_model.c +add vg_entity.c +add vg_metascene.c +add vg_af.c + +entity +{ + name ent_spawn + id 2 + + parameter + { + name transform + type transform + } + parameter + { + name name + type pstr + } + parameter + { + name flags + type u32 + flag + { + name locked + value 0x1 + } + flag + { + name group1 + value 0x2 + } + flag + { + name group2 + value 0x4 + } + flag + { + name group3 + value 0x8 + } + } + function + { + name lantern + } + function + { + name boat + } + function + { + name me + } +} +entity +{ + name ent_water + id 5 + function + { + name enable + } + function + { + name disable + } + function + { + name show + } +} +entity +{ + name ent_volume + id 6 + function + { + name enable + } + function + { + name disable + } +} +entity +{ + name ent_audio + id 7 +} +entity +{ + name ent_marker + id 8 + function + { + name push + } + function + { + name tooltip + } + function + { + name tooltip_special + } +} +entity +{ + name ent_camera + id 13 + function + { + name focus + } + function + { + name unfocus + } +} +entity +{ + name ent_ccmd + id 17 + function + { + name exec + } +} +entity +{ + name ent_cubemap + id 21 +} +entity +{ + name ent_prop + id 23 +} +entity +{ + name ent_armature + id 28 +} +entity +{ + name ent_atom + id 30 + function + { + name pass_equal + } + function + { + name pass_greater + } + function + { + name set + } + function + { + name set_or + } + function + { + name set_and + } +} +entity +{ + name ent_cutscene + id 31 + function + { + name play + } + function + { + name pause + } + function + { + name resume + } + function + { + name end + } +} +entity +{ + name ent_light + id 32 + + parameter + { + name transform + type transform + } + parameter + { + name flags + type u32 + flag + { + name daytime + value 0x1 + } + flag + { + name off + value 0x2 + } + } + parameter + { + name type + type u32 + } + parameter + { + name colour + type vec4 + ui rgbe + } + parameter + { + name angle + type f32 + } + parameter + { + name range + type f32 + } + + function + { + name on + argument + { + name enable + type i32 + } + } +} diff --git a/source/engine/metascene.c b/source/engine/metascene.c deleted file mode 100644 index f7328ac..0000000 --- a/source/engine/metascene.c +++ /dev/null @@ -1,499 +0,0 @@ -#include "vg_engine.h" -#include "metascene.h" -#include "common_maths.h" -#include "model.h" -#include "model_entity.h" -#include "shader_props.h" -#include "array_file.h" -#include "vg_async.h" - -void metascene_load( struct metascene *ms, const c8 *path, struct stack_allocator *stack ) -{ - ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) ); - zero_buffer( ms, sizeof(struct metascene) ); - - struct stream stream; - ASSERT_CRITICAL( stream_open_file( &stream, path, k_stream_read ) ); - - u32 temp_frame = _start_temporary_frame(); - { - struct array_file_context af; - ASSERT_CRITICAL( af_open_stream( &af, &stream, MS_VERSION_MIN, MS_VERSION_NR, _temporary_stack_allocator() ) ); - - struct array_file_ptr strings; - af_load_array( &af, &strings, "strings", stack, 1 ); - ms->packed_strings = strings.data; - af_load_array( &af, &ms->infos, "ms_scene_info", stack, sizeof(struct ms_scene_info) ); - af_load_array( &af, &ms->instances, "ms_instance", stack, sizeof(struct ms_instance) ); - af_load_array( &af, &ms->overrides, "ms_override", stack, sizeof(struct ms_override) ); - af_load_array( &af, &ms->strips, "ms_strip", stack, sizeof(struct ms_strip) ); - af_load_array( &af, &ms->tracks, "ms_track", stack, sizeof(struct ms_track) ); - af_load_array( &af, &ms->keyframes, "ms_keyframe", stack, sizeof(struct ms_keyframe) ); - af_load_array( &af, &ms->cameras, "ent_camera", stack, sizeof(struct ent_camera) ); - af_load_array( &af, &ms->audios, "ent_audio", stack, sizeof(struct ent_audio) ); - af_load_array( &af, &ms->audio_clips, "ent_audio_clip", stack, sizeof(struct ent_audio_clip) ); - af_load_array( &af, &ms->curves, "ms_curves", stack, sizeof(struct ms_curve_keyframe) ); - - ASSERT_CRITICAL( af_arrcount( &ms->infos ) ); - struct ms_scene_info *src_inf = af_arritm( &ms->infos, 0 ); - ms->info = *src_inf; - - stream_close( &stream ); - } - _end_temporary_frame( temp_frame ); -} - -u32 skeleton_bone_id( struct ms_skeleton *skele, const c8 *name ) -{ - for( u32 i=1; ibone_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; ico, 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; ibone_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; ibone_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; ibone_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; ibone_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; iik_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; ibone_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; ibone_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; iik_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; ibone_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; ibone_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; ibone_count; i ++ ) - { - struct ms_skeleton_bone *sb = &skele->bones[i]; - f32 p0[3], p1[3]; - v3_copy( sb->co, p0 ); - v3_add( p0, sb->end, p1 ); - - m4x3_mulv( final_mtx[i], p0, p0 ); - m4x3_mulv( final_mtx[i], p1, p1 ); - - if( sb->flags & k_bone_flag_deform ) - { - if( sb->flags & k_bone_flag_ik ) - vg_line( p0, p1, 0xff0000ff ); - else - vg_line( p0, p1, 0xffcccccc ); - } - else - vg_line( p0, p1, 0xff00ffff ); - } -} -#endif diff --git a/source/engine/metascene.h b/source/engine/metascene.h deleted file mode 100644 index 505d353..0000000 --- a/source/engine/metascene.h +++ /dev/null @@ -1,185 +0,0 @@ -#pragma once -#include "foundation.h" -#include "array_file.h" -#include "model.h" - -#define MS_VERSION_NR 2 -#define MS_VERSION_MIN 2 - -struct ms_scene_info -{ - f32 framerate; - u32 end_frame; -}; - -struct metascene -{ - const void *packed_strings; - struct ms_scene_info info; - struct array_file_ptr infos, - instances, - overrides, - strips, - tracks, - keyframes, - curves, - - audios, /* kinda temp? */ - audio_clips, - cameras; -}; - -struct ms_instance -{ - u32 pstr_name, - override_start, - override_count; -}; - -struct ms_override -{ - u32 entity_type, pstr_name; - struct mdl_transform transform; -}; - -enum ms_strip_mode -{ - k_ms_strip_mode_keyframes = 0x1, - k_ms_strip_mode_curves = 0x2, - k_ms_strip_mode_animation = 0x1|0x2, - - k_ms_strip_mode_camera = 0x10, - k_ms_strip_mode_event = 0x20, - k_ms_strip_mode_subtitle = 0x40, - k_ms_strip_mode_fadeout = 0x80, -}; - -struct ms_strip -{ - u8 mode; - i32 offset; - - union - { - struct - { - u32 start, count, length, - pstr_name, - pstr_internal_name, - instance_id, object_id; - f32 timing_offset; - } - strip; - - struct - { - u32 entity_id; - } - camera; - - struct - { - u32 pstr_en, res0, res1, res2; - u8 character; - } - subtitle; - - struct - { - u32 pstr_string; - } - event; - }; -}; - -struct ms_track -{ - u32 keyframe_start, - keyframe_count, - pstr_datapath, - semantic_type; -}; - -struct ms_curve_keyframe -{ - f32 co[2], l[2], r[2]; -}; - -struct ms_keyframe -{ - f32 co[3], s[3]; - f32 q[4]; -}; - -/* skeletons - * ------------------------------------------------------------------------------------------------------------------ */ - -struct ms_skeleton -{ - struct ms_skeleton_bone - { - f32 co[3], end[3]; - u32 parent; - - u32 flags; - int defer; - - //ms_keyframe kf; - struct mdl_bone *orig_bone; - u32 collider; // TODO: SOA - f32 hitbox[2][3]; // TODO: SOA - const char *name; // TODO: SOA - } - *bones; - u32 bone_count; - - struct ms_skeleton_ik - { - u32 lower, upper, target, pole; - f32 ia[3][3], ib[3][3]; - } - *ik; - u32 ik_count; - u32 collider_count, - bindable_count; -}; - -void metascene_load( struct metascene *ms, const c8 *path, struct stack_allocator *stack ); -void keyframe_copy_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, i32 num ); -void keyframe_rotate_around( struct ms_keyframe *kf, f32 origin[3], f32 offset[3], f32 q[4] ); -void keyframe_lerp( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd ); -void keyframe_lerp_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd, i32 count ); -void skeleton_alloc_from( struct ms_skeleton *skele, struct stack_allocator *stack, - struct vg_model *model, struct mdl_armature *armature ); -u32 skeleton_bone_id( struct ms_skeleton *skele, const c8 *name ); -void skeleton_lerp_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd ); -void skeleton_copy_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfd ); - -struct ms_skeletal_animation -{ - struct ms_strip *strip; - struct ms_keyframe *keyframes_base; - f32 framerate; -}; - -void skeleton_sample_anim( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output ); -int skeleton_sample_anim_clamped( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output ); - -enum anim_apply -{ - k_anim_apply_always, - k_anim_apply_defer_ik, - k_anim_apply_deffered_only, - k_anim_apply_absolute -}; - -void skeleton_apply_pose( struct ms_skeleton *skele, struct ms_keyframe *pose, enum anim_apply passtype, f32 final_mtx[][4][3] ); -void skeleton_decompose_mtx_absolute( struct ms_skeleton *skele, struct ms_keyframe *anim, f32 final_mtx[][4][3] ); -void skeleton_inverse_for_ik( struct ms_skeleton *skele, f32 ivaxis[3], u32 id, f32 inverse[3][3] ); -void skeleton_create_inverses( struct ms_skeleton *skele ); -void skeleton_apply_transform( struct ms_skeleton *skele, f32 transform[4][3], f32 final_mtx[][4][3] ); -void skeleton_apply_inverses( struct ms_skeleton *skele, f32 final_mtx[][4][3] ); -void skeleton_apply_ik_pass( struct ms_skeleton *skele, f32 final_mtx[][4][3] ); -void skeleton_apply_standard( struct ms_skeleton *skele, struct ms_keyframe *pose, f32 transform[4][3], f32 final_mtx[][4][3] ); -void skeleton_setup( struct ms_skeleton *skele, struct vg_model *model, u32 index, struct stack_allocator *stack ); -void skeleton_debug( struct ms_skeleton *skele, f32 final_mtx[][4][3] ); diff --git a/source/engine/model.c b/source/engine/model.c deleted file mode 100644 index c02999c..0000000 --- a/source/engine/model.c +++ /dev/null @@ -1,562 +0,0 @@ -#include "foundation.h" -#include "common_maths.h" -#include "vg_async.h" - -#include "model.h" -#include "array_file.h" -#include "vg_opengl.h" -#include "shader_props.h" -#include - -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; imodel->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; imodel->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; imodel->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; imodel->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; jworkshop.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; imodel->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; imodel->submesh_count; i++ ) - { - struct mdl_submesh *sm = &ctx->model->submeshes[ i ]; - u32 *indices = args->indice_buffer + sm->indice_start; - - for( u32 j=0; jindice_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; imodel->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; itexture_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; imesh_count; i++ ) - { - struct mdl_mesh *mesh = &model->meshes[ i ]; - if( af_str_eq( model->packed_strings, mesh->pstr_name, name, hash ) ) - return i; - } - return -1; -} - -i32 vg_model_get_submesh_index( struct vg_model *model, const c8 *mesh_name ) -{ - i32 mesh_index = vg_model_get_mesh_index( model, mesh_name ); - if( mesh_index == -1 ) - return -1; - - struct mdl_mesh *mesh = &model->meshes[ mesh_index ]; - if( !mesh->submesh_count ) - return -1; - return mesh->submesh_start; -} - -void vg_model_bind_texture( struct vg_model *model, GLuint target, u32 tex_id, u32 slot ) -{ - if( tex_id ) - { - union mdl_texture *mdl_tex = &model->textures[ tex_id -1 ]; - vg_tex_bind( target, &mdl_tex->tex, slot ); - } - else - vg_tex_bind( target, NULL, slot ); -} diff --git a/source/engine/model.h b/source/engine/model.h deleted file mode 100644 index f6b260c..0000000 --- a/source/engine/model.h +++ /dev/null @@ -1,279 +0,0 @@ -#pragma once -#include "vg_tex.h" -#include "shader_props.h" -#include "array_file.h" - -#define VG_MODEL_VERSION_MIN 110 -#define VG_MODEL_VERSION_NR 110 - -enum mdl_shader -{ - k_shader_standard = 0, - k_shader_standard_cutout = 1, - k_shader_terrain_blend = 2, - k_shader_standard_vertex_blend = 3, - k_shader_water = 4, - k_shader_invisible = 5, - k_shader_boundary = 6, - k_shader_fxglow = 7, - k_shader_cubemap = 8, - k_shader_walking = 9, - k_shader_foliage = 10, - k_shader_workshop = 11, - k_shader_pipe = 12, - k_shader_override = 30000 -}; - -enum mdl_surface_prop -{ - k_surface_prop_concrete = 0, - k_surface_prop_wood = 1, - k_surface_prop_grass = 2, - k_surface_prop_tiles = 3, - k_surface_prop_metal = 4, - k_surface_prop_snow = 5, - k_surface_prop_sand = 6 -}; - -enum material_flag -{ - k_material_flag_skate_target = 0x0001, - k_material_flag_collision = 0x0002, - k_material_flag_grow_grass = 0x0004, - k_material_flag_grindable = 0x0008, - k_material_flag_invisible = 0x0010, - k_material_flag_boundary = 0x0020, - k_material_flag_preview_visibile = 0x0040, - k_material_flag_walking = 0x0080, - - k_material_flag_ghosts = - k_material_flag_boundary| - k_material_flag_invisible| - k_material_flag_walking, - - k_material_flag_spritesheet = 0x1000 -}; - -#pragma pack(push,1) - -/* 48 byte */ -struct mdl_vert -{ - f32 co[3], /* 3*32 */ - norm[3]; /* 3*32 */ - f32 uv[2]; /* 2*32 */ - - u8 colour[4]; /* 4*8 */ - u16 weights[4];/* 4*16 */ - u8 groups[4]; /* 4*8 */ -}; - -#pragma pack(pop) - -struct mdl_transform -{ - f32 co[3], s[3]; - f32 q[4]; -}; - -void mdl_transform_identity( struct mdl_transform *transform ); -void mdl_transform_vector( struct mdl_transform *transform, f32 vec[3], f32 dest[3] ); -void mdl_transform_point( struct mdl_transform *transform, f32 co[3], f32 dest[3] ); -void mdl_transform_mul( struct mdl_transform *a, struct mdl_transform *b, struct mdl_transform *d ); - -#if (VG_MODEL_VERSION_MIN <= 105) -struct mdl_material_v101 -{ - u32 pstr_name, - shader, - flags, - surface_prop; - - f32 colour[4], - colour1[4]; - - u32 tex_diffuse, /* Indexes start from 1. 0 if missing. */ - tex_none0, - tex_none1; -}; -#endif - -struct mdl_material -{ - u32 pstr_name, - shader, - flags, - surface_prop; - - union - { - struct - { - u32 offset, size; /* indexes shader data */ - } - kvs; - u32 kv_root; /* runtime */ - } - props; -}; - -struct mdl_bone -{ - f32 co[3], end[3]; - u32 parent, - collider, - ik_target, - ik_pole, - flags, - pstr_name; - - f32 hitbox[2][3]; - f32 conevx[3], conevy[3], coneva[3]; - f32 conet; -}; - -enum bone_flag -{ - k_bone_flag_deform = 0x00000001, - k_bone_flag_ik = 0x00000002, - k_bone_flag_cone_constraint = 0x00000004 -}; - -enum bone_collider -{ - k_bone_collider_none = 0, - k_bone_collider_box = 1, - k_bone_collider_capsule = 2 -}; - -struct mdl_armature -{ - struct mdl_transform transform; - u32 bone_start, - bone_count, - anim_start_OBSOLETE_107, // obsolete 107+ - anim_count_OBSOLETE_107, // . - pstr_name; // v107+ -}; - -struct mdl_submesh -{ - u32 indice_start, - indice_count, - vertex_start, - vertex_count; - - f32 bbx[2][3]; - u16 material_id, flags; -}; - -enum esubmesh_flags -{ - k_submesh_flag_none = 0x0000, - k_submesh_flag_consumed = 0x0001 -}; - -struct mdl_mesh -{ - struct mdl_transform transform; - u32 submesh_start, - submesh_count, - pstr_name, - entity_id, /* upper 16 bits: type, lower 16 bits: index. hgn: 11.06.2025 Is this still used??? - hgn: 25.10.2025 Yes it is. */ - armature_id; -}; - -struct mdl_file -{ - u32 pstr_path, - pack_offset, - pack_size; -}; - -union mdl_texture -{ - struct mdl_file file; - struct vg_tex tex; -}; - -struct vg_model -{ - u32 version; - u32 flags; - - /* VG_MODEL_CPU_METADATA ---------------------- */ - const void *packed_strings; - union shader_props *shader_props; - - struct mdl_mesh *meshes; - u32 mesh_count; - - struct mdl_submesh *submeshes; - u32 submesh_count; - - struct mdl_material *materials; - u32 material_count; - - union mdl_texture *textures; - u32 texture_count; - - struct mdl_armature *armatures; - u32 armature_count; - - struct mdl_bone *bones; - u32 bone_count; - - /* VG_MODEL_CPU_MESHES ---------------------- */ - struct mdl_vert *verts; - u32 vert_count; - u32 *indices; - u32 indice_count; - - u32 pack_base_offset; - - /* VG_MODEL_GPU_MESHES ----------------------- */ - GLuint vao, vbo, ebo; -}; - -#define VG_MODEL_CPU_METADATA 0x4 -#define VG_MODEL_CPU_MESHES 0x8 -# define VG_MODEL_GPU_TEXTURES 0x1 -# define VG_MODEL_GPU_MESHES 0x2 -# define VG_MODEL_ENGINE_STANDARD (VG_MODEL_CPU_METADATA|VG_MODEL_GPU_TEXTURES|VG_MODEL_GPU_MESHES) -# define VG_MODEL_ENGINE_PROCEDURAL_SOURCE (VG_MODEL_CPU_METADATA|VG_MODEL_GPU_TEXTURES|VG_MODEL_CPU_MESHES) - -b8 vg_model_load( struct vg_model *model, u32 model_flags, const c8 *path, struct stack_allocator *stack ); -void vg_model_unload_gpu( struct vg_model *model ); - -/* Sub functions for each part of the load process - * --------------------------------------------------------------------------------------------------------------- */ -struct vg_model_stream_context -{ - struct stream stream; - struct array_file_context af; - struct vg_model *model; - u32 temp_frame; -}; - -b8 vg_model_stream_open( struct vg_model_stream_context *ctx, struct vg_model *model, const c8 *path ); -void vg_model_stream_close( struct vg_model_stream_context *ctx ); - -/* Parts which you might want (currently they all require metadata to be explicitly loaded) */ -void vg_model_stream_metadata( struct vg_model_stream_context *ctx, struct stack_allocator *stack ); -void vg_model_stream_meshes_cpu( struct vg_model_stream_context *ctx, struct stack_allocator *stack ); - -void vg_model_stream_meshes_gpu( struct vg_model_stream_context *ctx, u32 *fixup_table ); -void vg_model_stream_textures_gpu( struct vg_model_stream_context *ctx ); - -struct stream *vg_model_stream_pack_stream( struct vg_model_stream_context *ctx, struct mdl_file *file ); - -/* Rendering operations - * ----------------------------------------------------------------------------------------------------------------- */ -void vg_model_bind_mesh( struct vg_model *model ); -void vg_model_bind_texture( struct vg_model *model, GLuint target, u32 tex_id, u32 slot ); -void vg_model_draw_elements( u32 start, u32 count ); -void vg_model_draw_submesh( struct mdl_submesh *sm ); -i32 vg_model_get_mesh_index( struct vg_model *model, const c8 *name ); -i32 vg_model_get_submesh_index( struct vg_model *model, const c8 *mesh_name ); -void mdl_transform_m4x3( struct mdl_transform *transform, f32 mtx[4][3] ); diff --git a/source/engine/model_entity.h b/source/engine/model_entity.h deleted file mode 100644 index 682bb66..0000000 --- a/source/engine/model_entity.h +++ /dev/null @@ -1,782 +0,0 @@ -#pragma once -#include "model.h" - -enum entity_alias -{ - k_ent_none = 0, - k_ent_gate = 1, - k_ent_spawn = 2, - k_ent_route_node = 3, - k_ent_route = 4, - k_ent_water = 5, - k_ent_volume = 6, - k_ent_audio = 7, - k_ent_marker = 8, - k_ent_font = 9, - k_ent_font_variant= 10, - k_ent_traffic = 11, - k_ent_skateshop = 12, - k_ent_camera = 13, - k_ent_swspreview = 14, - k_ent_deleted1 = 15, //k_ent_menuitem = 15, - k_ent_worldinfo = 16, - k_ent_ccmd = 17, - k_ent_objective = 18, - k_ent_challenge = 19, - k_ent_deleted0 = 20, //k_ent_relay = 20, - k_ent_cubemap = 21, - k_ent_miniworld = 22, - k_ent_prop = 23, - k_ent_list = 24, - k_ent_region = 25, - k_ent_glider = 26, - k_ent_npc = 27, - k_ent_armature = 28, - k_ent_script = 29, - k_ent_atom = 30, - k_ent_cutscene = 31, - k_ent_light = 32, - k_mdl_mesh = 33, - k_editer_property = 34, - k_editer_item = 35, - - k_ent_flame = 200, // FIXME: Move thiese into own file per-game. - k_ent_waterfall = 201, - k_ent_rb2 = 202, - k_ent_heatpipe = 203, - k_ent_waterlevel = 204, - k_ent_viewstone = 205, - k_ent_gauge = 206, - k_ent_particle = 207, - k_ent_deep_npc = 208, - k_ent_max -}; - -// FIXME -static const char *_entity_alias_str[] = -{ - [k_ent_none] = "none/null", - [k_ent_gate] = "ent_gate", - [k_ent_spawn] = "ent_spawn", - [k_ent_route_node] = "ent_route_node", - [k_ent_route] = "ent_route", - [k_ent_water] = "ent_water", - [k_ent_volume] = "ent_volume", - [k_ent_audio] = "ent_audio", - [k_ent_marker] = "ent_marker", - [k_ent_font] = "ent_font", - [k_ent_font_variant] = "ent_font_variant", - [k_ent_traffic] = "ent_traffic", - [k_ent_skateshop] = "ent_skateshop", - [k_ent_camera] = "ent_camera", - [k_ent_swspreview] = "ent_swspreview", - //[k_ent_menuitem] = "ent_menuitem", - [k_ent_worldinfo] = "ent_worldinfo", - [k_ent_ccmd] = "ent_ccmd", - [k_ent_objective] = "ent_objective", - [k_ent_challenge] = "ent_challenge", - //[k_ent_relay] = "ent_relay", - [k_ent_cubemap] = "ent_cubemap", - [k_ent_miniworld] = "ent_miniworld", - [k_ent_prop] = "ent_prop", - [k_ent_region] = "ent_region", - [k_ent_glider] = "ent_glider", - [k_ent_npc] = "ent_npc", - [k_ent_armature] = "mdl_armature", - [k_ent_script] = "ent_script", - [k_ent_atom] = "ent_atom", - [k_ent_cutscene] = "ent_cutscene", - [k_ent_light] = "ent_light", - [k_ent_max] = NULL -}; - -static inline u32 mdl_entity_id_type( u32 entity_id ) -{ - return (entity_id & 0x0fff0000) >> 16; -} - -static inline u32 mdl_entity_id_id( u32 entity_id ) -{ - return entity_id & 0x0000ffff; -} - -static inline u32 mdl_entity_id( u32 type, u32 index ) -{ - return (type & 0xfffff)<<16 | (index & 0xfffff); -} - -enum ent_spawn_flag -{ - k_ent_spawn_flag_locked = 0x1, - k_ent_spawn_flag_group_1 = 0x10, - k_ent_spawn_flag_group_2 = 0x20, - k_ent_spawn_flag_group_3 = 0x40, - k_ent_spawn_flag_group_4 = 0x80 -}; - -struct ent_spawn -{ - struct mdl_transform transform; - u32 pstr_name; - u32 flags; -}; - -enum light_type -{ - k_light_type_point = 0, - k_light_type_spot = 1 -}; - -#define ENT_LIGHT_FLAG_DAYTIME 0x1 -#define ENT_LIGHT_FLAG_OFF 0x2 - -struct ent_light -{ - struct mdl_transform transform; - u32 flags, - type; - f32 colour[4]; - f32 angle, - range; - - union - { - /* These are temporary, we could remove them from the file structure. */ - struct - { - f32 inverse_world[4][3]; - f32 angle_sin_cos[2]; - }; - - struct - { - f32 lerp_start[3], lerp_end[3]; - f32 lerp_duration, timer; - }; - }; -}; - -/* v101 */ -#if 0 -enum gate_type{ - k_gate_type_unlinked = 0, - k_gate_type_teleport = 1, - k_gate_type_nonlocal_unlinked = 2, - k_gate_type_nonlocel = 3 -}; -#endif - -enum list_alias_type -{ - k_list_alias_none = 0, - k_list_alias_string = 1 -}; - -struct ent_list -{ - u16 entity_ref_start, entity_ref_count; - u8 alias_type, none0, none1, none2; - u32 pstr_alias; -}; - -struct file_entity_ref -{ - u32 entity_id, pstr_alias; -}; - -/* v102+ */ -enum ent_gate_flag -{ - k_ent_gate_linked = 0x1, /* this is a working portal */ - k_ent_gate_nonlocal = 0x2, /* use the key string to link this portal. - NOTE: if set, it adds the flip flag. */ - k_ent_gate_flip = 0x4, /* flip direction 180* for exiting portal */ - k_ent_gate_custom_mesh = 0x8, /* use a custom submesh instead of default */ - k_ent_gate_locked = 0x10,/* has to be unlocked to be useful */ - - k_ent_gate_clean_pass = 0x20,/* player didn't rewind while getting here */ - k_ent_gate_no_linkback = 0x40,/* NONLOCAL Recievers are not allowed to linkback through this gate */ - k_ent_gate_passive = 0x80 -}; - -struct ent_gate -{ - u32 flags, - target, - key; - - f32 dimensions[3], - co[2][3]; - - f32 q[2][4]; - - /* runtime */ - f32 to_world[4][3], transport[4][3]; - union - { - u32 timing_version; - u16 remote_addon_id; - - struct - { - u8 ref_count; - }; - }; - - union - { - f64 timing_time; - u32 cubemap_id; - }; - - u16 routes[4]; /* routes that pass through this gate */ - u8 route_count; - - /* v102+ */ - u32 submesh_start, submesh_count; -}; - -struct ent_route_node -{ - f32 co[3]; - u8 ref_count, ref_total; -}; - -struct ent_path_index -{ - u16 index; -}; - -struct ent_checkpoint -{ - u16 gate_index, - path_start, - path_count; - - /* EXTENSION */ - f32 best_time; -}; - -enum ent_route_flag -{ - k_ent_route_flag_achieve_silver = 0x1, - k_ent_route_flag_achieve_gold = 0x2, - - k_ent_route_flag_out_of_zone = 0x10, - k_ent_region_flag_hasname = 0x20, - k_ent_route_flag_target = 0x40 -}; - -struct ent_route -{ - union - { - struct mdl_transform transform; - u32 official_track_id; /* TODO: remove this */ - } - anon; - - u32 pstr_name; - u16 checkpoints_start, - checkpoints_count; - - f32 colour[4]; - - /* runtime */ - u16 active_checkpoint, - valid_checkpoints; - - f32 factive; - f32 board_transform[4][3]; - struct mdl_submesh sm; - f64 timing_base; - - u32 id_camera; /* v103+ */ - - /* v104+, but always accessible */ - u32 flags; - f64 best_laptime; - f32 ui_stopper, ui_residual; - i16 ui_first_block_width, ui_residual_block_w; -}; - -struct ent_water -{ - struct mdl_transform transform; - f32 max_dist; - u32 reserved0, reserved1; -}; - -struct ent_audio_clip -{ - union - { - struct mdl_file file; - // struct audio_clip clip; FIXME, MUST ALLOCATE THIS LATER INSTEAD OF PUTTING IT IN THE FUCKING FILE STRUCT. WHO CARES IF WE LOSE HALF A KB - } - _; - - f32 probability; -}; - -struct volume_particles -{ - u32 blank, blank2; -}; - -struct volume_trigger -{ - u32 blank1; - i32 blank; -}; - -struct volume_interact -{ - i32 blank; - u32 pstr_text; -}; - -enum ent_volume_flag -{ - k_ent_volume_flag_particles = 0x1, - k_ent_volume_flag_disabled = 0x2, - k_ent_volume_flag_removed0 = 0x4, - k_ent_volume_flag_interact = 0x8, - k_ent_volume_flag_water = 0x10, - k_ent_volume_flag_repeatable= 0x20 -}; - -struct ent_volume -{ - struct mdl_transform transform; - f32 to_world[4][3]; - - union - { - f32 to_local[4][3]; - f32 particle_co[3]; - }; - - u32 flags; - u32 filter; - union - { - struct volume_trigger trigger; - struct volume_interact interact; - struct volume_particles particles; - }; -}; - -struct ent_audio -{ - struct mdl_transform transform; - u32 flags, - clip_start, - clip_count; - f32 volume, crossfade; - u32 behaviour, - group, - probability_curve, - max_channels; -}; - -enum -{ - k_ent_marker_flag_hidden = 0x1, - k_ent_marker_flag_gui_icon = 0x8 -}; - -struct ent_marker -{ - struct mdl_transform transform; - u32 pstr_alias; - u32 flags; -}; - -enum skateshop_type -{ - k_skateshop_type_boardshop = 0, - k_skateshop_type_charshop = 1, - k_skateshop_type_worldshop = 2, - k_skateshop_type_DELETED = 3, - k_skateshop_type_server = 4 -}; - -struct ent_skateshop -{ - struct mdl_transform transform; - u32 type, id_camera; - - union - { - struct - { - u32 id_display, - id_info, - id_rack; - } - boards; - - struct - { - u32 id_display, - id_info, - id_rack; - } - character; - - struct - { - u32 id_display, - id_info; - } - worlds; - - struct - { - u32 id_lever; - } - server; - }; -}; - -struct ent_swspreview -{ - u32 id_camera, id_display, id_display1; -}; - -struct ent_traffic -{ - struct mdl_transform transform; - u32 submesh_start, - submesh_count, - start_node, - node_count; - f32 speed, - t; - u32 index; /* into the path */ -}; - -struct ent_camera -{ - f32 co[3], r[3]; - f32 fov; -}; - -#if (VG_MODEL_VERSION_MIN <= 107) -struct ent_camera_v107 -{ - struct mdl_transform transform; - f32 fov; -}; - -static inline void fix_ent_camera_v107( struct ent_camera_v107 *old, struct ent_camera *new ) -{ - f32 dir[3] = {0.0f,-1.0f,0.0f}; - mdl_transform_vector( &old->transform, dir, dir ); - v3_angles( dir, new->r ); - v3_copy( old->transform.co, new->co ); - new->fov = old->fov; -} -#endif - -enum world_flag -{ - k_world_flag_fixed_time = 0x1, - k_world_flag_water_is_safe = 0x2, - k_world_flag_no_skating = 0x4, - k_world_flag_no_rewind = 0x8 -}; - -struct ent_worldinfo -{ - u32 pstr_name, pstr_author, pstr_desc; - f32 timezone; - u32 pstr_skybox; - u32 flags; - f32 wind_scale; -}; - -enum channel_behaviour -{ - k_channel_behaviour_unlimited = 0, - k_channel_behaviour_discard_if_full = 1, - k_channel_behaviour_crossfade_if_full = 2 -}; - -enum probability_curve{ - k_probability_curve_constant = 0, - k_probability_curve_wildlife_day = 1, - k_probability_curve_wildlife_night = 2 -}; - -struct ent_font -{ - u32 alias, - variant_start, - variant_count, - glyph_start, - glyph_count, - glyph_utf32_base; -}; - -struct ent_font_variant -{ - u32 name, - material_id; -}; - -struct ent_glyph -{ - f32 size[2]; - u32 indice_start, - indice_count; -}; - -struct ent_ccmd -{ - u32 pstr_command; -}; - -enum ent_script_flag -{ - k_ent_script_flag_linked = 0x1 -}; - -struct ent_script -{ - union - { - u32 pstr_script_name, /* When its in the file */ - script_id; /* When its runtime */ - }; - - u32 entity_list_id; - u32 flags; -}; - -enum ent_atom_flag -{ - k_ent_atom_global = 0x1, - k_ent_atom_scrap = 0x2 -}; - -struct ent_atom -{ - union - { - u32 pstr_alias; - i32 scrap_value; - }; - - u32 flags; -}; - -enum ent_cutscene_flag -{ - k_ent_cutscene_freeze_player = 0x1, -}; - -struct ent_cutscene -{ - u32 pstr_path, - flags; -}; - -enum ent_objective_filter{ - k_ent_objective_filter_none = 0x00000000, - k_ent_objective_filter_trick_shuvit = 0x00000001, - k_ent_objective_filter_trick_kickflip = 0x00000002, - k_ent_objective_filter_trick_treflip = 0x00000004, - k_ent_objective_filter_trick_any = - k_ent_objective_filter_trick_shuvit| - k_ent_objective_filter_trick_treflip| - k_ent_objective_filter_trick_kickflip, - k_ent_objective_filter_flip_back = 0x00000008, - k_ent_objective_filter_flip_front = 0x00000010, - k_ent_objective_filter_flip_any = - k_ent_objective_filter_flip_back| - k_ent_objective_filter_flip_front, - k_ent_objective_filter_grind_truck_any = 0x00000020, - k_ent_objective_filter_grind_board_any = 0x00000040, - k_ent_objective_filter_grind_any = - k_ent_objective_filter_grind_truck_any| - k_ent_objective_filter_grind_board_any, - k_ent_objective_filter_footplant = 0x00000080, - k_ent_objective_filter_passthrough = 0x00000100, - k_ent_objective_filter_glider = 0x00000200 -}; - -enum ent_objective_flag -{ - k_ent_objective_hidden = 0x1, - k_ent_objective_passed = 0x2, - k_ent_objective_failed = 0x4 -}; - -struct ent_objective -{ - struct mdl_transform transform; - u32 submesh_start, - submesh_count, - flags, - id_next, - filter,filter2, - deleted0; - i32 deleted1; - f32 time_limit; - u32 pstr_description_ui; -}; - -enum ent_challenge_flag -{ - k_ent_challenge_timelimit = 0x1, - //k_ent_challenge_is_story = 0x2, - k_ent_challenge_locked = 0x4, - k_ent_challenge_any_order = 0x8, - k_ent_challenge_target = 0x10 -}; - -struct ent_challenge -{ - struct mdl_transform transform; - u32 pstr_alias, - flags; - - u32 deleted0;//on_activate_id; - u32 deleted1;//on_activate_event; - u32 deleted2;//on_complete_id; - i32 deleted3;//on_complete_event; - - u32 first_objective_id; - u32 camera_id; - - u32 status; - u32 reset_spawn_id; -}; - -struct ent_cubemap -{ - f32 co[3]; - u32 resolution, live, texture_id, - framebuffer_id, renderbuffer_id, placeholder[2]; -}; - -enum prop_flag -{ - k_prop_flag_hidden = 0x1, - k_prop_flag_spinning = 0x2, - k_prop_flag_collider = 0x4, - k_prop_flag_spinning_fast = 0x8, -}; - -struct ent_prop -{ - struct mdl_transform transform; - u32 submesh_start, submesh_count, flags, pstr_alias; -}; - -enum editer_type -{ - k_editer_type_toggle = 0, - k_editer_type_slider = 1, - k_editer_type_selecter = 2 -}; - -struct editer_property -{ - u32 pstr_alias; - u8 ui_type; - - union - { - u32 _u32; - f32 _f32; - u32 pstr_options; - } - max; -}; - -struct editer_item -{ - struct mdl_transform transform; - u32 submesh_start, submesh_count; - - u32 pstr_visibility, - pstr_uv_x, - pstr_uv_y; - u16 discard_send, - discard_mask; -}; - -struct ent_region -{ - struct mdl_transform transform; - u32 submesh_start, submesh_count, pstr_title, flags; - - union - { - struct{ u32 zone_volume; } v105; - struct{ u32 id_list; } v109; - }; - - /* 105+ */ - u32 deleted01[2]; -}; - -enum ent_glider_flag -{ - k_ent_glider_flag_locked = 0x1 -}; -struct ent_glider -{ - struct mdl_transform transform; - u32 flags; - f32 cooldown; -}; - -struct ent_npc -{ - struct mdl_transform transform; - u32 pstr_id, pstr_context_id, pstr_anim, none1; -}; - -enum entity_event_result -{ - k_entity_event_result_OK, - k_entity_event_result_unhandled, - k_entity_event_result_invalid -}; - -enum ent_event_flags -{ - k_ent_event_data_void = 0x0, - k_ent_event_data_const_i32 = 0x1, - k_ent_event_data_const_f32 = 0x2, - k_ent_event_data_const_entity_id = 0x4, - k_ent_event_data_const_string = 0x8, - k_ent_event_data_data_alias = 0x10, - k_ent_event_data_v3f = 0x20 -}; - -struct ent_event -{ - u32 pstr_source_event, - pstr_recieve_event, - source_entity_id, - recieve_entity_id, - flags; - - f32 delay; - u32 unused0; - - union - { - i32 const_i32; - f32 const_f32; - u32 const_entity_id; - u32 const_pstr; - u32 pstr_data_alias; - } - data; -}; diff --git a/source/engine/profiler.c b/source/engine/profiler.c deleted file mode 100644 index a03f85e..0000000 --- a/source/engine/profiler.c +++ /dev/null @@ -1,223 +0,0 @@ -#define PROFILER_ROW_MAX 16 -#define PROFILER_STACK_MAX 8 -#define PROFILER_HISTORY_LENGTH 64 - -struct vg_profiler -{ - const c8 *name; - f32 history_ms[ PROFILER_HISTORY_LENGTH ][ PROFILER_ROW_MAX ]; - u32 buffer_current; - - u64 row_accumulated[ PROFILER_ROW_MAX ]; - const c8 *row_names[ PROFILER_ROW_MAX ]; - u32 row_length; - - u64 sample_stack[ PROFILER_STACK_MAX ]; - u32 block_stack[ PROFILER_STACK_MAX ]; - u32 stack_height; - - u64 tick_last, segment_last; - - f32 default_budget_ms; -}; - -struct -{ - vg_stack_allocator stack; - u32 count; - vg_mutex tick_lock; -} -_vg_profiler; - -VG_API void _vg_profiler_init(void) -{ - vg_stack_init( &_vg_profiler.stack, NULL, VG_MB(8), "Profilers" ); - VG_ASSERT( VG_MUTEX_INIT( _vg_profiler.tick_lock ) ); -} - -VG_API u32 _vg_profiler_create( const c8 *name, f32 default_budget_ms ) -{ - struct vg_profiler *profiler = vg_stack_allocate( &_vg_profiler.stack, sizeof(struct vg_profiler), 1, "Profiler" ); - vg_zero_mem( profiler, sizeof(struct vg_profiler) ); - profiler->name = name; - profiler->default_budget_ms = default_budget_ms; - _vg_profiler.count ++; - return vg_stack_offset( &_vg_profiler.stack, profiler ); -} - -VG_API_INTERNAL static struct vg_profiler *_vg_profiler_get( u32 id ) -{ - return vg_stack_pointer( &_vg_profiler.stack, id ); -} - -VG_API void _vg_profiler_tick( u32 profiler_id ) -{ - struct vg_profiler *profiler = _vg_profiler_get( profiler_id ); - - u64 new_tick = SDL_GetPerformanceCounter(), - duration = new_tick - profiler->tick_last; - - f64 to_ms = 1000.0 / (f64)SDL_GetPerformanceFrequency(); - - VG_MUTEX_LOCK( _vg_profiler.tick_lock ); - for( u32 i=0; ihistory_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; irow_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; irow_length; i ++ ) - colours[i] = vg_strdjb2( profiler->row_names[i] ) | 0xff000000; - - for( i32 i=0; irow_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; iname; - 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 \n" ); - - return 0; -} - -static void cmd_vg_profile_poll( int argc, const c8 *argv[] ) -{ - const c8 *term = argv[ argc-1 ]; - if( argc == 1 ) - { - for( u32 i=0; i<_vg_profiler.count; i ++ ) - { - struct vg_profiler *profiler = vg_stack_pointer( &_vg_profiler.stack, i*sizeof(struct vg_profiler) ); - console_suggest_score_text( profiler->name, term, 0 ); - } - } -} - -VG_API void _vg_profiler_register(void) -{ - vg_console_reg_cmd( "vg_profile", cmd_vg_profile, cmd_vg_profile_poll ); -} diff --git a/source/engine/profiler.c_UNUSED b/source/engine/profiler.c_UNUSED new file mode 100644 index 0000000..a03f85e --- /dev/null +++ b/source/engine/profiler.c_UNUSED @@ -0,0 +1,223 @@ +#define PROFILER_ROW_MAX 16 +#define PROFILER_STACK_MAX 8 +#define PROFILER_HISTORY_LENGTH 64 + +struct vg_profiler +{ + const c8 *name; + f32 history_ms[ PROFILER_HISTORY_LENGTH ][ PROFILER_ROW_MAX ]; + u32 buffer_current; + + u64 row_accumulated[ PROFILER_ROW_MAX ]; + const c8 *row_names[ PROFILER_ROW_MAX ]; + u32 row_length; + + u64 sample_stack[ PROFILER_STACK_MAX ]; + u32 block_stack[ PROFILER_STACK_MAX ]; + u32 stack_height; + + u64 tick_last, segment_last; + + f32 default_budget_ms; +}; + +struct +{ + vg_stack_allocator stack; + u32 count; + vg_mutex tick_lock; +} +_vg_profiler; + +VG_API void _vg_profiler_init(void) +{ + vg_stack_init( &_vg_profiler.stack, NULL, VG_MB(8), "Profilers" ); + VG_ASSERT( VG_MUTEX_INIT( _vg_profiler.tick_lock ) ); +} + +VG_API u32 _vg_profiler_create( const c8 *name, f32 default_budget_ms ) +{ + struct vg_profiler *profiler = vg_stack_allocate( &_vg_profiler.stack, sizeof(struct vg_profiler), 1, "Profiler" ); + vg_zero_mem( profiler, sizeof(struct vg_profiler) ); + profiler->name = name; + profiler->default_budget_ms = default_budget_ms; + _vg_profiler.count ++; + return vg_stack_offset( &_vg_profiler.stack, profiler ); +} + +VG_API_INTERNAL static struct vg_profiler *_vg_profiler_get( u32 id ) +{ + return vg_stack_pointer( &_vg_profiler.stack, id ); +} + +VG_API void _vg_profiler_tick( u32 profiler_id ) +{ + struct vg_profiler *profiler = _vg_profiler_get( profiler_id ); + + u64 new_tick = SDL_GetPerformanceCounter(), + duration = new_tick - profiler->tick_last; + + f64 to_ms = 1000.0 / (f64)SDL_GetPerformanceFrequency(); + + VG_MUTEX_LOCK( _vg_profiler.tick_lock ); + for( u32 i=0; ihistory_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; irow_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; irow_length; i ++ ) + colours[i] = vg_strdjb2( profiler->row_names[i] ) | 0xff000000; + + for( i32 i=0; irow_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; iname; + 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 \n" ); + + return 0; +} + +static void cmd_vg_profile_poll( int argc, const c8 *argv[] ) +{ + const c8 *term = argv[ argc-1 ]; + if( argc == 1 ) + { + for( u32 i=0; i<_vg_profiler.count; i ++ ) + { + struct vg_profiler *profiler = vg_stack_pointer( &_vg_profiler.stack, i*sizeof(struct vg_profiler) ); + console_suggest_score_text( profiler->name, term, 0 ); + } + } +} + +VG_API void _vg_profiler_register(void) +{ + vg_console_reg_cmd( "vg_profile", cmd_vg_profile, cmd_vg_profile_poll ); +} diff --git a/source/engine/steamworks.c_UNUSED b/source/engine/steamworks.c_UNUSED deleted file mode 100644 index 549baf9..0000000 --- a/source/engine/steamworks.c_UNUSED +++ /dev/null @@ -1,364 +0,0 @@ -struct vg_steam_api _steam_api; - -static void cb_steam_warning( i32 severity, const c8 *pchMessage ) -{ - if( severity == 0 ) - vg_logsteam( "[message]" KBLK " '%s'\n", pchMessage ); - else - vg_logsteam( "[message]" KYEL " '%s'\n", pchMessage ); -} - -#if defined( VG_ENGINE ) -static void cb_auth_ticket_recieved( void *result, void *context ) -{ - EncryptedAppTicketResponse_t *response = result; - - if( response->m_eResult == k_EResultOK ) - vg_logsteam( " New app ticket ready\n" ); - else - vg_logsteam( KYEL " Could not request new encrypted app ticket (%u)\n", response->m_eResult ); - - if( SteamAPI_ISteamUser_GetEncryptedAppTicket( _steam_api.pSteamUser, _steam_api.app_symmetric_key, - VG_ARRAY_LEN(_steam_api.app_symmetric_key), &_steam_api.app_key_length )) - { - vg_logsteam( KGRN " Loaded app ticket\n" ); - } - else - { - vg_logsteam( KRED " No ticket availible\n" ); - _steam_api.app_key_length = 0; - } -} -#endif - -#if defined( VG_SERVER ) -static u8 vg_char_base16( c8 c ) -{ - if( c >= '0' && c <= '9' ) - return c-'0'; - if( c >= 'a' && c <= 'f' ) - return (c-'a') + 10; - return 0; -} -#endif - -#if defined( VG_SERVER ) -VG_API b8 _vg_steam_init( u32 unIP, u16 usGamePort, u16 usQueryPort, EServerMode eServerMode, - const c8 *pchVersionString, const c8 *appid_str ) -#else -VG_API b8 _vg_steam_init(void) -#endif -{ - if( _steam_api.disabled ) - return 0; - - SteamErrMsg err; - - /* Steamworks init step - * ---------------------------------------------------------------------------- */ -#if defined( VG_ENGINE ) - VG_ASSERT( _vg_thread_has_flags( VG_THREAD_OWNS_STEAM ) ); - - const char *pszInternalCheckInterfaceVersions = - STEAMUTILS_INTERFACE_VERSION "\0" - STEAMNETWORKINGUTILS_INTERFACE_VERSION "\0" - STEAMAPPS_INTERFACE_VERSION "\0" - STEAMCONTROLLER_INTERFACE_VERSION "\0" - STEAMFRIENDS_INTERFACE_VERSION "\0" - STEAMGAMESEARCH_INTERFACE_VERSION "\0" - STEAMHTMLSURFACE_INTERFACE_VERSION "\0" - STEAMHTTP_INTERFACE_VERSION "\0" - STEAMINPUT_INTERFACE_VERSION "\0" - STEAMINVENTORY_INTERFACE_VERSION "\0" - STEAMMATCHMAKINGSERVERS_INTERFACE_VERSION "\0" - STEAMMATCHMAKING_INTERFACE_VERSION "\0" - STEAMMUSICREMOTE_INTERFACE_VERSION "\0" - STEAMMUSIC_INTERFACE_VERSION "\0" - STEAMNETWORKINGMESSAGES_INTERFACE_VERSION "\0" - STEAMNETWORKINGSOCKETS_INTERFACE_VERSION "\0" - STEAMNETWORKING_INTERFACE_VERSION "\0" - STEAMPARENTALSETTINGS_INTERFACE_VERSION "\0" - STEAMPARTIES_INTERFACE_VERSION "\0" - STEAMREMOTEPLAY_INTERFACE_VERSION "\0" - STEAMREMOTESTORAGE_INTERFACE_VERSION "\0" - STEAMSCREENSHOTS_INTERFACE_VERSION "\0" - STEAMUGC_INTERFACE_VERSION "\0" - STEAMUSERSTATS_INTERFACE_VERSION "\0" - STEAMUSER_INTERFACE_VERSION "\0" - STEAMVIDEO_INTERFACE_VERSION "\0" - - "\0"; - - if( SteamInternal_SteamAPI_Init( pszInternalCheckInterfaceVersions, &err ) != k_ESteamAPIInitResult_OK ) - { - _steam_api.disabled = 1; - vg_logsteam( KRED "SteamInternal_SteamAPI_Init() failed: '%s'\nAll steam interactions disabled for this session\n", err ); - return 0; - } -#endif - -#if defined( VG_SERVER ) - FILE *txt = fopen( "steam_appid.txt", "w" ); - fputs( appid_str, txt ); - fclose( txt ); - - // FIXME: Add no-auth launch option (hoist from skaterift, as we have it there, to vg steam module?) - vg_stack_allocator stack; - vg_stack_init( &stack, NULL, VG_KB(256), NULL ); - u32 size; - c8 *src = vg_file_read( &stack, "application_key", &size, 0 ); - if( src ) - { - if( size < k_nSteamEncryptedAppTicketSymmetricKeyLen ) - { - vg_error( "Application key was invalid size\n" ); - return 0; - } - - for( int i=0; iuserdata = NULL; - call->cb = cb_auth_ticket_recieved; - call->id = SteamAPI_ISteamUser_RequestEncryptedAppTicket( _steam_api.pSteamUser, NULL, 0 ); - } -# endif -#endif - - return 1; -} - -static const c8 *string_ESteamNetworkingConnectionState( ESteamNetworkingConnectionState s ) -{ - if( s == k_ESteamNetworkingConnectionState_None ) return "None"; - if( s == k_ESteamNetworkingConnectionState_Connecting) return "Connecting"; - if( s == k_ESteamNetworkingConnectionState_FindingRoute) return "Finding route"; - if( s == k_ESteamNetworkingConnectionState_Connected) return "Connected"; - if( s == k_ESteamNetworkingConnectionState_ClosedByPeer) return "Closed By Peer"; - if( s == k_ESteamNetworkingConnectionState_ProblemDetectedLocally) return "Problem detected locally"; - if( s == k_ESteamNetworkingConnectionState_FinWait) return "Finwait"; - if( s == k_ESteamNetworkingConnectionState_Linger) return "Linger"; - if( s == k_ESteamNetworkingConnectionState_Dead) return "Dead"; - return "enum-out-of-range"; -} - -static const c8 *string_ESteamAPICallFailure( ESteamAPICallFailure e ) -{ - if( e == k_ESteamAPICallFailureNone ) return "None"; - if( e == k_ESteamAPICallFailureSteamGone ) return "Steam Gone"; - if( e == k_ESteamAPICallFailureNetworkFailure ) return "Network Failure"; - if( e == k_ESteamAPICallFailureInvalidHandle ) return KBLK "Invalid Handle"; - if( e == k_ESteamAPICallFailureMismatchedCallback ) return "Mismatched Callback"; - return "enum-out-of-range"; -} - -void vg_steam_frame(void) -{ - if( _steam_api.disabled ) - return; - - SteamAPI_ManualDispatch_RunFrame( _steam_api.hPipe ); - - CallbackMsg_t callback; - while( SteamAPI_ManualDispatch_GetNextCallback( _steam_api.hPipe, &callback ) ) - { - /* Check for dispatching API call results */ - i32 type = callback.m_iCallback; - if( type == k_iSteamUtils_SteamAPICallCompleted ) - { - SteamAPICallCompleted_t *inf = callback.m_pubParam; - - b8 bFailed; - void *call_data = alloca( inf->m_cubParam ); - - if( SteamAPI_ManualDispatch_GetAPICallResult( _steam_api.hPipe, inf->m_hAsyncCall, - call_data, inf->m_cubParam, - inf->m_iCallback, &bFailed ) ) - { - vg_logsteam( "api_call_completed %lu\n", inf->m_hAsyncCall ); - - int j=0; - for( int i=0; i<_steam_api.api_call_count; i++ ) - { - if( _steam_api.api_calls[i].id != inf->m_hAsyncCall ) - _steam_api.api_calls[j ++] = _steam_api.api_calls[i]; - else - _steam_api.api_calls[i].cb( call_data, _steam_api.api_calls[i].userdata ); - } - - if( _steam_api.api_call_count == j ) - vg_error( "No tracker was register for API call\n" ); - - _steam_api.api_call_count = j; - } - else - { - enum ESteamAPICallFailure e = - SteamAPI_ISteamUtils_GetAPICallFailureReason( _steam_api.pSteamUtils, inf->m_hAsyncCall ); - const c8 *fail_str = string_ESteamAPICallFailure( e ); - vg_logsteam( KRED "Error getting call result on %lu (code %d)\n", inf->m_hAsyncCall, fail_str ); - } - } - else - { - /* - * Look at callback.m_iCallback to see what kind of callback it is, - * and dispatch to appropriate handler(s) - * void *data = callback.m_pubParam; - */ - if( type == k_iSteamUser_SteamServersConnected ) - vg_success( "Steam servers connected" ); - else if( type == k_iSteamUser_SteamConnectFailure ) - { - SteamServerConnectFailure_t *inf = callback.m_pubParam; - vg_logsteam( KRED "Steam server connect failure, code %d, retrying: %d\n", inf->m_eResult, inf->m_bStillRetrying ); - } - else if( type == k_iSteamUser_SteamServersDisconnected ) - { - SteamServersDisconnected_t *inf = callback.m_pubParam; - vg_logsteam( "Steam servers disconnect, code %d\n", inf->m_eResult ); - } - else if( type == k_iSteamNetworkingSockets_SteamNetConnectionStatusChangedCallback ) - { - SteamNetConnectionStatusChangedCallback_t *inf = callback.m_pubParam; - const c8 *status_string = string_ESteamNetworkingConnectionState( inf->m_info.m_eState ); - vg_logsteam( "Connection status changed: %x -> %s\n Debug: '%s'\n EndDebug: '%s'\n", - inf->m_hConn, status_string, inf->m_info.m_szConnectionDescription, - inf->m_info.m_szEndDebug ); - - if( _steam_api.cb_connection_changed ) - _steam_api.cb_connection_changed( inf ); - } - else if( type == k_iSteamNetworkingSockets_SteamNetAuthenticationStatus ) - { - SteamNetAuthenticationStatus_t *inf = callback.m_pubParam; - vg_logsteam( "Steam Authentication Status: %d\n Debug: '%s'\n", inf->m_eAvail, inf->m_debugMsg ); - } - } - - SteamAPI_ManualDispatch_FreeLastCallback( _steam_api.hPipe ); - } -} - -VG_API void _vg_steam_shutdown(void) -{ -#if defined( VG_SERVER ) - if( _steam_api.is_connected ) - { - SteamAPI_ISteamGameServer_LogOff( _steam_api.pSteamGameServer ); - _steam_api.is_connected = 0; - } - SteamGameServer_Shutdown(); -#else - SteamAPI_Shutdown(); -#endif -} - -vg_steam_api_call *vg_alloc_async_steam_api_call(void) -{ - if( _steam_api.api_call_count == VG_ARRAY_LEN(_steam_api.api_calls) ) - return NULL; - return &_steam_api.api_calls[ _steam_api.api_call_count ++ ]; -} - -#if defined( VG_ENGINE ) -void vg_steam_set_achievement( const c8 *name, b8 yes ) -{ - if( _steam_api.disabled || _steam_api.demo_mode ) - return; - - if( yes ) - { - if( SteamAPI_ISteamUserStats_SetAchievement( _steam_api.pSteamUserStats, name ) ) - { - vg_logsteam( KGRN "Set achievement '%s'\n", name ); - SteamAPI_ISteamUserStats_StoreStats( _steam_api.pSteamUserStats ); - } - } - else - { - if( SteamAPI_ISteamUserStats_ClearAchievement( _steam_api.pSteamUserStats, name ) ) - { - vg_logsteam( KBLK "Clear achievement '%s'\n", name ); - SteamAPI_ISteamUserStats_StoreStats( _steam_api.pSteamUserStats ); - } - } -} -#endif diff --git a/source/engine/ui.c_UNUSED b/source/engine/ui.c_UNUSED deleted file mode 100644 index 14ca3cc..0000000 --- a/source/engine/ui.c_UNUSED +++ /dev/null @@ -1,71 +0,0 @@ -#include "foundation.h" -#include "vg_engine.h" -#include "vg_graphics.h" -#include "vg_render.h" -#include "vg_opengl.h" -#include "vg_shader.h" - -struct graphics_target _ui_surface = -{ - .mode = k_graphics_mode_software, - .size = { 1920, 1080 }, -}; - -static GLuint _ui_surface_texture; - -#include -void _engine_ui_init(void) -{ - _ui_surface.buffer = _heap_allocate( 1920*1080*4 ); - - glGenTextures( 1, &_ui_surface_texture ); - glActiveTexture( GL_TEXTURE0 ); - glBindTexture( GL_TEXTURE_2D, _ui_surface_texture ); - glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); -} - -void _engine_ui_pre_render(void) -{ - ASSERT_CRITICAL( _engine.w <= 1920 ); - ASSERT_CRITICAL( _engine.h <= 1080 ); - - f64 x = 0, y = 0; -#if 0 - glfwGetCursorPos( _engine.window_handle, &x, &y ); -#endif - _ui_set_mouse( x, y ); - _ui_update(); - - _graphics_set_target( &_ui_surface ); - _graphics_viewport( 0, 0, _engine.w, _engine.h ); - //_graphics_fill_rect( (i16[]){ 0, 0, _engine.w, _engine.h }, (union colour){{0,0,0,255}} ); -} - -void _engine_ui_input_character( u32 codepoint ) -{ - if( (codepoint >= (u32)' ') && (codepoint <= (u32)'~') ) - _ui_input_text( (const c8[]){ codepoint, 0 } ); -} - -void _engine_ui_post_render(void) -{ - glEnable( GL_BLEND ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - glBlendEquation( GL_FUNC_ADD ); - - glActiveTexture( GL_TEXTURE0 ); - glBindTexture( GL_TEXTURE_2D, _ui_surface_texture ); - glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 1920, 1080, GL_BGRA, GL_UNSIGNED_BYTE, _ui_surface.buffer ); - - _shader_bind( k_shader_blit ); - _shader_blit_uTexMain( 0 ); - _shader_blit_uFlip( 1 ); - _shader_blit_uInverseRatio( (f32[2]){ (f64)_engine.w/1920.0, (f64)_engine.h/1080.0 } ); - - _render_fullscreen_quad(); - glDisable( GL_BLEND ); -} diff --git a/source/engine/vg_af.c b/source/engine/vg_af.c new file mode 100644 index 0000000..b1b73fb --- /dev/null +++ b/source/engine/vg_af.c @@ -0,0 +1,356 @@ +#include "foundation.h" +#include "vg_af.h" + +u32 af_str_hash( const void *packed_strings, u32 pstr ) +{ + if( pstr & 0x3 ) + { + $log( $fatal, {"ALIGNMENT ERROR, PSTR INDEX: "}, $unsigned(pstr) ); + } + return *((u32 *)(packed_strings + pstr)); +} + +const c8 *af_str( const void *packed_strings, u32 pstr ) +{ + return packed_strings + pstr + 4; +} + +b8 af_str_eq( const void *packed_strings, u32 pstr, const c8 *str, u32 str_hash ) +{ + if( af_str_hash( packed_strings, pstr ) == str_hash ) + if( compare_buffers( str, 0, af_str( packed_strings, pstr ), 0 )) + return 1; + return 0; +} + +void af_load_array_file_buffer( struct array_file_context *ctx, struct array_file_meta *arr, void *buffer, u32 stride ) +{ +#if defined( __EMSCRIPTEN__ ) + if( arr->item_count ) + { + stream_seek( ctx->stream, arr->file_offset ); + if( stride != arr->item_size ) + { + zero_buffer( buffer, stride*arr->item_count ); + u32 temp_frame = _start_temporary_frame(); + { + u8 *temp = _temporary_allocate( arr->item_count * arr->item_size, 8 ); + stream_read( ctx->stream, temp, arr->item_count * arr->item_size ); + + u32 read_size = u32_min( stride, arr->item_size ); + for( u32 i=0; iitem_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; iitem_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; iindex); 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; idata)[i] = 0xab; + + if( index->last ) + index->last->next = entry; + index->last = entry; + + if( !index->first ) + index->first = entry; + + return entry; +} + +static void af_compiler_init_index( struct af_compiler_index *index, const c8 *alias, u32 element_size ) +{ + ASSERT_CRITICAL( element_size ); + if( !buffer_copy( alias, 0, index->name, sizeof(index->name) ) ) + { + $log( $fatal, {"Index name overflowed: "}, {alias} ); + _fatal_exit(); + } + index->element_size = element_size; + index->element_count = 0; + index->first = NULL; + index->last = NULL; +} + +struct af_compiler_index *af_compiler_create_index( struct af_compiler *compiler, const c8 *alias, u32 element_size ) +{ + struct af_compiler_item *item = af_compiler_allocate_items( compiler, &compiler->index, 1 ); + struct af_compiler_index *index = item->data; + af_compiler_init_index( index, alias, element_size ); + return index; +} + +u32 af_compile_string( struct af_compiler *compiler, const c8 *string ) +{ + u32 string_hash = buffer_djb2( string, 0 ); + + // TODO: Hash table against existing strings (low priority) + u32 offset = offset = compiler->strings_index->element_count; + u32 bytes = PAD_TO_4( buffer_first_index( string, 0,0 )+1 + 4 ); + struct af_compiler_item *item = af_compiler_allocate_items( compiler, compiler->strings_index, bytes ); + *((u32 *)item->data) = string_hash; + buffer_copy( string, 0, item->data+4, 0 ); + return offset; +} + +void af_compiler_init( struct af_compiler *compiler, struct stack_allocator *stack ) +{ + compiler->stack = stack; + af_compiler_init_index( &compiler->index, "index", sizeof(struct af_compiler_index) ); + compiler->strings_index = af_compiler_create_index( compiler, "strings", 1 ); + af_compile_string( compiler, "nul" ); +} + +static void af_write_bin( struct af_compiler *compiler, void *data, u32 data_len, u32 padding ) +{ + if( data ) + { + stream_write( &compiler->stream, data, data_len ); + compiler->file_offset += data_len; + } + + if( padding ) + { + while( compiler->file_offset % padding ) + { + const u8 pad_byte = 0xac; + stream_write( &compiler->stream, &pad_byte, 1 ); + compiler->file_offset ++; + } + } +} + +struct af_compiler_index *af_get_or_make_index( struct af_compiler *compiler, const c8 *alias, u32 element_size ) +{ + struct af_compiler_iter iter; + af_init_iterator( &iter, &compiler->index ); + while( af_next( &iter ) ) + { + struct af_compiler_index *index = iter.data; + if( compare_buffers( index->name, 0, alias, 0 ) ) + return index; + } + + return af_compiler_create_index( compiler, alias, element_size ); +} + +b8 af_write( struct af_compiler *compiler, const c8 *path, u32 version ) +{ + u32 indices_to_write = 0; + + struct af_compiler_iter iter; + af_init_iterator( &iter, &compiler->index ); + while( af_next( &iter ) ) + { + struct af_compiler_index *index = iter.data; + if( index->element_count ) + indices_to_write ++; + } + + u32 header_size = PAD_TO_8( sizeof( struct array_file_header ) ); + u32 index_size = PAD_TO_8( sizeof( struct array_file_meta ) * indices_to_write ); + + if( !stream_open_file( &compiler->stream, path, k_stream_write ) ) + return 0; + + compiler->file_offset = 0; + + struct array_file_header header; + header.version = version; + header.index.file_offset = header_size; + header.index.item_count = indices_to_write; + header.index.item_size = sizeof(struct array_file_meta); + buffer_copy( "index", 0, header.index.name, sizeof(header.index.name) ); + af_write_bin( compiler, &header, sizeof(struct array_file_header), 8 ); + + /* write index */ + u32 file_offset = header_size + index_size; + af_init_iterator( &iter, &compiler->index ); + while( af_next( &iter ) ) + { + struct af_compiler_index *index = iter.data; + if( index->element_count ) + { + struct array_file_meta meta; + buffer_copy( index->name, sizeof(index->name), meta.name, sizeof(meta.name) ); + meta.item_count = index->element_count; + meta.item_size = index->element_size; + meta.file_offset = file_offset; + file_offset += PAD_TO_8( meta.item_size*meta.item_count ); + af_write_bin( compiler, &meta, sizeof(struct array_file_meta), 0 ); + } + } + af_write_bin( compiler, NULL, 0, 8 ); + + af_init_iterator( &iter, &compiler->index ); + while( af_next( &iter ) ) + { + struct af_compiler_index *index = iter.data; + + if( index->element_count ) + { + struct af_compiler_iter item_iter; + af_init_iterator( &item_iter, index ); + + while( af_next( &item_iter ) ) + af_write_bin( compiler, item_iter.data, index->element_size, 0 ); + af_write_bin( compiler, NULL, 0, 8 ); + } + } + + stream_close( &compiler->stream ); + return 1; +} diff --git a/source/engine/vg_af.h b/source/engine/vg_af.h new file mode 100644 index 0000000..5abd164 --- /dev/null +++ b/source/engine/vg_af.h @@ -0,0 +1,89 @@ +#pragma once + +struct array_file_ptr +{ + void *data; + u32 count, stride; +}; + +struct array_file_meta +{ + u32 file_offset, + item_count, + item_size; + + c8 name[16]; +}; + +struct array_file_header +{ + u32 version; + struct array_file_meta index; +}; + +struct array_file_context +{ + struct stream *stream; + struct array_file_header header; + struct array_file_ptr index; +}; + +/* array loading */ +struct array_file_meta *af_find_array( struct array_file_context *ctx, const c8 *name ); + +void af_load_array_file( struct array_file_context *ctx, struct array_file_ptr *out_ptr, + struct array_file_meta *arr, struct stack_allocator *stack, u32 stride ); + +b8 af_load_array( struct array_file_context *ctx, struct array_file_ptr *ptr, const c8 *name, + struct stack_allocator *stack, u32 stride ); +void af_load_array_file_buffer( struct array_file_context *ctx, struct array_file_meta *arr, void *buffer, u32 stride ); + +/* array access */ +void *af_arritm( struct array_file_ptr *arr, u32 index ); +u32 af_arrcount( struct array_file_ptr *arr ); + +/* packed string buffer access (with djb2 hash prefix) */ +const c8 *af_str( const void *packed_strings, u32 pstr ); +u32 af_str_hash( const void *packed_strings, u32 pstr ); +b8 af_str_eq( const void *packed_strings, u32 pstr, const char *str, u32 str_hash ); + +#define AF_STR_EQ( PACKED, PSTR, CONSTR ) \ + af_str_eq( PACKED, PSTR, CONSTR, buffer_djb2( CONSTR, 0 ) ) + +/* COmpiler + * ------------------------------------ */ + +struct af_compiler_item +{ + void *data; + u32 count; + struct af_compiler_item *next; +}; + +struct af_compiler_index +{ + c8 name[16]; + u32 element_size, element_count; + struct af_compiler_item *first, *last; +}; + +struct af_compiler +{ + struct stack_allocator *stack; + struct af_compiler_index index, + *strings_index; + struct af_compiler_item *most_recent_item; + + struct stream stream; + u32 file_offset; +}; + +void af_compiler_init( struct af_compiler *compiler, struct stack_allocator *stack ); +struct af_compiler_item *af_compiler_allocate_items( struct af_compiler *compiler, struct af_compiler_index *index, u32 count ); +struct af_compiler_index *af_compiler_create_index( struct af_compiler *compiler, const c8 *alias, u32 element_size ); +b8 af_write( struct af_compiler *compiler, const c8 *path, u32 version ); +struct af_compiler_index *af_get_or_make_index( struct af_compiler *compiler, const c8 *alias, u32 element_size ); +u32 af_compile_string( struct af_compiler *compiler, const c8 *string ); + +b8 af_open_stream( struct array_file_context *afc, struct stream *stream, u32 min_version, u32 max_version, + struct stack_allocator *stack ); diff --git a/source/engine/vg_audio.h b/source/engine/vg_audio.h index 0005b3c..3c6601a 100644 --- a/source/engine/vg_audio.h +++ b/source/engine/vg_audio.h @@ -63,6 +63,7 @@ struct audio_clip }; }; +void _audio_init(void); void vg_audio_device_init(void); void vg_audio_begin(void); diff --git a/source/engine/vg_audio_dsp.h b/source/engine/vg_audio_dsp.h index c9feb99..5e20ed0 100644 --- a/source/engine/vg_audio_dsp.h +++ b/source/engine/vg_audio_dsp.h @@ -1,5 +1,6 @@ //#define VG_ECHO_LPF_BUTTERWORTH #pragma once +void _dsp_init(void); void _dsp_update_tunings( f32 echo_distances[14] ); void _dsp_process( f32 *stereo_in, f32 *stereo_out ); diff --git a/source/engine/vg_console.c b/source/engine/vg_console.c new file mode 100644 index 0000000..735580f --- /dev/null +++ b/source/engine/vg_console.c @@ -0,0 +1,86 @@ +#include "foundation.h" +#include "console_system.h" +#include "vg_graphics.h" +#include "vg_engine.h" +#include "vg_input.h" + +struct +{ + struct queue_allocator input_history, log_history; + b8 open; +} +_console; + +struct log_history_item +{ + u32 type; + c8 buffer[]; +}; + +static c8 _input_buffer[ 1024 ]; + +static void _log_listener( const c8 *line, u32 length, u32 type ) +{ +again:; + struct log_history_item *item = queue_alloc( &_console.log_history, sizeof( struct log_history_item ) + length+1 ); + if( !item ) + { + queue_pop( &_console.log_history ); + goto again; + } + + item->type = type; + buffer_copy( line, 0, item->buffer, length+1 ); +} + +void _engine_console_init(void) +{ + _log_add_listener( _log_listener, ($info | $ok | $warning | $error | $fatal | $shell), 0 ); + queue_init( &_console.input_history, _heap_allocate( BYTES_KB(2) ), BYTES_KB(2) ); + queue_init( &_console.log_history, _heap_allocate( BYTES_KB(4) ), BYTES_KB(4) ); +} + +void _engine_console_update(void) +{ + for( u8 j=0; j<_input_button_down( k_input_action_console, 0 ); j ++ ) + { + _console.open ^= 0x1; + u32 console_mask = (1 << k_input_layer_console) | (1 << k_input_layer_ui); + _input_layer_whitelist( _console.open? console_mask: 0 ); + } +} + +void _engine_console_ui(void) +{ + if( !_console.open ) + return; + + i16 kerning[3]; + _font_get_kerning( kerning ); + i16 history_box[4] = { 0, 0, _engine.w, kerning[1] * 32 }; + + if( _console.log_history.allocation_count ) + { + u32 item_offset = _console.log_history.head_offset; + for( u32 i=0; i<32; i ++ ) + { + struct log_history_item *item = queue_data( &_console.log_history, item_offset ); + i16 line_rect[4] = (i16[]){ 0, kerning[1] * (31-i), _engine.w, kerning[1] }; + _ui_text( line_rect, item->buffer, k_ui_align_x_left, (union colour){{128,128,128,255}} ); + + if( !queue_previous( &_console.log_history, item_offset, &item_offset ) ) + break; + } + } + + _graphics_line_rect( history_box, (union colour){{0,255,255,255}} ); + + enum textbox_action action = _ui_textbox( (i16[]){ 0, kerning[1]*32, _engine.w, kerning[1] }, + _input_buffer, sizeof(_input_buffer), 1, UI_AUTOFOCUS ); + + if( action == k_textbox_enter ) + { + _console_execute( _input_buffer, 0, 0 ); + _input_buffer[0] = '\0'; + } +} diff --git a/source/engine/vg_console.h b/source/engine/vg_console.h new file mode 100644 index 0000000..09750a3 --- /dev/null +++ b/source/engine/vg_console.h @@ -0,0 +1,5 @@ +#pragma once + +void _engine_console_init(void); +void _engine_console_update(void); +void _engine_console_ui(void); diff --git a/source/engine/vg_engine.c b/source/engine/vg_engine.c index d508054..8a16dc4 100644 --- a/source/engine/vg_engine.c +++ b/source/engine/vg_engine.c @@ -9,10 +9,17 @@ #include "vg_ui.h" #include "vg_audio.h" #include "vg_graphics.h" +#include "vg_audio_dsp.h" +#include "vg_audio.h" +#include "vg_shader.h" +#include "vg_render.h" +#include "vg_tex.h" +#include "vg_lines.h" +#include "vg_framebuffer.h" +#include "vg_console.h" // TODO: temp #include "console_system.h" -#include "generated/hooks.h" #if defined( __EMSCRIPTEN__ ) #include @@ -89,7 +96,12 @@ SDL_Thread *_async_thread_handle = NULL; i32 main( i32 argc, const c8 *argv[] ) { - VG_PRE_MAIN; + _exit_init(); + _log_init(); + _options_init( argc, argv ); + _engine_options(); + if( _vg_engine_hooks.options ) _vg_engine_hooks.options(); + _options_check_end(); $log( $info, {"SDL_INIT_VIDEO"} ); if( SDL_Init( SDL_INIT_VIDEO ) == 0 ) @@ -236,7 +248,19 @@ i32 main( i32 argc, const c8 *argv[] ) mt_random_seed( &_engine.random, 887765 ); - EVENT_CALL( START ); + _async_init(); + _dsp_init(); + _audio_init(); + _engine_ui_init(); + _input_init(); + _console_init(); + _engine_console_init(); + _vg_render_init(); + _shader_init(); + _vg_tex_init(); + _vg_lines_init(); + if( _vg_engine_hooks.start ) _vg_engine_hooks.start(); + _async_thread_handle = SDL_CreateThread( _async_thread, "ASync thread", NULL ); #if defined( __EMSCRIPTEN__ ) @@ -288,7 +312,9 @@ void _vg_engine_frame(void) while( _task_queue_process( 0 ) ) {} /* normal update */ - EVENT_CALL( ENGINE_NEW_FRAME ); + _input_update(); + _engine_console_update(); + if( _vg_engine_hooks.frame_start ) _vg_engine_hooks.frame_start(); /* fixed update */ _fixed_accumulator += _engine.time_delta; @@ -302,7 +328,7 @@ void _vg_engine_frame(void) _fixed_accumulator -= 1.0/60.0; _fixed_time += _engine.time_delta; _engine.time = _fixed_time; - EVENT_CALL( ENGINE_FIXED_UPDATE ); + if( _vg_engine_hooks.fixed_frame ) _vg_engine_hooks.fixed_frame(); } else break; } @@ -314,13 +340,13 @@ void _vg_engine_frame(void) //glfwGetFramebufferSize( _engine.window_handle, &_engine.w, &_engine.h ); SDL_GetWindowSizeInPixels( _engine.window_handle, &_engine.w, &_engine.h ); if( (pw != _engine.w) || (ph != _engine.h) ) - { - EVENT_CALL( ENGINE_WINDOW_RESIZE ); - } + _framebuffer_resize(); - EVENT_CALL( ENGINE_RENDER ); + if( _vg_engine_hooks.frame_render ) _vg_engine_hooks.frame_render(); _engine_ui_pre_render(); - EVENT_CALL( ENGINE_UI ); + + if( _vg_engine_hooks.frame_ui ) _vg_engine_hooks.frame_ui(); + _engine_console_ui(); _audio_gui(); _engine_ui_post_render(); @@ -333,7 +359,7 @@ void _vg_engine_frame(void) if( e.type == SDL_EVENT_QUIT ) { SDL_DestroyWindow( _engine.window_handle ); - EVENT_CALL( END ); + if( _vg_engine_hooks.end ) _vg_engine_hooks.end(); _task_queue_stop_normal( k_thread_async ); SDL_WaitThread( _async_thread_handle, NULL ); SDL_Quit(); diff --git a/source/engine/vg_engine.h b/source/engine/vg_engine.h index 8f6e563..ea1c8f4 100644 --- a/source/engine/vg_engine.h +++ b/source/engine/vg_engine.h @@ -23,3 +23,15 @@ struct _engine struct mt_random random; } extern _engine; + +struct _vg_engine_hooks +{ + void (*options)( void ); + void (*start)( void ); + void (*frame_start)( void ); + void (*fixed_frame)( void ); + void (*frame_render)( void ); + void (*frame_ui)( void ); + void (*end)( void ); +} +extern _vg_engine_hooks; diff --git a/source/engine/vg_engine_core.kv b/source/engine/vg_engine_core.kv deleted file mode 100644 index d639c16..0000000 --- a/source/engine/vg_engine_core.kv +++ /dev/null @@ -1,379 +0,0 @@ -{ - add vg_engine.c - hook - { - event OPTIONS - function _engine_options - } - event - { - name ENGINE_NEW_FRAME - prototype "void" - } - event - { - name ENGINE_FIXED_UPDATE - prototype "void" - } - event - { - name ENGINE_RENDER - prototype "void" - } - event - { - name ENGINE_UI - prototype "void" - } - event - { - name ENGINE_WINDOW_RESIZE - prototype "void" - } -} - -{ - add ui.c - hook - { - event START - function _engine_ui_init - } -} - -{ - add input.c - hook - { - event START - function _input_init - } - hook - { - event ENGINE_NEW_FRAME - function _input_update - } - ccmd - { - name bind - function _input_bind_ccmd - description "Bind device input to a button, action or axis" - - parameter - { - description "Device input alias" - } - - parameter - { - description "button,action,axis name" - } - } - ccmd - { - name unbind - description "Unbind all bindings that match" - function _input_unbind_ccmd - - parameter - { - description "binding" - } - } -} - -{ - add console.c - hook - { - event START - function _engine_console_init - } - hook - { - event ENGINE_NEW_FRAME - function _engine_console_update - } - hook - { - event ENGINE_UI - function _engine_console_ui - affinity 9999 - } -} - -{ - add vg_framebuffer.c - hook - { - event ENGINE_WINDOW_RESIZE - function _framebuffer_resize - } -} - -{ -add source/engine/shader.c -input_layer -{ - name console -} -input_layer -{ - name ui -} -input -{ - name console - type action - layer_mask console -} - -append async.kv -thread -{ - name main - queue_size_m 40 - flag MAIN - flag OPENGL -} -thread -{ - name async - queue_size_m 40 - flag ASYNC -} -thread -{ - name audio -} -thread -{ - name exitor - queue_size_m 1 -} - -append graphics.kv - -ccmd -{ - name exec - function _console_exec_ccmd - description "Execute a configuration file" - - parameter - { - description "The path to the config" - } -} -config "bind ALT+GRAVE_ACCENT console" - -config "bind BACKSPACE ui_backspace" -config "bind DELETE ui_delete" -config "bind ENTER ui_enter" -config "bind TAB ui_indent" -config "bind HOME ui_home" -config "bind END ui_end" -config "bind LEFT ui_left" -config "bind RIGHT ui_right" -config "bind UP ui_up" -config "bind DOWN ui_down" -config "bind MOUSE_LEFT ui_click" - -config "bind LEFT_SHIFT ui_select" -config "bind RIGHT_SHIFT ui_select" - -input -{ - name ui_delete - type action - layer_mask ui -} -input -{ - name ui_backspace - type action - layer_mask ui -} -input -{ - name ui_enter - type action - layer_mask ui -} -input -{ - name ui_indent - type action - layer_mask ui -} - -input -{ - name ui_select - type button - layer_mask ui -} -input -{ - name ui_home - type button - layer_mask ui -} -input -{ - name ui_end - type button - layer_mask ui -} -input -{ - name ui_left - type button - layer_mask ui -} -input -{ - name ui_right - type button - layer_mask ui -} -input -{ - name ui_up - type button - layer_mask ui -} -input -{ - name ui_down - type button - layer_mask ui -} -input -{ - name ui_click - type action - layer_mask ui -} - -shader -{ - name blit - - subshader - { - type vertex - add shaders/blit.vs - - uniform - { - type vec2 - alias uInverseRatio - } - uniform - { - type int - alias uFlip - } - } - - subshader - { - type fragment - add shaders/blit_tex.fs - - uniform - { - type sampler2D - alias uTexMain - } - } -} - -cvar -{ - name test - type u32 - default 5 - cheat 1 - description "This is just a test variable!" -} - -hook -{ - event START - function "_shaders_compile" -} -} - - - - - - -add vg_camera.c - - -add vg_tex.c -hook -{ - event START - function _vg_tex_init -} - - -add vg_lines.c -hook -{ - event START - function _vg_lines_init -} -cvar -{ - name debug_lines - type u32 - default 0 - cheat 1 - description "Show line debuggers" -} -shader -{ - name debug_lines - subshader - { - type vertex - add shaders/debug_lines.vs - uniform - { - type mat4 - alias uPv - } - } - subshader - { - type fragment - add shaders/debug_lines.fs - } -} - - -add vg_audio.c -add vg_audio_dsp.c -add vorbis.c -hook -{ - affinity -3008 - event START - function _audio_init -} -hook -{ - affinity -6004 - event START - function _dsp_init -} - - -add model.c -add metascene.c -add array_file.c diff --git a/source/engine/vg_entity.c b/source/engine/vg_entity.c new file mode 100644 index 0000000..99aa597 --- /dev/null +++ b/source/engine/vg_entity.c @@ -0,0 +1,15 @@ +#include "vg_entity.h" +#include "generated/entities.c" + +i32 _vg_entity_link_function( u16 entity_type, const c8 *alias ) +{ + struct entity_info *info = _vg_entity_info( entity_type ); + for( u16 i=0; ifunction_count; i ++ ) + { + struct entity_function_info *func_info = &_entity_function_infos[ info->function_start + i ]; + if( compare_buffers( alias, 0, func_info->name, 0 ) ) + return (i32)i; + } + $log( $error, {"Failed to link function from alias '"}, {alias}, {"'"} ); + return -1; +} diff --git a/source/engine/vg_entity.h b/source/engine/vg_entity.h new file mode 100644 index 0000000..40e835b --- /dev/null +++ b/source/engine/vg_entity.h @@ -0,0 +1,757 @@ +#pragma once +#include "vg_model.h" + +#include "generated/entities.h" + +#if 0 +enum entity_alias +{ + k_ent_none = 0, + k_ent_gate = 1, + k_ent_spawn = 2, + k_ent_route_node = 3, + k_ent_route = 4, + k_ent_water = 5, + k_ent_volume = 6, + k_ent_audio = 7, + k_ent_marker = 8, + k_ent_font = 9, + k_ent_font_variant= 10, + k_ent_traffic = 11, + k_ent_skateshop = 12, + k_ent_camera = 13, + k_ent_swspreview = 14, + k_ent_deleted1 = 15, //k_ent_menuitem = 15, + k_ent_worldinfo = 16, + k_ent_ccmd = 17, + k_ent_objective = 18, + k_ent_challenge = 19, + k_ent_deleted0 = 20, //k_ent_relay = 20, + k_ent_cubemap = 21, + k_ent_miniworld = 22, + k_ent_prop = 23, + k_ent_list = 24, + k_ent_region = 25, + k_ent_glider = 26, + k_ent_npc = 27, + k_ent_armature = 28, + k_ent_script = 29, + k_ent_atom = 30, + k_ent_cutscene = 31, + k_ent_light = 32, + k_mdl_mesh = 33, + k_editer_property = 34, + k_editer_item = 35, + + k_ent_flame = 200, // FIXME: Move thiese into own file per-game. + k_ent_waterfall = 201, + k_ent_rb2 = 202, + k_ent_heatpipe = 203, + k_ent_waterlevel = 204, + k_ent_viewstone = 205, + k_ent_gauge = 206, + k_ent_particle = 207, + k_ent_deep_npc = 208, + k_ent_max +}; +#endif + +const c8 *_vg_entity_name( u32 entity_id ); + +#if 0 +enum ent_spawn_flag +{ + k_ent_spawn_flag_locked = 0x1, + k_ent_spawn_flag_group_1 = 0x10, + k_ent_spawn_flag_group_2 = 0x20, + k_ent_spawn_flag_group_3 = 0x40, + k_ent_spawn_flag_group_4 = 0x80 +}; + +struct ent_spawn +{ + struct mdl_transform transform; + u32 pstr_name; + u32 flags; +}; +#endif + +enum light_type +{ + k_light_type_point = 0, + k_light_type_spot = 1 +}; + +#define ENT_LIGHT_FLAG_DAYTIME 0x1 +#define ENT_LIGHT_FLAG_OFF 0x2 + +#if 0 +struct ent_light +{ + struct mdl_transform transform; + u32 flags, + type; + f32 colour[4]; + f32 angle, + range; + + union + { + /* These are temporary, we could remove them from the file structure. */ + struct + { + f32 inverse_world[4][3]; + f32 angle_sin_cos[2]; + }; + + struct + { + f32 lerp_start[3], lerp_end[3]; + f32 lerp_duration, timer; + }; + }; +}; +#endif + +/* v101 */ +#if 0 +enum gate_type{ + k_gate_type_unlinked = 0, + k_gate_type_teleport = 1, + k_gate_type_nonlocal_unlinked = 2, + k_gate_type_nonlocel = 3 +}; +#endif + +enum list_alias_type +{ + k_list_alias_none = 0, + k_list_alias_string = 1 +}; + +struct ent_list +{ + u16 entity_ref_start, entity_ref_count; + u8 alias_type, none0, none1, none2; + u32 pstr_alias; +}; + +struct file_entity_ref +{ + union entity_id entity; + u32 pstr_alias; +}; + +/* v102+ */ +enum ent_gate_flag +{ + k_ent_gate_linked = 0x1, /* this is a working portal */ + k_ent_gate_nonlocal = 0x2, /* use the key string to link this portal. + NOTE: if set, it adds the flip flag. */ + k_ent_gate_flip = 0x4, /* flip direction 180* for exiting portal */ + k_ent_gate_custom_mesh = 0x8, /* use a custom submesh instead of default */ + k_ent_gate_locked = 0x10,/* has to be unlocked to be useful */ + + k_ent_gate_clean_pass = 0x20,/* player didn't rewind while getting here */ + k_ent_gate_no_linkback = 0x40,/* NONLOCAL Recievers are not allowed to linkback through this gate */ + k_ent_gate_passive = 0x80 +}; + +struct ent_gate +{ + u32 flags, + target, + key; + + f32 dimensions[3], + co[2][3]; + + f32 q[2][4]; + + /* runtime */ + f32 to_world[4][3], transport[4][3]; + union + { + u32 timing_version; + u16 remote_addon_id; + + struct + { + u8 ref_count; + }; + }; + + union + { + f64 timing_time; + u32 cubemap_id; + }; + + u16 routes[4]; /* routes that pass through this gate */ + u8 route_count; + + /* v102+ */ + u32 submesh_start, submesh_count; +}; + +struct ent_route_node +{ + f32 co[3]; + u8 ref_count, ref_total; +}; + +struct ent_path_index +{ + u16 index; +}; + +struct ent_checkpoint +{ + u16 gate_index, + path_start, + path_count; + + /* EXTENSION */ + f32 best_time; +}; + +enum ent_route_flag +{ + k_ent_route_flag_achieve_silver = 0x1, + k_ent_route_flag_achieve_gold = 0x2, + + k_ent_route_flag_out_of_zone = 0x10, + k_ent_region_flag_hasname = 0x20, + k_ent_route_flag_target = 0x40 +}; + +struct ent_route +{ + union + { + struct mdl_transform transform; + u32 official_track_id; /* TODO: remove this */ + } + anon; + + u32 pstr_name; + u16 checkpoints_start, + checkpoints_count; + + f32 colour[4]; + + /* runtime */ + u16 active_checkpoint, + valid_checkpoints; + + f32 factive; + f32 board_transform[4][3]; + struct mdl_submesh sm; + f64 timing_base; + + u32 id_camera; /* v103+ */ + + /* v104+, but always accessible */ + u32 flags; + f64 best_laptime; + f32 ui_stopper, ui_residual; + i16 ui_first_block_width, ui_residual_block_w; +}; + +struct ent_water +{ + struct mdl_transform transform; + f32 max_dist; + u32 reserved0, reserved1; +}; + +struct ent_audio_clip +{ + union + { + struct mdl_file file; + // struct audio_clip clip; FIXME, MUST ALLOCATE THIS LATER INSTEAD OF PUTTING IT IN THE FUCKING FILE STRUCT. WHO CARES IF WE LOSE HALF A KB + } + _; + + f32 probability; +}; + +struct volume_particles +{ + u32 blank, blank2; +}; + +struct volume_trigger +{ + u32 blank1; + i32 blank; +}; + +struct volume_interact +{ + i32 blank; + u32 pstr_text; +}; + +enum ent_volume_flag +{ + k_ent_volume_flag_particles = 0x1, + k_ent_volume_flag_disabled = 0x2, + k_ent_volume_flag_removed0 = 0x4, + k_ent_volume_flag_interact = 0x8, + k_ent_volume_flag_water = 0x10, + k_ent_volume_flag_repeatable= 0x20 +}; + +struct ent_volume +{ + struct mdl_transform transform; + f32 to_world[4][3]; + + union + { + f32 to_local[4][3]; + f32 particle_co[3]; + }; + + u32 flags; + u32 filter; + union + { + struct volume_trigger trigger; + struct volume_interact interact; + struct volume_particles particles; + }; +}; + +struct ent_audio +{ + struct mdl_transform transform; + u32 flags, + clip_start, + clip_count; + f32 volume, crossfade; + u32 behaviour, + group, + probability_curve, + max_channels; +}; + +enum +{ + k_ent_marker_flag_hidden = 0x1, + k_ent_marker_flag_gui_icon = 0x8 +}; + +struct ent_marker +{ + struct mdl_transform transform; + u32 pstr_alias; + u32 flags; +}; + +enum skateshop_type +{ + k_skateshop_type_boardshop = 0, + k_skateshop_type_charshop = 1, + k_skateshop_type_worldshop = 2, + k_skateshop_type_DELETED = 3, + k_skateshop_type_server = 4 +}; + +struct ent_skateshop +{ + struct mdl_transform transform; + u32 type, id_camera; + + union + { + struct + { + u32 id_display, + id_info, + id_rack; + } + boards; + + struct + { + u32 id_display, + id_info, + id_rack; + } + character; + + struct + { + u32 id_display, + id_info; + } + worlds; + + struct + { + u32 id_lever; + } + server; + }; +}; + +struct ent_swspreview +{ + u32 id_camera, id_display, id_display1; +}; + +struct ent_traffic +{ + struct mdl_transform transform; + u32 submesh_start, + submesh_count, + start_node, + node_count; + f32 speed, + t; + u32 index; /* into the path */ +}; + +struct ent_camera +{ + f32 co[3], r[3]; + f32 fov; +}; + +#if (VG_MODEL_VERSION_MIN <= 107) +struct ent_camera_v107 +{ + struct mdl_transform transform; + f32 fov; +}; + +static inline void fix_ent_camera_v107( struct ent_camera_v107 *old, struct ent_camera *new ) +{ + f32 dir[3] = {0.0f,-1.0f,0.0f}; + mdl_transform_vector( &old->transform, dir, dir ); + v3_angles( dir, new->r ); + v3_copy( old->transform.co, new->co ); + new->fov = old->fov; +} +#endif + +enum world_flag +{ + k_world_flag_fixed_time = 0x1, + k_world_flag_water_is_safe = 0x2, + k_world_flag_no_skating = 0x4, + k_world_flag_no_rewind = 0x8 +}; + +struct ent_worldinfo +{ + u32 pstr_name, pstr_author, pstr_desc; + f32 timezone; + u32 pstr_skybox; + u32 flags; + f32 wind_scale; +}; + +enum channel_behaviour +{ + k_channel_behaviour_unlimited = 0, + k_channel_behaviour_discard_if_full = 1, + k_channel_behaviour_crossfade_if_full = 2 +}; + +enum probability_curve{ + k_probability_curve_constant = 0, + k_probability_curve_wildlife_day = 1, + k_probability_curve_wildlife_night = 2 +}; + +struct ent_font +{ + u32 alias, + variant_start, + variant_count, + glyph_start, + glyph_count, + glyph_utf32_base; +}; + +struct ent_font_variant +{ + u32 name, + material_id; +}; + +struct ent_glyph +{ + f32 size[2]; + u32 indice_start, + indice_count; +}; + +struct ent_ccmd +{ + u32 pstr_command; +}; + +enum ent_script_flag +{ + k_ent_script_flag_linked = 0x1 +}; + +struct ent_script +{ + union + { + u32 pstr_script_name, /* When its in the file */ + script_id; /* When its runtime */ + }; + + u32 entity_list_id; + u32 flags; +}; + +enum ent_atom_flag +{ + k_ent_atom_global = 0x1, + k_ent_atom_scrap = 0x2 +}; + +struct ent_atom +{ + union + { + u32 pstr_alias; + i32 scrap_value; + }; + + u32 flags; +}; + +enum ent_cutscene_flag +{ + k_ent_cutscene_freeze_player = 0x1, +}; + +struct ent_cutscene +{ + u32 pstr_path, + flags; +}; + +enum ent_objective_filter{ + k_ent_objective_filter_none = 0x00000000, + k_ent_objective_filter_trick_shuvit = 0x00000001, + k_ent_objective_filter_trick_kickflip = 0x00000002, + k_ent_objective_filter_trick_treflip = 0x00000004, + k_ent_objective_filter_trick_any = + k_ent_objective_filter_trick_shuvit| + k_ent_objective_filter_trick_treflip| + k_ent_objective_filter_trick_kickflip, + k_ent_objective_filter_flip_back = 0x00000008, + k_ent_objective_filter_flip_front = 0x00000010, + k_ent_objective_filter_flip_any = + k_ent_objective_filter_flip_back| + k_ent_objective_filter_flip_front, + k_ent_objective_filter_grind_truck_any = 0x00000020, + k_ent_objective_filter_grind_board_any = 0x00000040, + k_ent_objective_filter_grind_any = + k_ent_objective_filter_grind_truck_any| + k_ent_objective_filter_grind_board_any, + k_ent_objective_filter_footplant = 0x00000080, + k_ent_objective_filter_passthrough = 0x00000100, + k_ent_objective_filter_glider = 0x00000200 +}; + +enum ent_objective_flag +{ + k_ent_objective_hidden = 0x1, + k_ent_objective_passed = 0x2, + k_ent_objective_failed = 0x4 +}; + +struct ent_objective +{ + struct mdl_transform transform; + u32 submesh_start, + submesh_count, + flags, + id_next, + filter,filter2, + deleted0; + i32 deleted1; + f32 time_limit; + u32 pstr_description_ui; +}; + +enum ent_challenge_flag +{ + k_ent_challenge_timelimit = 0x1, + //k_ent_challenge_is_story = 0x2, + k_ent_challenge_locked = 0x4, + k_ent_challenge_any_order = 0x8, + k_ent_challenge_target = 0x10 +}; + +struct ent_challenge +{ + struct mdl_transform transform; + u32 pstr_alias, + flags; + + u32 deleted0;//on_activate_id; + u32 deleted1;//on_activate_event; + u32 deleted2;//on_complete_id; + i32 deleted3;//on_complete_event; + + u32 first_objective_id; + u32 camera_id; + + u32 status; + u32 reset_spawn_id; +}; + +struct ent_cubemap +{ + f32 co[3]; + u32 resolution, live, texture_id, + framebuffer_id, renderbuffer_id, placeholder[2]; +}; + +enum prop_flag +{ + k_prop_flag_hidden = 0x1, + k_prop_flag_spinning = 0x2, + k_prop_flag_collider = 0x4, + k_prop_flag_spinning_fast = 0x8, +}; + +struct ent_prop +{ + struct mdl_transform transform; + u32 submesh_start, submesh_count, flags, pstr_alias; +}; + +enum editer_type +{ + k_editer_type_toggle = 0, + k_editer_type_slider = 1, + k_editer_type_selecter = 2 +}; + +struct editer_property +{ + u32 pstr_alias; + u8 ui_type; + + union + { + u32 _u32; + f32 _f32; + u32 pstr_options; + } + max; +}; + +struct editer_item +{ + struct mdl_transform transform; + u32 submesh_start, submesh_count; + + u32 pstr_visibility, + pstr_uv_x, + pstr_uv_y; + u16 discard_send, + discard_mask; +}; + +struct ent_region +{ + struct mdl_transform transform; + u32 submesh_start, submesh_count, pstr_title, flags; + + union + { + struct{ u32 zone_volume; } v105; + struct{ u32 id_list; } v109; + }; + + /* 105+ */ + u32 deleted01[2]; +}; + +enum ent_glider_flag +{ + k_ent_glider_flag_locked = 0x1 +}; +struct ent_glider +{ + struct mdl_transform transform; + u32 flags; + f32 cooldown; +}; + +struct ent_npc +{ + struct mdl_transform transform; + u32 pstr_id, pstr_context_id, pstr_anim, none1; +}; + +enum entity_event_result +{ + k_entity_event_result_OK, + k_entity_event_result_unhandled, + k_entity_event_result_invalid, + k_entity_event_result_invalid_parameter, +}; + +enum ent_event_flags +{ + k_ent_event_data_void = 0x0, + k_ent_event_data_const_i32 = 0x1, + k_ent_event_data_const_f32 = 0x2, + k_ent_event_data_const_entity_id = 0x4, + k_ent_event_data_const_string = 0x8, + k_ent_event_data_data_alias = 0x10, + k_ent_event_data_v3f = 0x20, +}; + +struct ent_event +{ + u32 pstr_source_event; + u32 pstr_recieve_event; + + union entity_id source; + union entity_id reciever; + u32 flags; + + f32 delay; + u8 function_index, activator, unused0, unused1; // FIXME: REPLACES PSTR!!!!!!!!!!! + + union + { + i32 const_i32; + f32 const_f32; + union entity_id const_entity_id; + u32 const_pstr; + u32 pstr_data_alias; + } + data; +}; + +struct entity_info +{ + u16 id, unused0; + u16 function_start, function_count; + const c8 *name; +}; +struct entity_function_info +{ + const c8 *name; +}; + +struct entity_info *_vg_entity_info( u16 entity_type ); +enum entity_event_result _vg_entity_event_dispatch( struct ent_event *event ); +i32 _vg_entity_link_function( u16 entity_type, const c8 *alias ); diff --git a/source/engine/vg_framebuffer.h b/source/engine/vg_framebuffer.h index a086efa..ed3b41f 100644 --- a/source/engine/vg_framebuffer.h +++ b/source/engine/vg_framebuffer.h @@ -56,6 +56,8 @@ struct framebuffer attachments[]; }; +void _framebuffer_resize(void); + struct framebuffer *_framebuffer_allocate( struct stack_allocator *stack, u32 attachment_count, b8 track ); /* diff --git a/source/engine/vg_input.h b/source/engine/vg_input.h index c26b669..8fadff2 100644 --- a/source/engine/vg_input.h +++ b/source/engine/vg_input.h @@ -33,6 +33,9 @@ enum input_button_action }; void _input_callback_buttonlike( enum input_device device, i32 id, enum input_button_action action, u32 mods ); +void _input_init(void); +void _input_update(void); + u8 _input_button_down( enum input_id id, b8 allow_repeats ); u8 _input_button_up( enum input_id id ); u8 _input_button( enum input_id id ); diff --git a/source/engine/vg_lines.h b/source/engine/vg_lines.h index 62fb781..6a2a356 100644 --- a/source/engine/vg_lines.h +++ b/source/engine/vg_lines.h @@ -1,3 +1,4 @@ +#pragma once #define LINE_RED 0xff0000ff #define LINE_GREEN 0xff00ff00 #define LINE_BLUE 0xffff0000 @@ -9,6 +10,7 @@ #define LINE_CYAN 0xffffff00 #define LINE_NONE 0x00000000 +void _vg_lines_init(void); void vg_lines_clear(void); void vg_lines_draw( f32 pv[4][4] ); diff --git a/source/engine/vg_metascene.c b/source/engine/vg_metascene.c new file mode 100644 index 0000000..783a965 --- /dev/null +++ b/source/engine/vg_metascene.c @@ -0,0 +1,499 @@ +#include "vg_engine.h" +#include "vg_metascene.h" +#include "common_maths.h" +#include "vg_model.h" +#include "vg_entity.h" +#include "shader_props.h" +#include "vg_af.h" +#include "vg_async.h" + +void metascene_load( struct metascene *ms, const c8 *path, struct stack_allocator *stack ) +{ + ASSERT_CRITICAL( _thread_has_flags( _get_thread_id(), THREAD_FLAG_ASYNC ) ); + zero_buffer( ms, sizeof(struct metascene) ); + + struct stream stream; + ASSERT_CRITICAL( stream_open_file( &stream, path, k_stream_read ) ); + + u32 temp_frame = _start_temporary_frame(); + { + struct array_file_context af; + ASSERT_CRITICAL( af_open_stream( &af, &stream, MS_VERSION_MIN, MS_VERSION_NR, _temporary_stack_allocator() ) ); + + struct array_file_ptr strings; + af_load_array( &af, &strings, "strings", stack, 1 ); + ms->packed_strings = strings.data; + af_load_array( &af, &ms->infos, "ms_scene_info", stack, sizeof(struct ms_scene_info) ); + af_load_array( &af, &ms->instances, "ms_instance", stack, sizeof(struct ms_instance) ); + af_load_array( &af, &ms->overrides, "ms_override", stack, sizeof(struct ms_override) ); + af_load_array( &af, &ms->strips, "ms_strip", stack, sizeof(struct ms_strip) ); + af_load_array( &af, &ms->tracks, "ms_track", stack, sizeof(struct ms_track) ); + af_load_array( &af, &ms->keyframes, "ms_keyframe", stack, sizeof(struct ms_keyframe) ); + af_load_array( &af, &ms->cameras, "ent_camera", stack, sizeof(struct ent_camera) ); + af_load_array( &af, &ms->audios, "ent_audio", stack, sizeof(struct ent_audio) ); + af_load_array( &af, &ms->audio_clips, "ent_audio_clip", stack, sizeof(struct ent_audio_clip) ); + af_load_array( &af, &ms->curves, "ms_curves", stack, sizeof(struct ms_curve_keyframe) ); + + ASSERT_CRITICAL( af_arrcount( &ms->infos ) ); + struct ms_scene_info *src_inf = af_arritm( &ms->infos, 0 ); + ms->info = *src_inf; + + stream_close( &stream ); + } + _end_temporary_frame( temp_frame ); +} + +u32 skeleton_bone_id( struct ms_skeleton *skele, const c8 *name ) +{ + for( u32 i=1; ibone_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; ico, 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; ibone_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; ibone_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; ibone_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; ibone_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; iik_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; ibone_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; ibone_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; iik_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; ibone_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; ibone_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; ibone_count; i ++ ) + { + struct ms_skeleton_bone *sb = &skele->bones[i]; + f32 p0[3], p1[3]; + v3_copy( sb->co, p0 ); + v3_add( p0, sb->end, p1 ); + + m4x3_mulv( final_mtx[i], p0, p0 ); + m4x3_mulv( final_mtx[i], p1, p1 ); + + if( sb->flags & k_bone_flag_deform ) + { + if( sb->flags & k_bone_flag_ik ) + vg_line( p0, p1, 0xff0000ff ); + else + vg_line( p0, p1, 0xffcccccc ); + } + else + vg_line( p0, p1, 0xff00ffff ); + } +} +#endif diff --git a/source/engine/vg_metascene.h b/source/engine/vg_metascene.h new file mode 100644 index 0000000..6b3c1ac --- /dev/null +++ b/source/engine/vg_metascene.h @@ -0,0 +1,186 @@ +#pragma once +#include "foundation.h" +#include "vg_af.h" +#include "vg_model.h" +#include "vg_entity.h" + +#define MS_VERSION_NR 2 +#define MS_VERSION_MIN 2 + +struct ms_scene_info +{ + f32 framerate; + u32 end_frame; +}; + +struct metascene +{ + const void *packed_strings; + struct ms_scene_info info; + struct array_file_ptr infos, + instances, + overrides, + strips, + tracks, + keyframes, + curves, + + audios, /* kinda temp? */ + audio_clips, + cameras; +}; + +struct ms_instance +{ + u32 pstr_name, + override_start, + override_count; +}; + +struct ms_override +{ + u32 entity_type, pstr_name; + struct mdl_transform transform; +}; + +enum ms_strip_mode +{ + k_ms_strip_mode_keyframes = 0x1, + k_ms_strip_mode_curves = 0x2, + k_ms_strip_mode_animation = 0x1|0x2, + + k_ms_strip_mode_camera = 0x10, + k_ms_strip_mode_event = 0x20, + k_ms_strip_mode_subtitle = 0x40, + k_ms_strip_mode_fadeout = 0x80, +}; + +struct ms_strip +{ + u8 mode; + i32 offset; + + union + { + struct + { + u32 start, count, length, + pstr_name, + pstr_internal_name, + instance_id, object_id; + f32 timing_offset; + } + strip; + + struct + { + union entity_id entity; + } + camera; + + struct + { + u32 pstr_en, res0, res1, res2; + u8 character; + } + subtitle; + + struct + { + u32 pstr_string; + } + event; + }; +}; + +struct ms_track +{ + u32 keyframe_start, + keyframe_count, + pstr_datapath, + semantic_type; +}; + +struct ms_curve_keyframe +{ + f32 co[2], l[2], r[2]; +}; + +struct ms_keyframe +{ + f32 co[3], s[3]; + f32 q[4]; +}; + +/* skeletons + * ------------------------------------------------------------------------------------------------------------------ */ + +struct ms_skeleton +{ + struct ms_skeleton_bone + { + f32 co[3], end[3]; + u32 parent; + + u32 flags; + int defer; + + //ms_keyframe kf; + struct mdl_bone *orig_bone; + u32 collider; // TODO: SOA + f32 hitbox[2][3]; // TODO: SOA + const char *name; // TODO: SOA + } + *bones; + u32 bone_count; + + struct ms_skeleton_ik + { + u32 lower, upper, target, pole; + f32 ia[3][3], ib[3][3]; + } + *ik; + u32 ik_count; + u32 collider_count, + bindable_count; +}; + +void metascene_load( struct metascene *ms, const c8 *path, struct stack_allocator *stack ); +void keyframe_copy_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, i32 num ); +void keyframe_rotate_around( struct ms_keyframe *kf, f32 origin[3], f32 offset[3], f32 q[4] ); +void keyframe_lerp( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd ); +void keyframe_lerp_pose( struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd, i32 count ); +void skeleton_alloc_from( struct ms_skeleton *skele, struct stack_allocator *stack, + struct vg_model *model, struct mdl_armature *armature ); +u32 skeleton_bone_id( struct ms_skeleton *skele, const c8 *name ); +void skeleton_lerp_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfb, f32 t, struct ms_keyframe *kfd ); +void skeleton_copy_pose( struct ms_skeleton *skele, struct ms_keyframe *kfa, struct ms_keyframe *kfd ); + +struct ms_skeletal_animation +{ + struct ms_strip *strip; + struct ms_keyframe *keyframes_base; + f32 framerate; +}; + +void skeleton_sample_anim( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output ); +int skeleton_sample_anim_clamped( struct ms_skeleton *skele, struct ms_skeletal_animation *anim, f32 time, struct ms_keyframe *output ); + +enum anim_apply +{ + k_anim_apply_always, + k_anim_apply_defer_ik, + k_anim_apply_deffered_only, + k_anim_apply_absolute +}; + +void skeleton_apply_pose( struct ms_skeleton *skele, struct ms_keyframe *pose, enum anim_apply passtype, f32 final_mtx[][4][3] ); +void skeleton_decompose_mtx_absolute( struct ms_skeleton *skele, struct ms_keyframe *anim, f32 final_mtx[][4][3] ); +void skeleton_inverse_for_ik( struct ms_skeleton *skele, f32 ivaxis[3], u32 id, f32 inverse[3][3] ); +void skeleton_create_inverses( struct ms_skeleton *skele ); +void skeleton_apply_transform( struct ms_skeleton *skele, f32 transform[4][3], f32 final_mtx[][4][3] ); +void skeleton_apply_inverses( struct ms_skeleton *skele, f32 final_mtx[][4][3] ); +void skeleton_apply_ik_pass( struct ms_skeleton *skele, f32 final_mtx[][4][3] ); +void skeleton_apply_standard( struct ms_skeleton *skele, struct ms_keyframe *pose, f32 transform[4][3], f32 final_mtx[][4][3] ); +void skeleton_setup( struct ms_skeleton *skele, struct vg_model *model, u32 index, struct stack_allocator *stack ); +void skeleton_debug( struct ms_skeleton *skele, f32 final_mtx[][4][3] ); diff --git a/source/engine/vg_model.c b/source/engine/vg_model.c new file mode 100644 index 0000000..fb1787a --- /dev/null +++ b/source/engine/vg_model.c @@ -0,0 +1,562 @@ +#include "foundation.h" +#include "common_maths.h" +#include "vg_async.h" + +#include "vg_model.h" +#include "vg_af.h" +#include "vg_opengl.h" +#include "shader_props.h" +#include + +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; imodel->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; imodel->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; imodel->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; imodel->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; jworkshop.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; imodel->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; imodel->submesh_count; i++ ) + { + struct mdl_submesh *sm = &ctx->model->submeshes[ i ]; + u32 *indices = args->indice_buffer + sm->indice_start; + + for( u32 j=0; jindice_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; imodel->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; itexture_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; imesh_count; i++ ) + { + struct mdl_mesh *mesh = &model->meshes[ i ]; + if( af_str_eq( model->packed_strings, mesh->pstr_name, name, hash ) ) + return i; + } + return -1; +} + +i32 vg_model_get_submesh_index( struct vg_model *model, const c8 *mesh_name ) +{ + i32 mesh_index = vg_model_get_mesh_index( model, mesh_name ); + if( mesh_index == -1 ) + return -1; + + struct mdl_mesh *mesh = &model->meshes[ mesh_index ]; + if( !mesh->submesh_count ) + return -1; + return mesh->submesh_start; +} + +void vg_model_bind_texture( struct vg_model *model, GLuint target, u32 tex_id, u32 slot ) +{ + if( tex_id ) + { + union mdl_texture *mdl_tex = &model->textures[ tex_id -1 ]; + vg_tex_bind( target, &mdl_tex->tex, slot ); + } + else + vg_tex_bind( target, NULL, slot ); +} diff --git a/source/engine/vg_model.h b/source/engine/vg_model.h new file mode 100644 index 0000000..936e065 --- /dev/null +++ b/source/engine/vg_model.h @@ -0,0 +1,288 @@ +#pragma once +#include "vg_tex.h" +#include "shader_props.h" +#include "vg_af.h" + +#define VG_MODEL_VERSION_MIN 110 +#define VG_MODEL_VERSION_NR 110 + +union entity_id +{ + u32 id; + struct + { + u16 index; + u16 type; + }; +}; + +enum mdl_shader +{ + k_shader_standard = 0, + k_shader_standard_cutout = 1, + k_shader_terrain_blend = 2, + k_shader_standard_vertex_blend = 3, + k_shader_water = 4, + k_shader_invisible = 5, + k_shader_boundary = 6, + k_shader_fxglow = 7, + k_shader_cubemap = 8, + k_shader_walking = 9, + k_shader_foliage = 10, + k_shader_workshop = 11, + k_shader_pipe = 12, + k_shader_override = 30000 +}; + +enum mdl_surface_prop +{ + k_surface_prop_concrete = 0, + k_surface_prop_wood = 1, + k_surface_prop_grass = 2, + k_surface_prop_tiles = 3, + k_surface_prop_metal = 4, + k_surface_prop_snow = 5, + k_surface_prop_sand = 6 +}; + +enum material_flag +{ + k_material_flag_skate_target = 0x0001, + k_material_flag_collision = 0x0002, + k_material_flag_grow_grass = 0x0004, + k_material_flag_grindable = 0x0008, + k_material_flag_invisible = 0x0010, + k_material_flag_boundary = 0x0020, + k_material_flag_preview_visibile = 0x0040, + k_material_flag_walking = 0x0080, + + k_material_flag_ghosts = + k_material_flag_boundary| + k_material_flag_invisible| + k_material_flag_walking, + + k_material_flag_spritesheet = 0x1000 +}; + +#pragma pack(push,1) + +/* 48 byte */ +struct mdl_vert +{ + f32 co[3], /* 3*32 */ + norm[3]; /* 3*32 */ + f32 uv[2]; /* 2*32 */ + + u8 colour[4]; /* 4*8 */ + u16 weights[4];/* 4*16 */ + u8 groups[4]; /* 4*8 */ +}; + +#pragma pack(pop) + +struct mdl_transform +{ + f32 co[3], s[3]; + f32 q[4]; +}; + +void mdl_transform_identity( struct mdl_transform *transform ); +void mdl_transform_vector( struct mdl_transform *transform, f32 vec[3], f32 dest[3] ); +void mdl_transform_point( struct mdl_transform *transform, f32 co[3], f32 dest[3] ); +void mdl_transform_mul( struct mdl_transform *a, struct mdl_transform *b, struct mdl_transform *d ); + +#if (VG_MODEL_VERSION_MIN <= 105) +struct mdl_material_v101 +{ + u32 pstr_name, + shader, + flags, + surface_prop; + + f32 colour[4], + colour1[4]; + + u32 tex_diffuse, /* Indexes start from 1. 0 if missing. */ + tex_none0, + tex_none1; +}; +#endif + +struct mdl_material +{ + u32 pstr_name, + shader, + flags, + surface_prop; + + union + { + struct + { + u32 offset, size; /* indexes shader data */ + } + kvs; + u32 kv_root; /* runtime */ + } + props; +}; + +struct mdl_bone +{ + f32 co[3], end[3]; + u32 parent, + collider, + ik_target, + ik_pole, + flags, + pstr_name; + + f32 hitbox[2][3]; + f32 conevx[3], conevy[3], coneva[3]; + f32 conet; +}; + +enum bone_flag +{ + k_bone_flag_deform = 0x00000001, + k_bone_flag_ik = 0x00000002, + k_bone_flag_cone_constraint = 0x00000004 +}; + +enum bone_collider +{ + k_bone_collider_none = 0, + k_bone_collider_box = 1, + k_bone_collider_capsule = 2 +}; + +struct mdl_armature +{ + struct mdl_transform transform; + u32 bone_start, + bone_count, + anim_start_OBSOLETE_107, // obsolete 107+ + anim_count_OBSOLETE_107, // . + pstr_name; // v107+ +}; + +struct mdl_submesh +{ + u32 indice_start, + indice_count, + vertex_start, + vertex_count; + + f32 bbx[2][3]; + u16 material_id, flags; +}; + +enum esubmesh_flags +{ + k_submesh_flag_none = 0x0000, + k_submesh_flag_consumed = 0x0001 +}; + +struct mdl_mesh +{ + struct mdl_transform transform; + u32 submesh_start, + submesh_count, + pstr_name; + union entity_id entity; + union entity_id armature; +}; + +struct mdl_file +{ + u32 pstr_path, + pack_offset, + pack_size; +}; + +union mdl_texture +{ + struct mdl_file file; + struct vg_tex tex; +}; + +struct vg_model +{ + u32 version; + u32 flags; + + /* VG_MODEL_CPU_METADATA ---------------------- */ + const void *packed_strings; + union shader_props *shader_props; + + struct mdl_mesh *meshes; + u32 mesh_count; + + struct mdl_submesh *submeshes; + u32 submesh_count; + + struct mdl_material *materials; + u32 material_count; + + union mdl_texture *textures; + u32 texture_count; + + struct mdl_armature *armatures; + u32 armature_count; + + struct mdl_bone *bones; + u32 bone_count; + + /* VG_MODEL_CPU_MESHES ---------------------- */ + struct mdl_vert *verts; + u32 vert_count; + u32 *indices; + u32 indice_count; + + u32 pack_base_offset; + + /* VG_MODEL_GPU_MESHES ----------------------- */ + GLuint vao, vbo, ebo; +}; + +#define VG_MODEL_CPU_METADATA 0x4 +#define VG_MODEL_CPU_MESHES 0x8 +# define VG_MODEL_GPU_TEXTURES 0x1 +# define VG_MODEL_GPU_MESHES 0x2 +# define VG_MODEL_ENGINE_STANDARD (VG_MODEL_CPU_METADATA|VG_MODEL_GPU_TEXTURES|VG_MODEL_GPU_MESHES) +# define VG_MODEL_ENGINE_PROCEDURAL_SOURCE (VG_MODEL_CPU_METADATA|VG_MODEL_GPU_TEXTURES|VG_MODEL_CPU_MESHES) + +b8 vg_model_load( struct vg_model *model, u32 model_flags, const c8 *path, struct stack_allocator *stack ); +void vg_model_unload_gpu( struct vg_model *model ); + +/* Sub functions for each part of the load process + * --------------------------------------------------------------------------------------------------------------- */ +struct vg_model_stream_context +{ + struct stream stream; + struct array_file_context af; + struct vg_model *model; + u32 temp_frame; +}; + +b8 vg_model_stream_open( struct vg_model_stream_context *ctx, struct vg_model *model, const c8 *path ); +void vg_model_stream_close( struct vg_model_stream_context *ctx ); + +/* Parts which you might want (currently they all require metadata to be explicitly loaded) */ +void vg_model_stream_metadata( struct vg_model_stream_context *ctx, struct stack_allocator *stack ); +void vg_model_stream_meshes_cpu( struct vg_model_stream_context *ctx, struct stack_allocator *stack ); + +void vg_model_stream_meshes_gpu( struct vg_model_stream_context *ctx, u32 *fixup_table ); +void vg_model_stream_textures_gpu( struct vg_model_stream_context *ctx ); + +struct stream *vg_model_stream_pack_stream( struct vg_model_stream_context *ctx, struct mdl_file *file ); + +/* Rendering operations + * ----------------------------------------------------------------------------------------------------------------- */ +void vg_model_bind_mesh( struct vg_model *model ); +void vg_model_bind_texture( struct vg_model *model, GLuint target, u32 tex_id, u32 slot ); +void vg_model_draw_elements( u32 start, u32 count ); +void vg_model_draw_submesh( struct mdl_submesh *sm ); +i32 vg_model_get_mesh_index( struct vg_model *model, const c8 *name ); +i32 vg_model_get_submesh_index( struct vg_model *model, const c8 *mesh_name ); +void mdl_transform_m4x3( struct mdl_transform *transform, f32 mtx[4][3] ); diff --git a/source/engine/vg_render.h b/source/engine/vg_render.h index f052b0a..84946a5 100644 --- a/source/engine/vg_render.h +++ b/source/engine/vg_render.h @@ -1,2 +1,4 @@ #pragma once + +void _vg_render_init(void); void _render_fullscreen_quad(void); diff --git a/source/engine/vg_shader.h b/source/engine/vg_shader.h index 76ee8e7..1f2c925 100644 --- a/source/engine/vg_shader.h +++ b/source/engine/vg_shader.h @@ -1,6 +1,7 @@ #include "vg_opengl.h" #include "generated/shaders.h" +void _shader_init(void); GLuint compile_opengl_subshader( GLint type, const c8 *sources[], u32 source_count, b8 critical, const c8 *name ); b8 link_opengl_program( GLuint program, b8 critical ); void _shader_bind( enum shader_id id ); diff --git a/source/engine/vg_tex.h b/source/engine/vg_tex.h index 89c14c4..df597d4 100644 --- a/source/engine/vg_tex.h +++ b/source/engine/vg_tex.h @@ -28,6 +28,8 @@ struct qoi_desc # define QOI_SRGB 0 # define QOI_LINEAR 1 +void _vg_tex_init(void); + /* Reading qois */ u32 vg_qoi_stream_init( struct qoi_desc *desc, struct stream *file ); void vg_qoi_stream_decode( struct qoi_desc *desc, struct stream *file, u8 *dest_buffer, b8 v_flip ); diff --git a/source/engine/vg_ui.h b/source/engine/vg_ui.h index 22d8b25..fe8fec3 100644 --- a/source/engine/vg_ui.h +++ b/source/engine/vg_ui.h @@ -7,6 +7,7 @@ struct _engine_ui } extern _engine_ui; +void _engine_ui_init(void); void _engine_ui_pre_render(void); void _engine_ui_post_render(void); b8 _engine_ui_want_text_input(void); diff --git a/source/foundation/foundation.h b/source/foundation/foundation.h index ad108a4..fe6fb81 100644 --- a/source/foundation/foundation.h +++ b/source/foundation/foundation.h @@ -1,12 +1,14 @@ /* Voyager common application interface */ #pragma once +#if 0 #define VG_PRE_MAIN \ _exit_init(); \ _log_init(); \ _options_init( argc, argv ); \ EVENT_CALL( OPTIONS ); \ _options_check_end(); +#endif #define BYTES_KB( X ) X*1024 #define BYTES_MB( X ) X*1024*1024 diff --git a/source/foundation/foundation.kv b/source/foundation/foundation.kv index 6145269..c4c4991 100644 --- a/source/foundation/foundation.kv +++ b/source/foundation/foundation.kv @@ -1,28 +1,6 @@ include "" - -event -{ - name OPTIONS - prototype "void" -} -event -{ - name START - prototype "void" -} -event -{ - name END - prototype "void" -} - add options.c add logging.c -{ - hook OPTIONS - function _log_options -} - add allocator_heap.c add allocator_stack.c add allocator_pool.c diff --git a/source/foundation/keyvalues.c b/source/foundation/keyvalues.c index dc0984a..39898fd 100644 --- a/source/foundation/keyvalues.c +++ b/source/foundation/keyvalues.c @@ -393,10 +393,11 @@ void keyvalues_parse_stream( struct keyvalues *kvs, u32 root_offset, struct stre while( stream_read( in_stream, &c, 1 ) ) { parser.stat_source_characters ++; - if( c == '\0' ) - break; b8 is_control_character = 0; + if( c == 0 ) + is_control_character = 1; + if( parser.token0_deliminator ) { if( c == parser.token0_deliminator ) @@ -439,40 +440,38 @@ void keyvalues_parse_stream( struct keyvalues *kvs, u32 root_offset, struct stre parser.token0_length = 0; } - if( c=='{'||c=='}'||c=='\n' ) + if( c == '{' ) { - if( c == '{' ) + struct keyvalue *kv = keyvalues_new( parser.kvs ); + if( parser.token1_length ) { - struct keyvalue *kv = keyvalues_new( parser.kvs ); - if( parser.token1_length ) - { - kv->key_info = (0xFFFFF & parser.token1_hash) | (0x3FF & parser.token1_length) << 20; - kv->key_offset = parser.token1_start_offset; - } - else - kv->key_info = 5381; - - u32 id = stack_offset( parser.kvs->stack, kv ), - depth = parser.depth; - - parser.depth ++; - parser.frame_stack[ parser.depth ].latest_child_offset = 0; - parser.frame_stack[ parser.depth ].frame_offset = id; - keyvalue_parser_link( &parser, id, depth ); - parser.token1_length = 0; + kv->key_info = (0xFFFFF & parser.token1_hash) | (0x3FF & parser.token1_length) << 20; + kv->key_offset = parser.token1_start_offset; } - else if( c == '}' ) + else + kv->key_info = 5381; + + u32 id = stack_offset( parser.kvs->stack, kv ), + depth = parser.depth; + + parser.depth ++; + parser.frame_stack[ parser.depth ].latest_child_offset = 0; + parser.frame_stack[ parser.depth ].frame_offset = id; + keyvalue_parser_link( &parser, id, depth ); + parser.token1_length = 0; + } + else if( c == '}' ) + { + if( parser.depth ) + parser.depth --; + else { - if( parser.depth ) - parser.depth --; - else - { - /* We specify that a depth of -1 by way of closing bracket is just an END marker and not an error */ - return; - } - parser.token1_length = 0; + /* We specify that a depth of -1 by way of closing bracket is just an END marker and not an error */ + return; } + parser.token1_length = 0; } + parser.token0_deliminator = 0; } else @@ -506,6 +505,9 @@ void keyvalues_parse_stream( struct keyvalues *kvs, u32 root_offset, struct stre } } } + + if( c == '\0' ) + break; } } diff --git a/source/tools/metacompiler.c b/source/tools/metacompiler.c index 8d0c6e0..8601165 100644 --- a/source/tools/metacompiler.c +++ b/source/tools/metacompiler.c @@ -13,6 +13,12 @@ u32 _option_optimise = 0; struct stream _assemble_file; struct keyvalues _assembly; +b8 _option_codegen_shaders = 0; +b8 _option_codegen_inputs = 0; +b8 _option_codegen_console = 0; +b8 _option_codegen_threads = 0; +b8 _option_codegen_entities= 0; + enum libc_version { k_libc_version_native = 0, @@ -58,13 +64,6 @@ const c8 *platform_names[] = [k_platform_web] = "web" }; -struct -{ - struct stream name, tripple, folder; - const c8 *enabled_features[ 64 ]; -} -static _metacompiler; - struct shell_command { u32 temp_frame; @@ -87,27 +86,79 @@ void _shell_run( struct shell_command *command ) _end_temporary_frame( command->temp_frame ); } -u32 _assembly_kv_file_context( u32 kv ) +struct +{ + struct stream name, tripple, folder; + const c8 *enabled_features[ 64 ]; +} +static _metacompiler; + +struct stack_allocator _source_text; +struct file_context { - u32 vgc_block = keyvalues_get( &_assembly, 0, "vgc", 0 ); - ASSERT_CRITICAL( vgc_block ); + const c8 *full_path; + u32 assembly_kv_start, assembly_kv_end; +}; - u32 narrowest = 0; - u32 it = 0; - while( keyvalues_foreach( &_assembly, &it, vgc_block, "file_context" ) ) +struct stretchy_allocator _files; + +struct file_context *_assembly_kv_file_context( u32 kv ) +{ + i32 narrowest = -1; + for( i32 i=0; iassembly_kv_start && (kv >= file->assembly_kv_start) && (kv < file->assembly_kv_end) ) + narrowest = i; + } + + ASSERT_CRITICAL( narrowest != -1 ); + return stretchy_get( &_files, narrowest ); +} + +struct file_context *_create_file_context( const c8 *path ) +{ + c8 *full_path = realpath( path, NULL ); + + /* Add it */ + struct file_context *file_context = stretchy_append( &_files ); + u32 path_length = buffer_last_index(full_path,0,0); + c8 *dest = stack_allocate( &_source_text, path_length+1, 8, "" ); + buffer_copy( full_path, 0, dest, path_length+1 ); + file_context->full_path = dest; + file_context->assembly_kv_start = 0; + file_context->assembly_kv_end = 0; + free( full_path ); + return file_context; +} + +/* This is for getting a path for external tools */ +void _cannonicalize( struct file_context *file_context, const c8 *path, struct stream *dest_string ) +{ + c8 path_buf[4096]; + struct stream cannon_path; + stream_open_buffer_write( &cannon_path, path_buf, sizeof(path_buf), k_stream_null_terminate ); - if( (kv >= min) && (kv <= max) ) - narrowest = it; + if( path[0] != '/' ) + { + i32 context_path_folder_seperator = buffer_last_index( file_context->full_path, '/', 0 ); + ASSERT_CRITICAL( context_path_folder_seperator > -1 ); + string_append( &cannon_path, file_context->full_path, context_path_folder_seperator ); + string_append( &cannon_path, "/", 0 ); + } + string_append( &cannon_path, path, 0 ); + c8 *source_file_path = realpath( string_get( &cannon_path ), NULL ); + if( !source_file_path ) + { + $log( $error, {string_get(&cannon_path)}, {"... "}, $errno() ); + ASSERT_CRITICAL( source_file_path ); } - return narrowest; + string_append( dest_string, source_file_path, 0 ); + free( source_file_path ); } -void _assemble_kv_file( const c8 *path, u32 assembly_block, u32 feature_count ); -void _parse_kv_block( struct keyvalues *kvs, u32 block, u32 file_context, u32 assembly_block, u32 feature_count ) +void _assemble_unit_file( const c8 *path, u32 assembly_block, u32 feature_count ); +void _parse_kv_block( struct keyvalues *kvs, u32 block, struct file_context *file_context, u32 assembly_block, u32 feature_count ) { u32 it = 0; while( keyvalues_foreach( kvs, &it, block, NULL ) ) @@ -150,12 +201,8 @@ void _parse_kv_block( struct keyvalues *kvs, u32 block, u32 file_context, u32 as { struct stream path_string; stream_open_stack( &path_string, _temporary_stack_allocator(), k_stream_null_terminate ); - - const c8 *folder = keyvalues_read_string( &_assembly, file_context, "folder", NULL ); - ASSERT_CRITICAL( folder ); - - $v_string( &path_string, {folder}, {"/"}, {it_v} ); - _assemble_kv_file( string_get( &path_string ), assembly_block, feature_count ); + _cannonicalize( file_context, it_v, &path_string ); + _assemble_unit_file( string_get( &path_string ), assembly_block, feature_count ); } _end_temporary_frame( temp_frame ); } @@ -167,38 +214,19 @@ void _parse_kv_block( struct keyvalues *kvs, u32 block, u32 file_context, u32 as } } -void _assemble_kv_file( const c8 *path, u32 assembly_block, u32 feature_count ) +void _assemble_unit_file( const c8 *path, u32 assembly_block, u32 feature_count ) { - /* Append context block */ - u32 vgc_block = keyvalues_get( &_assembly, 0, "vgc", 0 ); - ASSERT_CRITICAL( vgc_block ); - u32 context_block = keyvalues_append_frame( &_assembly, vgc_block, "file_context" ); - - u32 start_offset = keyvalues_current_offset( &_assembly ); - u32 temp_frame = _start_temporary_frame(); - c8 *folder = realpath( path, NULL ); - if( !folder ) { - $log( $fatal, {"'"}, {path}, {"' "}, $errno() ); - _fatal_exit(); - } - - i32 s = buffer_last_index( folder, '/', 0 ); - if( s != -1 ) - folder[s] = '\0'; + struct file_context *file_context = _create_file_context( path ); + file_context->assembly_kv_start = keyvalues_current_offset( &_assembly ); - keyvalues_append_string( &_assembly, context_block, "folder", folder ); - free( folder ); + struct keyvalues kvs; + ASSERT_CRITICAL( keyvalues_read_file( &kvs, path, _temporary_stack_allocator() ) ); + _parse_kv_block( &kvs, 0, file_context, assembly_block, feature_count ); - /* read file and pre-process it into the assembly */ - struct keyvalues kvs; - ASSERT_CRITICAL( keyvalues_read_file( &kvs, path, _temporary_stack_allocator() ) ); - _parse_kv_block( &kvs, 0, context_block, assembly_block, feature_count ); - - u32 end_offset = keyvalues_current_offset( &_assembly ); - keyvalues_append_u32s( &_assembly, context_block, "kv_start", &start_offset, 1 ); - keyvalues_append_u32s( &_assembly, context_block, "kv_end", &end_offset, 1 ); + file_context->assembly_kv_end = keyvalues_current_offset( &_assembly ); + } _end_temporary_frame( temp_frame ); } @@ -254,6 +282,14 @@ void _metacompiler_options(void) if( _option_flag( 'O', "Run optimisers" ) ) _option_optimise = 3; + b8 all = 0; + if( _option_long( "meta-all", "All metadata programs" ) ) all = 1; + if( _option_long( "meta-shaders", "Make shader headers" ) || all ) _option_codegen_shaders = 1; + if( _option_long( "meta-inputs", "Make input headers" ) || all ) _option_codegen_inputs = 1; + if( _option_long( "meta-console", "Make console headers" )|| all ) _option_codegen_console = 1; + if( _option_long( "meta-threads", "Make thread headers" ) || all ) _option_codegen_threads = 1; + if( _option_long( "meta-entities", "Make entity headers" ) || all ) _option_codegen_entities = 1; + if( (arg = _option_argument( 'E', "Assemble target KV's only, and output it to file" )) ) { _option_assemble_only = 1; @@ -261,7 +297,6 @@ void _metacompiler_options(void) } _option_file_path = _option(); - ASSERT_CRITICAL( _option_file_path ); } void (*_event_OPTIONS_subscribers[])( void ) = @@ -280,60 +315,6 @@ void index_sort( struct sort_index *indices, u32 indice_count ) qsort( indices, indice_count, sizeof(struct sort_index), compar ); } -void _codegen_hooks(void) -{ - $log( $info, {"[CODEGEN] hooks.h/hooks.c"} ); - struct stream hooks_c, hooks_h; - stream_open_file( &hooks_h, "generated/hooks.h", k_stream_write ); - stream_open_file( &hooks_c, "generated/hooks.c", k_stream_write ); - - u32 it = 0; - while( keyvalues_foreach( &_assembly, &it, 0, "event" ) ) - { - const c8 *name = keyvalues_read_string( &_assembly, it, "name", NULL ); - const c8 *prototype = keyvalues_read_string( &_assembly, it, "prototype", NULL ); - const c8 *returns = keyvalues_read_string( &_assembly, it, "returns", "void" ); - ASSERT_CRITICAL( name && prototype && returns ); - - $v_string( &hooks_h, {"extern "}, {returns}, {" (*_event_"}, {name}, {"_subscribers[])( "}, {prototype}, {" );\n"} ); - - struct sort_index indices[ 128 ]; - u32 indice_count = 0; - - u32 user_it = 0; - while( keyvalues_foreach( &_assembly, &user_it, 0, "hook" ) ) - { - if( compare_buffers( keyvalues_read_string( &_assembly, user_it, "event", NULL ), 0, name, 0 ) ) - { - i32 affinity = 0; - keyvalues_read_i32s( &_assembly, user_it, "affinity", NULL, &affinity, 1 ); - - ASSERT_CRITICAL( indice_count < ARRAY_COUNT( indices ) ); - indices[ indice_count ].index = user_it; - indices[ indice_count ++ ].value = affinity; - - const c8 *function = keyvalues_read_string( &_assembly, user_it, "function", NULL ); - ASSERT_CRITICAL( function ); - $v_string( &hooks_c, {returns}, {" "}, {function}, {"( "}, {prototype}, {" );\n"} ); - } - } - - index_sort( indices, indice_count ); - - $v_string( &hooks_c, {returns}, {" (*_event_"}, {name}, {"_subscribers[])( "}, {prototype}, {" ) = \n{\n"} ); - for( u32 i=0; i 0) && (id < 0xffff) ); + entity_ids[ entity_count ].value = id; + + for( u32 i=0; ifunction_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; iblock, "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; iname}, {"\", .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; iblock, "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; ireciever.type == "}, $signed(ent_id), {" ){\n"} ); + u32 function_it = 0; + u32 j = 0; + while( keyvalues_foreach( &_assembly, &function_it, ent->block, "function" ) ) + { + const c8 *name = keyvalues_read_string( &_assembly, function_it, "name", NULL ); + ASSERT_CRITICAL( name ); + $v_string( &C, {" if( event->function_index == "}, $unsigned(j), {" ) return "}, + {ent->name}, {"_event_"}, {name}, {"( event );\n"} ); + j ++; + } + $v_string( &C, {" }\n"} ); + } + $v_string( &C, {" return k_entity_event_result_invalid;\n"} ); + $v_string( &C, {"}\n"} ); + + stream_close( &H ); + stream_close( &C ); + stream_close( &PY ); +} + void _default_config_gen(void) { $log( $info, {"[CODEGEN] default.cfg"} ); @@ -691,13 +907,19 @@ void _default_config_gen(void) i32 main( i32 argc, const c8 *argv[] ) { - VG_PRE_MAIN; + _exit_init(); + _log_init(); + _options_init( argc, argv ); + _metacompiler_options(); + _options_check_end(); + ASSERT_CRITICAL( _option_file_path ); /* Get name of target and do a few checks */ i32 name_start = buffer_last_index( _option_file_path, '/', 0 ); if( name_start == -1 ) name_start = 0; i32 name_end = buffer_last_index( _option_file_path+name_start, '.', 0 ); +#if 0 b8 valid_kv = 1; if( name_end == -1 ) valid_kv = 0; @@ -709,6 +931,7 @@ i32 main( i32 argc, const c8 *argv[] ) $log( $fatal, {"Target file must be a text KV file"} ); _fatal_exit(); } +#endif struct stream open_test; if( stream_open_file( &open_test, _option_file_path, k_stream_read ) ) @@ -763,6 +986,9 @@ i32 main( i32 argc, const c8 *argv[] ) stack_init( &stack, NULL, BYTES_MB(16), "Assembly KV" ); keyvalues_init( &_assembly, &stack ); + stack_init( &_source_text, NULL, BYTES_MB(32), "Source text" ); + stretchy_init( &_files, sizeof(struct file_context) ); + u32 info_block = keyvalues_append_frame( &_assembly, 0, "vgc" ); keyvalues_append_string( &_assembly, info_block, "version", "0.2" ); keyvalues_append_string( &_assembly, info_block, "name", string_get( &_metacompiler.name ) ); @@ -775,7 +1001,7 @@ i32 main( i32 argc, const c8 *argv[] ) if( _option_platform == k_platform_windows ) _metacompiler.enabled_features[start_features ++] = "windows"; if( _option_platform == k_platform_web ) _metacompiler.enabled_features[start_features ++] = "web"; - _assemble_kv_file( _option_file_path, 0, start_features ); + _assemble_unit_file( _option_file_path, 0, start_features ); if( _option_assemble_only ) { @@ -796,11 +1022,11 @@ i32 main( i32 argc, const c8 *argv[] ) _shell_run( cmd ); } - _codegen_hooks(); - _codegen_shaders(); - _codegen_inputs(); - _codegen_console(); - _codegen_threads(); + if( _option_codegen_shaders ) _codegen_shaders(); + if( _option_codegen_inputs ) _codegen_inputs(); + if( _option_codegen_console ) _codegen_console(); + if( _option_codegen_threads ) _codegen_threads(); + if( _option_codegen_entities )_codegen_entities(); { struct shell_command *cmd = _shell_command(); @@ -888,12 +1114,11 @@ i32 main( i32 argc, const c8 *argv[] ) if( include || link_path || add ) { - u32 context_block = _assembly_kv_file_context( compile_it ); if( include ) $v_string( &cmd->line, {" -I"} ); if( link_path ) $v_string( &cmd->line, {" -L"} ); if( add ) $v_string( &cmd->line, {" "} ); - $v_string( &cmd->line, {keyvalues_read_string( &_assembly, context_block, "folder", NULL )}, - {"/"}, {keyvalues_value( &_assembly, compile_it, NULL )}, {" \\\n"} ); + _cannonicalize( _assembly_kv_file_context( compile_it ), keyvalues_value( &_assembly, compile_it, NULL ), &cmd->line ); + string_append( &cmd->line, " \\\n", 0 ); } else if( define || link ) {