+#include "vg/vg_mem.h"
+#include "ent_npc.h"
+#include "shaders/model_character_view.h"
+
+struct npc npc_gumpa;
+static struct skeleton_anim *gumpa_idle;
+
+void npc_load_model( struct npc *npc, const char *path )
+{
+ vg_linear_clear( vg_mem.scratch );
+
+ mdl_context *meta = &npc->meta;
+ mdl_open( meta, path, vg_mem.rtmemory );
+ mdl_load_metadata_block( meta, vg_mem.rtmemory );
+ mdl_load_animation_block( meta, vg_mem.rtmemory );
+
+ struct skeleton *sk = &npc->skeleton;
+ skeleton_setup( sk, vg_mem.rtmemory, meta );
+
+ u32 mtx_size = sizeof(m4x3f)*sk->bone_count;
+ npc->final_mtx = vg_linear_alloc( vg_mem.rtmemory, mtx_size );
+
+ if( !mdl_arrcount( &meta->textures ) )
+ vg_fatal_error( "No texture in model" );
+
+ mdl_texture *tex0 = mdl_arritm( &meta->textures, 0 );
+ void *data = vg_linear_alloc( vg_mem.scratch, tex0->file.pack_size );
+ mdl_fread_pack_file( meta, &tex0->file, data );
+
+ vg_tex2d_load_qoi_async( data, tex0->file.pack_size,
+ VG_TEX2D_NEAREST|VG_TEX2D_CLAMP,
+ &npc->texture );
+
+ mdl_async_load_glmesh( meta, &npc->mesh, NULL );
+ mdl_close( meta );
+}
+
+void npc_init(void)
+{
+ npc_load_model( &npc_gumpa, "models/gumpa.mdl" );
+ gumpa_idle = skeleton_get_anim( &npc_gumpa.skeleton, "gumpa_idle" );
+}
+
+static struct npc *npc_resolve( u32 id )
+{
+ if( id == 1 ) return &npc_gumpa;
+ else return NULL;
+}
+
+void npc_update( ent_npc *ent )
+{
+ struct npc *npc_def = npc_resolve( ent->id );
+ VG_ASSERT( npc_def );
+
+ player_pose pose;
+ struct skeleton *sk = &npc_def->skeleton;
+ pose.type = k_player_pose_type_ik;
+ pose.board.lean = 0.0f;
+
+ skeleton_sample_anim( sk, gumpa_idle, vg.time, pose.keyframes );
+
+ v3_copy( ent->transform.co, pose.root_co );
+ v4_copy( ent->transform.q, pose.root_q );
+ apply_full_skeleton_pose( &npc_def->skeleton, &pose, npc_def->final_mtx );
+}
+
+void npc_render( ent_npc *ent, world_instance *world, vg_camera *cam )
+{
+ struct npc *npc_def = npc_resolve( ent->id );
+ VG_ASSERT( npc_def );
+
+ shader_model_character_view_use();
+
+ glActiveTexture( GL_TEXTURE0 );
+ glBindTexture( GL_TEXTURE_2D, npc_def->texture );
+ shader_model_character_view_uTexMain( 0 );
+ shader_model_character_view_uCamera( cam->transform[3] );
+ shader_model_character_view_uPv( cam->mtx.pv );
+ shader_model_character_view_uDepthCompare( 0 );
+
+ WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world, model_character_view );
+
+ glUniformMatrix4x3fv( _uniform_model_character_view_uTransforms,
+ npc_def->skeleton.bone_count,
+ 0,
+ (const GLfloat *)npc_def->final_mtx );
+
+ mesh_bind( &npc_def->mesh );
+ mesh_draw( &npc_def->mesh );
+}