+
+static void mdl_load_fatal_corrupt( mdl_context *mdl )
+{
+ fclose( mdl->file );
+ vg_file_print_invalid( mdl->file );
+ vg_fatal_error( "Corrupt model" );
+}
+
+/*
+ * Model implementation
+ */
+
+static const char *mdl_pstr( mdl_context *mdl, u32 pstr );
+static
+void mdl_fread_pack_file( mdl_context *mdl, mdl_file *info, void *dst )
+{
+ if( !info->pack_size ){
+ vg_warn( "path: %s\n", mdl_pstr( mdl, info->pstr_path ) );
+ vg_fatal_error( "Packed file is only a header; it is not packed" );
+ }
+
+ fseek( mdl->file, mdl->pack_base_offset+info->pack_offset, SEEK_SET );
+ u64 l = fread( dst, info->pack_size, 1, mdl->file );
+
+ if( l != 1 ) mdl_load_fatal_corrupt( mdl );
+}
+
+/* TODO: Rename these */
+static void mdl_load_array_file_buffer( mdl_context *mdl, mdl_array *arr,
+ void *buffer, u32 stride )
+{
+ if( arr->item_count ){
+ fseek( mdl->file, arr->file_offset, SEEK_SET );
+
+ if( stride == arr->item_size ){
+ u64 l = fread( buffer, arr->item_size*arr->item_count, 1, mdl->file );
+ if( l != 1 ) mdl_load_fatal_corrupt( mdl );
+ }
+ else {
+ vg_warn( "Applying alignment fixup to array @%p [%u -> %u] x %u\n",
+ buffer, arr->item_size, stride, arr->item_count );
+ if( stride < arr->item_size )
+ vg_fatal_error( "not safe\n" );
+
+ for( u32 i=0; i<arr->item_count; i++ ){
+ u64 l = fread( buffer+i*stride, arr->item_size, 1, mdl->file );
+ if( l != 1 ) mdl_load_fatal_corrupt( mdl );
+ }
+ }
+ }
+}
+
+static void mdl_load_array_file( mdl_context *mdl, mdl_array_ptr *ptr,
+ mdl_array *arr, void *lin_alloc, u32 stride )
+{
+ if( stride < arr->item_size ){
+ vg_error( "Structure max: %u. Got: %u\n", stride, arr->item_size );
+ vg_fatal_error( "not safe\n" );
+ }
+
+ if( arr->item_count ){
+ u32 size = stride*arr->item_count;
+ ptr->data = vg_linear_alloc( lin_alloc, vg_align8(size) );
+ mdl_load_array_file_buffer( mdl, arr, ptr->data, stride );
+ }
+ else{
+ ptr->data = NULL;
+ }
+
+ ptr->stride = stride;
+ ptr->count = arr->item_count;
+}
+
+static void *mdl_arritm( mdl_array_ptr *arr, u32 index )
+{
+ return ((u8 *)arr->data) + index*arr->stride;
+}
+
+static u32 mdl_arrcount( mdl_array_ptr *arr )
+{
+ return arr->count;
+}
+
+static mdl_array *mdl_find_array( mdl_context *mdl, const char *name )
+{
+ for( u32 i=0; i<mdl_arrcount(&mdl->index); i++ ){
+ mdl_array *arr = mdl_arritm( &mdl->index, i );
+
+ if( !strncmp(arr->name,name,16) ){
+ return arr;
+ }
+ }
+
+ return NULL;
+}
+
+static int _mdl_load_array( mdl_context *mdl, mdl_array_ptr *ptr,
+ const char *name, void *lin_alloc, u32 stride )
+{
+ mdl_array *arr = mdl_find_array( mdl, name );
+
+ if( arr ){
+ mdl_load_array_file( mdl, ptr, arr, lin_alloc, stride );
+ return 1;
+ }
+ else{
+ ptr->data = NULL;
+ ptr->count = 0;
+ ptr->stride = 0;
+ return 0;
+ }
+}
+
+#define MDL_LOAD_ARRAY( MDL, PTR, STRUCT, ALLOCATOR ) \
+ _mdl_load_array( MDL, PTR, #STRUCT, ALLOCATOR, sizeof(STRUCT) )
+
+static int mdl_load_mesh_block( mdl_context *mdl, void *lin_alloc ){
+ int success = 1;
+
+ success &= MDL_LOAD_ARRAY( mdl, &mdl->verts, mdl_vert, lin_alloc );
+ success &= MDL_LOAD_ARRAY( mdl, &mdl->indices, mdl_indice, lin_alloc );
+
+ return success;
+}
+
+static int mdl_load_metadata_block( mdl_context *mdl, void *lin_alloc ){
+ int success = 1;
+
+ success &= _mdl_load_array( mdl, &mdl->strings, "strings", lin_alloc, 1 );
+ success &= MDL_LOAD_ARRAY( mdl, &mdl->meshs, mdl_mesh, lin_alloc );
+ success &= MDL_LOAD_ARRAY( mdl, &mdl->submeshs, mdl_submesh, lin_alloc );
+ success &= MDL_LOAD_ARRAY( mdl, &mdl->materials, mdl_material, lin_alloc );
+ success &= MDL_LOAD_ARRAY( mdl, &mdl->textures, mdl_texture, lin_alloc );
+ success &= MDL_LOAD_ARRAY( mdl, &mdl->armatures, mdl_armature, lin_alloc );
+ success &= MDL_LOAD_ARRAY( mdl, &mdl->bones, mdl_bone, lin_alloc );
+ success &= MDL_LOAD_ARRAY( mdl, &mdl->animations,mdl_animation,lin_alloc );
+
+ return success;
+}
+
+static int mdl_load_animation_block( mdl_context *mdl, void *lin_alloc ){
+ return MDL_LOAD_ARRAY( mdl, &mdl->keyframes, mdl_keyframe, lin_alloc );
+}
+
+/*
+ * if calling mdl_open, and the file does not exist, the game will fatal quit
+ */
+static void mdl_open( mdl_context *mdl, const char *path, void *lin_alloc )
+{
+ memset( mdl, 0, sizeof( mdl_context ) );
+ mdl->file = fopen( path, "rb" );
+
+ if( !mdl->file ){
+ vg_error( "mdl_open('%s'): %s\n", path, strerror(errno) );
+ vg_fatal_error( "see above for details" );
+ }
+
+ u64 l = fread( &mdl->info, sizeof(mdl_header), 1, mdl->file );
+ if( l != 1 )
+ mdl_load_fatal_corrupt( mdl );
+
+ if( mdl->info.version < MDL_VERSION_MIN ){
+ vg_warn( "For model: %s\n", path );
+ vg_warn( " version: %u (min: %u, current: %u)\n",
+ mdl->info.version, MDL_VERSION_MIN, MDL_VERSION_NR );
+
+ vg_fatal_error( "Legacy model version incompatable" );
+ }
+
+ mdl_load_array_file( mdl, &mdl->index, &mdl->info.index, lin_alloc,
+ sizeof(mdl_array) );
+
+ mdl_array *pack = mdl_find_array( mdl, "pack" );
+ if( pack ) mdl->pack_base_offset = pack->file_offset;
+ else mdl->pack_base_offset = 0;
+}
+
+/*
+ * close file handle
+ */
+static void mdl_close( mdl_context *mdl )
+{
+ fclose( mdl->file );
+ mdl->file = NULL;
+}
+
+/* useful things you can do with the model */
+
+static void mdl_transform_m4x3( mdl_transform *transform, m4x3f mtx )
+{
+ 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] );
+}
+
+static const char *mdl_pstr( mdl_context *mdl, u32 pstr )
+{
+ return ((char *)mdl_arritm( &mdl->strings, pstr )) + 4;
+}
+
+
+static int
+mdl_pstreq( mdl_context *mdl, u32 pstr, const char *str, u32 djb2 )
+{
+ u32 hash = *((u32 *)mdl_arritm( &mdl->strings, pstr ));
+ if( hash == djb2 ){
+ if( !strcmp( str, mdl_pstr( mdl, pstr ))) return 1;
+ else return 0;
+ }
+ else return 0;
+}
+
+#define MDL_CONST_PSTREQ( MDL, Q, CONSTSTR )\
+ mdl_pstreq( MDL, Q, CONSTSTR, vg_strdjb2( CONSTSTR ) )
+
+/*
+ * Simple mesh interface for OpenGL
+ * ----------------------------------------------------------------------------
+ */