alloc, sizeof(ms_curve_keyframe) );
fclose( fp );
}
+
+struct
+{
+ ms_context meta;
+ void *arena;
+
+ bool ready;
+
+ struct model_ref
+ {
+ const char *name;
+ u32 name_hash;
+
+ mdl_context mdl;
+
+ struct cs_skeleton
+ {
+ struct skeleton sk;
+ u32 skinning_offset;
+ }
+ *skeletons;
+ u32 total_skinning_bones;
+ }
+ * refs;
+ u32 unique_refs;
+
+ struct cs_instance
+ {
+ u32 ref_id;
+ m4x3f *skinning_data;
+ }
+ * instances;
+ u32 instance_count;
+
+ struct cs_sampler
+ {
+ ms_strip *strip;
+ ms_override *override;
+ union
+ {
+ struct
+ {
+ ms_track *track;
+ f32 *target;
+ }
+ curves;
+
+ struct
+ {
+ struct skeleton *ref_sk;
+ m4x3f *skinning_data;
+ }
+ skeleton;
+ };
+ }
+ samplers[32];
+ u32 active_samplers;
+
+ u32 strip;
+ f32 time;
+}
+_cutscene;
+
+void cutscene_debug_ui( ui_context *ctx )
+{
+ ms_strip *usage[8];
+ for( u32 i=0; i<VG_ARRAY_LEN(usage); i ++ ) usage[i] = NULL;
+
+ for( u32 i=0; i<af_arrcount(&_cutscene.meta.strips); i ++ )
+ {
+ ms_strip *strip = af_arritm(&_cutscene.meta.strips, i );
+
+ u32 layer = 0;
+ for( u32 k=0; k<VG_ARRAY_LEN(usage); k ++ )
+ {
+ if( usage[k] )
+ {
+ if( usage[k]->offset + usage[k]->length < strip->offset )
+ {
+ usage[k] = NULL;
+ }
+ }
+
+ if( !usage[k] )
+ {
+ usage[k] = strip;
+ layer = k;
+ break;
+ }
+ }
+
+ ui_rect box = { strip->offset, layer*32, strip->length, 30 };
+ u32 ps = strip->pstr_name,
+ colour = *((u32 *)af_arritm( &_cutscene.meta.strings, ps ));
+
+ ui_fill( ctx, box, colour | 0xff000000 );
+ ui_text( ctx, box, ps_get( &_cutscene.meta.strings, ps ), 1,
+ k_ui_align_middle_center, 0 );
+ }
+
+ ui_fill( ctx, (ui_rect){ (f32)_cutscene.time*_cutscene.meta.info.framerate,
+ 0, 1, VG_ARRAY_LEN(usage)*32 }, 0xffffffff );
+}
+
+/*
+ * Find associated entity data. We should also probably do this on the world
+ * thingy
+ */
+struct cs_asoc
+{
+ mdl_context *orig_data;
+ u16 entity_type,
+ entity_index;
+ ms_override *override;
+};
+
+static void _cutscene_override_asoc( u32 instance_id, u32 override_index,
+ struct cs_asoc *out_asoc )
+{
+ struct cs_instance *instance = &_cutscene.instances[ instance_id ];
+ mdl_context *mdl = &_cutscene.refs[ instance->ref_id ].mdl;
+
+ ms_instance *oins = af_arritm( &_cutscene.meta.instances, instance_id );
+ ms_override *override =
+ af_arritm( &_cutscene.meta.overrides,
+ oins->override_start + override_index );
+
+ out_asoc->orig_data = mdl;
+ out_asoc->entity_type = override->entity_type;
+ out_asoc->override = override;
+ const char *name = ps_get( &_cutscene.meta.strings, override->pstr_name );
+
+ if( out_asoc->entity_type != 28 )
+ goto NOT_IMPLEMENTED;
+
+ u32 name_hash = vg_strdjb2( name );
+ for( u32 j=0; j<af_arrcount( &mdl->armatures ); j ++ )
+ {
+ mdl_armature *armature = af_arritm( &mdl->armatures, j );
+ if( ps_consteq( &mdl->strings, armature->pstr_name,
+ name, name_hash ) )
+ {
+ out_asoc->entity_index = j;
+ return;
+ }
+ }
+
+NOT_IMPLEMENTED:
+
+ vg_fatal_condition();
+ vg_warn( "The data association was not found.\n"
+ " Entity Type: %u\n"
+ " Entity name: %s\n", override->entity_type, name );
+ vg_fatal_exit();
+}
+
+static void _cutscene_get_strip_asoc( ms_strip *strip,
+ struct cs_asoc *out_asoc )
+{
+ if( strip->instance_id == 0xffffffff )
+ {
+ out_asoc->orig_data = NULL;// &_cutscene.meta;
+ out_asoc->entity_type = mdl_entity_id_type( strip->object_id );
+ out_asoc->entity_index = mdl_entity_id_id( strip->object_id );
+ out_asoc->override = NULL;
+ }
+ else
+ {
+ _cutscene_override_asoc( strip->instance_id, strip->object_id, out_asoc );
+ }
+}
+
+static void sync_cutscene_loaded( void *payload, u32 size )
+{
+ vg_info( "Cutscene ready\n" );
+ _cutscene.ready = 1;
+}
+
+static void cutscene_load_thread( void *data )
+{
+ const char *path = data;
+ vg_info( "Loading cutscene: %s\n", path );
+
+ u32 size = 20*1024*1024;
+ _cutscene.arena =
+ _vg_create_linear_allocator( NULL, size, VG_MEMORY_SYSTEM, "Cutscene" );
+
+ metascene_load( &_cutscene.meta, path, _cutscene.arena );
+
+ _cutscene.instance_count = af_arrcount( &_cutscene.meta.instances );
+ _cutscene.instances = vg_linear_alloc( _cutscene.arena,
+ sizeof(struct cs_instance) * _cutscene.instance_count );
+
+ _cutscene.refs = NULL;
+ _cutscene.unique_refs = 0;
+ for( u32 i=0; i < _cutscene.instance_count; i ++ )
+ {
+ ms_instance *instance = af_arritm( &_cutscene.meta.instances, i );
+ const char *name = ps_get( &_cutscene.meta.strings, instance->pstr_name );
+
+ struct model_ref *ref = NULL;
+ u32 ref_id = 0;
+
+ for( u32 j=0; j<_cutscene.unique_refs; j ++ )
+ {
+ struct model_ref *ref_j = &_cutscene.refs[ j ];
+
+ if( ps_consteq( &_cutscene.meta.strings, instance->pstr_name,
+ ref_j->name, ref_j->name_hash ) )
+ {
+ ref = ref_j;
+ ref_id = j;
+ break;
+ }
+ }
+
+ if( !ref )
+ {
+ _cutscene.refs = vg_linear_extend( _cutscene.arena, _cutscene.refs,
+ sizeof( struct model_ref ) );
+ ref_id = _cutscene.unique_refs;
+ ref = &_cutscene.refs[ ref_id ];
+ ref->name = name;
+ ref->name_hash = vg_strdjb2( name );
+ vg_info( "Indexed reference '%s'\n", name );
+ _cutscene.unique_refs ++;
+ }
+
+ _cutscene.instances[ i ].ref_id = ref_id;
+ _cutscene.instances[ i ].skinning_data = NULL;
+ }
+
+ /* load model data */
+ for( u32 i=0; i<_cutscene.unique_refs; i ++ )
+ {
+ struct model_ref *ref = &_cutscene.refs[ i ];
+ char mdl_path_buf[ 512 ];
+ vg_str mdl_path;
+ vg_strnull( &mdl_path, mdl_path_buf, sizeof(mdl_path_buf) );
+ vg_strcat( &mdl_path, ref->name );
+ vg_strcat( &mdl_path, ".mdl" );
+
+ vg_info( "Loading instance model: %s\n", mdl_path.buffer );
+ mdl_open( &ref->mdl, mdl_path.buffer, _cutscene.arena );
+ mdl_load_metadata_block( &ref->mdl, _cutscene.arena );
+ mdl_async_full_load_std( &ref->mdl );
+ mdl_close( &ref->mdl );
+
+ u32 skeleton_count = af_arrcount( &ref->mdl.armatures );
+ if( skeleton_count )
+ {
+ ref->skeletons = vg_linear_alloc( _cutscene.arena,
+ sizeof(struct cs_skeleton) * skeleton_count );
+
+ ref->total_skinning_bones = 0;
+ for( u32 j=0; j<skeleton_count; j ++ )
+ {
+ struct cs_skeleton *skele = &ref->skeletons[ j ];
+ skeleton_setup( &skele->sk, &ref->mdl, j, _cutscene.arena );
+ skele->skinning_offset = ref->total_skinning_bones;
+ ref->total_skinning_bones += skele->sk.bone_count;
+ }
+ }
+ else
+ {
+ ref->skeletons = NULL;
+ ref->total_skinning_bones = 0;
+ }
+ }
+
+ /* allocate skinning memory per-instance */
+ for( u32 i=0; i<_cutscene.instance_count; i ++ )
+ {
+ struct cs_instance *ins = &_cutscene.instances[ i ];
+ struct model_ref *ref = &_cutscene.refs[ ins->ref_id ];
+
+ ins->skinning_data = vg_linear_alloc( _cutscene.arena,
+ sizeof(m4x3f) * ref->total_skinning_bones );
+
+ for( u32 j=0; j<ref->total_skinning_bones; j ++ )
+ {
+ m4x3_identity( ins->skinning_data[ j ] );
+ }
+
+ /* load overrides */
+ ms_instance *oins = af_arritm( &_cutscene.meta.instances, i );
+ for( u32 j=0; j<oins->override_count; j ++ )
+ {
+ ms_override *override =
+ af_arritm( &_cutscene.meta.overrides, oins->override_start + j );
+
+ struct cs_asoc asoc;
+ _cutscene_override_asoc( i, j, &asoc );
+
+ VG_ASSERT( asoc.entity_type == 28 );
+
+ struct cs_skeleton *skele = &ref->skeletons[ asoc.entity_index ];
+
+ m4x3f mmdl;
+ mdl_transform_m4x3( &override->transform, mmdl );
+
+ for( u32 l=0; l<skele->sk.bone_count; l ++ )
+ {
+ m4x3_copy( mmdl, ins->skinning_data[skele->skinning_offset+l] );
+ }
+ }
+ }
+
+ vg_async_call( sync_cutscene_loaded, NULL, 0 );
+}
+
+static int ccmd_cutscene_play( int argc, const char *argv[] )
+{
+ if( argc == 1 )
+ {
+ if( _cutscene.ready )
+ {
+ vg_info( "Resetting cutscene..\n" );
+ _cutscene.time = 0.0f;
+ _cutscene.strip = 0;
+ _cutscene.active_samplers = 0;
+ return 1;
+ }
+
+ if( vg_loader_availible() )
+ {
+ _cutscene.ready = 0;
+ _cutscene.time = 0.0f;
+ _cutscene.strip = 0;
+ _cutscene.active_samplers = 0;
+
+ vg_linear_clear( vg_async.buffer );
+ u32 len = strlen( argv[0] ) +1;
+ char *path_buf = vg_linear_alloc( vg_async.buffer, len );
+ strcpy( path_buf, argv[0] );
+ vg_loader_start( cutscene_load_thread, path_buf );
+ return 1;
+ }
+ else
+ {
+ vg_error( "The loading thread is currently occupied.\n" );
+ return 0;
+ }
+ }
+ else
+ {
+ vg_error( "Usage: cutscene path/to/cutscene.ms\n" );
+ return 0;
+ }
+}
+
+void cutscene_init(void)
+{
+ vg_console_reg_cmd( "cutscene", ccmd_cutscene_play, NULL );
+}
+
+/*
+ * Currently draws everything as skinned meshes.
+ */
+void cutscene_render_instance( struct cs_instance *ins,
+ world_instance *world, vg_camera *cam )
+{
+ struct model_ref *ref = &_cutscene.refs[ ins->ref_id ];
+ mdl_context *mdl = &ref->mdl;
+ mesh_bind( &mdl->mesh );
+
+ shader_model_character_view_use();
+ shader_model_character_view_uCamera( cam->transform[3] );
+ shader_model_character_view_uPv( cam->mtx.pv );
+ shader_model_character_view_uDepthMode( 0 );
+
+ WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world, model_character_view );
+
+ glActiveTexture( GL_TEXTURE0 );
+ shader_model_character_view_uTexMain( 0 );
+
+ u32 armature_id = 0x00;
+ u32 material_id = 0x00;
+
+ for( u32 i=0; i<af_arrcount( &mdl->meshs ); i ++ )
+ {
+ mdl_mesh *mesh = af_arritm( &mdl->meshs, i );
+ VG_ASSERT( mesh->armature_id );
+
+ if( mesh->armature_id != armature_id )
+ {
+ armature_id = mesh->armature_id;
+ u32 sk_index = mdl_entity_id_id( armature_id );
+
+ struct cs_skeleton *skele = &ref->skeletons[ sk_index ];
+
+ m4x3f *skinning_data = ins->skinning_data + skele->skinning_offset;
+ glUniformMatrix4x3fv( _uniform_model_character_view_uTransforms,
+ skele->sk.bone_count,
+ 0,
+ (const GLfloat *)skinning_data );
+ }
+
+ for( u32 j=0; j<mesh->submesh_count; j ++ )
+ {
+ mdl_submesh *sm = af_arritm( &mdl->submeshs, mesh->submesh_start+j );
+ VG_ASSERT( sm->material_id );
+
+ if( sm->material_id != material_id )
+ {
+ mdl_material *m = af_arritm( &mdl->materials, sm->material_id-1 );
+ VG_ASSERT( m->shader == k_shader_standard );
+
+ struct shader_props_standard *props = m->props.compiled;
+ VG_ASSERT( props->tex_diffuse );
+
+ mdl_texture *tex = af_arritm( &mdl->textures, props->tex_diffuse-1);
+ glBindTexture( GL_TEXTURE_2D, tex->glname );
+ }
+
+ mdl_draw_submesh( sm );
+ }
+ }
+}
+
+void cutscene_update( f32 delta )
+{
+ _cutscene.time += delta;
+ u32 frame = _cutscene.time * _cutscene.meta.info.framerate;
+
+ /* clear out finished samplers */
+ bool move = 0;
+ u32 j = 0;
+ for( u32 i=0; i<_cutscene.active_samplers; i ++ )
+ {
+ struct cs_sampler *si = &_cutscene.samplers[i];
+
+ if( frame > (si->strip->offset + si->strip->length) )
+ {
+ move = 1;
+ }
+ else
+ {
+ if( move )
+ {
+ struct cs_sampler *sj = &_cutscene.samplers[j];
+ *sj = *si;
+ }
+
+ j ++;
+ }
+ }
+ _cutscene.active_samplers = j;
+
+ /* add new samplers as we get to them */
+ for( u32 i=_cutscene.strip; i<af_arrcount(&_cutscene.meta.strips); i ++ )
+ {
+ ms_strip *strip = af_arritm(&_cutscene.meta.strips, i);
+
+ if( frame < strip->offset )
+ {
+ break;
+ }
+
+ if( frame > strip->offset + strip->length )
+ {
+ vg_warn( "Skipping?\n" );
+ _cutscene.strip ++;
+ continue;
+ }
+
+ if( strip->instance_id == 0xffffffff )
+ {
+ // TODO
+ _cutscene.strip ++;
+ continue;
+ }
+
+ struct cs_instance *ins = &_cutscene.instances[ strip->instance_id ];
+
+ struct cs_asoc asoc;
+ _cutscene_get_strip_asoc( strip, &asoc );
+ VG_ASSERT( asoc.entity_type == 28 );
+
+ if( strip->data_mode == 1 )
+ {
+ for( u32 j=0; j<strip->data_count; j ++ )
+ {
+ ms_track *track = af_arritm( &_cutscene.meta.tracks,
+ strip->data_start + j );
+
+ VG_ASSERT( _cutscene.active_samplers <
+ VG_ARRAY_LEN(_cutscene.samplers) );
+
+ struct cs_sampler *samp =
+ &_cutscene.samplers[ _cutscene.active_samplers ++ ];
+ samp->strip = strip;
+ samp->curves.track = track;
+ samp->curves.target = NULL; /* DOTO */
+ samp->override = asoc.override;
+ }
+ }
+ else
+ {
+ VG_ASSERT( _cutscene.active_samplers <
+ VG_ARRAY_LEN(_cutscene.samplers) );
+
+ struct cs_sampler *samp =
+ &_cutscene.samplers[ _cutscene.active_samplers ++ ];
+
+ struct model_ref *ref = &_cutscene.refs[ ins->ref_id ];
+ struct cs_skeleton *skele = &ref->skeletons[ asoc.entity_index ];
+
+ samp->strip = strip;
+ samp->skeleton.skinning_data =
+ &ins->skinning_data[ skele->skinning_offset ];
+ samp->skeleton.ref_sk = &skele->sk;
+ samp->override = asoc.override;
+ }
+
+ _cutscene.strip ++;
+ }
+
+ /* sample da samplers */
+ for( u32 i=0; i<_cutscene.active_samplers; i ++ )
+ {
+ struct cs_sampler *samp = &_cutscene.samplers[ i ];
+
+ struct skeleton_anim temp_anim =
+ {
+ .strip = samp->strip,
+ .framerate = _cutscene.meta.info.framerate,
+ .keyframes_base = af_arritm( &_cutscene.meta.keyframes,
+ samp->strip->data_start )
+ };
+
+ f32 t = _cutscene.time;
+ t -= (f32)samp->strip->offset / _cutscene.meta.info.framerate;
+
+ struct skeleton *ref_sk = samp->skeleton.ref_sk;
+ m4x3f *final_mtx = samp->skeleton.skinning_data;
+
+ ms_keyframe pose[32];
+ skeleton_sample_anim( ref_sk, &temp_anim, t, pose );
+
+ skeleton_apply_pose( ref_sk, pose,
+ k_anim_apply_defer_ik, final_mtx );
+ skeleton_apply_ik_pass( ref_sk, final_mtx );
+ skeleton_apply_pose( ref_sk, pose,
+ k_anim_apply_deffered_only, final_mtx );
+ skeleton_apply_inverses( ref_sk, final_mtx );
+
+ if( samp->override )
+ {
+ m4x3f mmdl;
+ mdl_transform_m4x3( &samp->override->transform, mmdl );
+ skeleton_apply_transform( ref_sk, mmdl, final_mtx );
+ }
+ }
+}
+
+void cutscene_render( world_instance *world, vg_camera *cam )
+{
+ if( _cutscene.ready )
+ {
+ for( u32 i=0; i<_cutscene.instance_count; i ++ )
+ {
+ cutscene_render_instance( &_cutscene.instances[i], world, cam );
+ }
+ }
+}