+
+VG_STATIC void ent_ccmd_call( world_instance *world, ent_call *call ){
+ if( call->function == k_ent_function_trigger ){
+ u32 index = mdl_entity_id_id( call->id );
+ ent_ccmd *ccmd = mdl_arritm( &world->ent_ccmd, index );
+ vg_execute_console_input( mdl_pstr(&world->meta, ccmd->pstr_command) );
+ }
+}
+
+/*
+ * BVH implementation
+ * ----------------------------------------------------------------------------
+ */
+
+VG_STATIC void
+entity_bh_expand_bound( void *user, boxf bound, u32 item_index ){
+ world_instance *world = user;
+
+ u32 id = world->entity_list[ item_index ],
+ type = mdl_entity_id_type( id ),
+ index = mdl_entity_id_id( id );
+
+ if( type == k_ent_gate ){
+ ent_gate *gate = mdl_arritm( &world->ent_gate, index );
+ boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
+ { gate->dimensions[0], gate->dimensions[1], 0.1f }};
+
+ m4x3_expand_aabb_aabb( gate->to_world, bound, box );
+ }
+ else if( type == k_ent_challenge ){
+ ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
+
+ /* TODO: This might be more work than necessary. could maybe just get
+ * away with representing them as points */
+
+ boxf box;
+ box_init_inf( box );
+
+ for( u32 i=0; i<challenge->submesh_count; i++ ){
+ mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
+ challenge->submesh_start+i );
+ box_concat( box, sm->bbx );
+ }
+
+ m4x3f transform;
+ mdl_transform_m4x3( &challenge->transform, transform );
+ m4x3_expand_aabb_aabb( transform, bound, box );
+ }
+ else if( type == k_ent_volume ){
+ ent_volume *volume = mdl_arritm( &world->ent_volume, index );
+ m4x3_expand_aabb_aabb( volume->to_world, bound,
+ (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}} );
+ }
+}
+
+VG_STATIC float entity_bh_centroid( void *user, u32 item_index, int axis ){
+ world_instance *world = user;
+
+ u32 id = world->entity_list[ item_index ],
+ type = mdl_entity_id_type( id ),
+ index = mdl_entity_id_id( id );
+
+ if( type == k_ent_gate ){
+ ent_gate *gate = mdl_arritm( &world->ent_gate, index );
+ return gate->to_world[3][axis];
+ }
+ else if( type == k_ent_challenge ){
+ ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
+ return challenge->transform.co[axis];
+ }
+ else if( type == k_ent_volume ){
+ ent_volume *volume = mdl_arritm( &world->ent_volume, index );
+ return volume->transform.co[axis];
+ }
+ else {
+ vg_fatal_error( "Programming error\n" );
+ return INFINITY;
+ }
+}
+
+VG_STATIC void entity_bh_swap( void *user, u32 ia, u32 ib ){
+ world_instance *world = user;
+
+ u32 a = world->entity_list[ ia ],
+ b = world->entity_list[ ib ];
+
+ world->entity_list[ ia ] = b;
+ world->entity_list[ ib ] = a;
+}
+
+VG_STATIC void entity_bh_debug( void *user, u32 item_index ){
+ world_instance *world = user;
+
+ u32 id = world->entity_list[ item_index ],
+ type = mdl_entity_id_type( id ),
+ index = mdl_entity_id_id( id );
+
+ if( type == k_ent_gate ){
+ ent_gate *gate = mdl_arritm( &world->ent_gate, index );
+ boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
+ { gate->dimensions[0], gate->dimensions[1], 0.1f }};
+ vg_line_boxf_transformed( gate->to_world, box, 0xf000ff00 );
+ }
+ else if( type == k_ent_challenge ){
+ ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
+ boxf box;
+ box_init_inf( box );
+
+ for( u32 i=0; i<challenge->submesh_count; i++ ){
+ mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
+ challenge->submesh_start+i );
+ box_concat( box, sm->bbx );
+ }
+
+ m4x3f transform;
+ mdl_transform_m4x3( &challenge->transform, transform );
+ vg_line_boxf_transformed( transform, box, 0xf000ff00 );
+ }
+ else if( type == k_ent_volume ){
+ ent_volume *volume = mdl_arritm( &world->ent_volume, index );
+ vg_line_boxf_transformed( volume->to_world,
+ (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}},
+ 0xf000ff00 );
+ }
+ else{
+ vg_fatal_error( "Programming error\n" );
+ }
+}
+
+VG_STATIC void entity_bh_closest( void *user, u32 item_index, v3f point,
+ v3f closest ){
+ world_instance *world = user;
+
+ u32 id = world->entity_list[ item_index ],
+ type = mdl_entity_id_type( id ),
+ index = mdl_entity_id_id( id );
+
+ if( type == k_ent_gate ){
+ ent_gate *gate = mdl_arritm( &world->ent_gate, index );
+ v3_copy( gate->to_world[3], closest );
+ }
+ else if( type == k_ent_challenge ){
+ ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
+ v3_copy( challenge->transform.co, closest );
+ }
+ else if( type == k_ent_volume ){
+ ent_volume *volume = mdl_arritm( &world->ent_volume, index );
+ v3_copy( volume->to_world[3], closest );
+ }
+ else{
+ vg_fatal_error( "Programming error\n" );
+ }
+}
+
+VG_STATIC void world_entity_start( world_instance *world, vg_msg *sav ){
+ vg_info( "Start instance %p\n", world );
+
+ world->probabilities[ k_probability_curve_constant ] = 1.0f;
+ for( u32 i=0; i<mdl_arrcount(&world->ent_audio); i++ ){
+ ent_audio *audio = mdl_arritm(&world->ent_audio,i);
+ if( audio->flags & AUDIO_FLAG_AUTO_START ){
+ ent_call call;
+ call.data = NULL;
+ call.function = k_ent_function_trigger;
+ call.id = mdl_entity_id( k_ent_audio, i );
+ entity_call( world, &call );
+ }
+ }
+
+ /* read savedata
+ * ----------------------------------------------------------------------- */
+
+ for( u32 i=0; i<mdl_arrcount(&world->ent_unlock); i++ ){
+ ent_unlock *unlock = mdl_arritm( &world->ent_unlock, i );
+ const char *alias = mdl_pstr( &world->meta, unlock->pstr_alias );
+
+ if( vg_msg_seekkvu32( sav, alias, k_vg_msg_first ) ){
+ ent_call call;
+ call.data = NULL;
+ call.function = 0;
+ call.id = mdl_entity_id( k_ent_unlock, i );
+ entity_call( world, &call );
+ }
+ }
+}
+
+VG_STATIC void world_entity_serialize( world_instance *world, vg_msg *sav ){
+ for( u32 i=0; i<mdl_arrcount(&world->ent_unlock); i++ ){
+ ent_unlock *unlock = mdl_arritm(&world->ent_unlock,i);
+
+ const char *alias = mdl_pstr(&world->meta,unlock->pstr_alias);
+ vg_msg_wkvu32( sav, alias, unlock->status );
+ }
+}
+