2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
10 #define MDL_VERSION_NR 100
14 k_shader_standard
= 0,
15 k_shader_standard_cutout
= 1,
16 k_shader_terrain_blend
= 2,
17 k_shader_standard_vertex_blend
= 3,
19 k_shader_invisible
= 5,
25 k_surface_prop_concrete
= 0,
26 k_surface_prop_wood
= 1,
27 k_surface_prop_grass
= 2,
28 k_surface_prop_tiles
= 3,
29 k_surface_prop_metal
= 4
34 k_material_flag_skate_target
= 0x00000001,
35 k_material_flag_collision
= 0x00000002,
36 k_material_flag_grow_grass
= 0x00000004,
37 k_material_flag_grindable
= 0x00000008,
38 k_material_flag_invisible
= 0x00000010,
39 k_material_flag_boundary
= 0x00000020
51 u8 colour
[4]; /* 4*8 */
52 u16 weights
[4];/* 4*16 */
53 u8 groups
[4]; /* 4*8 */
58 typedef struct mdl_context mdl_context
;
59 typedef struct mdl_array_ptr mdl_array_ptr
;
60 typedef struct mdl_vert mdl_vert
;
61 typedef struct mdl_transform mdl_transform
;
62 typedef struct mdl_submesh mdl_submesh
;
63 typedef struct mdl_material mdl_material
;
64 typedef struct mdl_bone mdl_bone
;
65 typedef struct mdl_armature mdl_armature
;
66 typedef struct mdl_animation mdl_animation
;
67 typedef struct mdl_transform mdl_keyframe
;
68 typedef struct mdl_mesh mdl_mesh
;
69 typedef struct mdl_file mdl_file
;
70 typedef struct mdl_texture mdl_texture
;
71 typedef struct mdl_array mdl_array
;
72 typedef struct mdl_header mdl_header
;
106 v3f conevx
, conevy
, coneva
;
112 k_bone_flag_deform
= 0x00000001,
113 k_bone_flag_ik
= 0x00000002,
114 k_bone_flag_cone_constraint
= 0x00000004
119 k_bone_collider_none
= 0,
120 k_bone_collider_box
= 1,
121 k_bone_collider_capsule
= 2
126 mdl_transform transform
;
149 u16 material_id
, flags
;
154 k_submesh_flag_none
= 0x0000,
155 k_submesh_flag_consumed
= 0x0001
160 mdl_transform transform
;
164 entity_id
, /* upper 16 bits: type, lower 16 bits: index */
218 /* animation buffers */
225 u32 pack_base_offset
;
232 VG_STATIC
void mdl_load_fatal_corrupt( mdl_context
*mdl
)
235 vg_file_print_invalid( mdl
->file
);
236 vg_fatal_error( "Corrupt model" );
240 * Model implementation
243 VG_STATIC u32
mdl_query_array_size( mdl_array
*arr
)
245 if( arr
->item_count
){
246 u32 size
= arr
->item_size
*arr
->item_count
;
247 return vg_align8(size
);
253 VG_STATIC
const char *mdl_pstr( mdl_context
*mdl
, u32 pstr
);
255 void mdl_fread_pack_file( mdl_context
*mdl
, mdl_file
*info
, void *dst
)
257 if( !info
->pack_size
){
258 vg_warn( "path: %s\n", mdl_pstr( mdl
, info
->pstr_path
) );
259 vg_fatal_error( "Packed file is only a header; it is not packed" );
262 fseek( mdl
->file
, mdl
->pack_base_offset
+info
->pack_offset
, SEEK_SET
);
263 u64 l
= fread( dst
, info
->pack_size
, 1, mdl
->file
);
266 mdl_load_fatal_corrupt( mdl
);
269 /* TODO: Rename these */
271 void mdl_load_array_file_buffer( mdl_context
*mdl
, mdl_array
*arr
,
274 if( arr
->item_count
){
275 fseek( mdl
->file
, arr
->file_offset
, SEEK_SET
);
276 u64 l
= fread( buffer
, arr
->item_size
*arr
->item_count
, 1, mdl
->file
);
279 mdl_load_fatal_corrupt( mdl
);
283 VG_STATIC
void mdl_load_array_file( mdl_context
*mdl
, mdl_array_ptr
*ptr
,
284 mdl_array
*arr
, void *lin_alloc
)
286 if( arr
->item_count
){
287 u32 size
= arr
->item_size
*arr
->item_count
;
288 ptr
->data
= vg_linear_alloc( lin_alloc
, vg_align8(size
) );
289 mdl_load_array_file_buffer( mdl
, arr
, ptr
->data
);
294 ptr
->count
= arr
->item_count
;
295 ptr
->stride
= arr
->item_size
;
298 VG_STATIC
void *mdl_arritm( mdl_array_ptr
*arr
, u32 index
)
300 return ((u8
*)arr
->data
) + index
*arr
->stride
;
303 VG_STATIC u32
mdl_arrcount( mdl_array_ptr
*arr
)
308 VG_STATIC mdl_array
*mdl_find_array( mdl_context
*mdl
, const char *name
)
310 for( u32 i
=0; i
<mdl_arrcount(&mdl
->index
); i
++ ){
311 mdl_array
*arr
= mdl_arritm( &mdl
->index
, i
);
313 if( !strncmp(arr
->name
,name
,16) ){
321 VG_STATIC
int mdl_load_array( mdl_context
*mdl
, mdl_array_ptr
*ptr
,
322 const char *name
, void *lin_alloc
)
324 mdl_array
*arr
= mdl_find_array( mdl
, name
);
327 mdl_load_array_file( mdl
, ptr
, arr
, lin_alloc
);
338 VG_STATIC
int mdl_load_mesh_block( mdl_context
*mdl
, void *lin_alloc
)
342 success
&= mdl_load_array( mdl
, &mdl
->verts
, "mdl_vert", lin_alloc
);
343 success
&= mdl_load_array( mdl
, &mdl
->indices
, "mdl_indice", lin_alloc
);
348 VG_STATIC
int mdl_load_metadata_block( mdl_context
*mdl
, void *lin_alloc
)
352 success
&= mdl_load_array( mdl
, &mdl
->strings
, "strings", lin_alloc
);
353 success
&= mdl_load_array( mdl
, &mdl
->meshs
, "mdl_mesh", lin_alloc
);
354 success
&= mdl_load_array( mdl
, &mdl
->submeshs
, "mdl_submesh", lin_alloc
);
355 success
&= mdl_load_array( mdl
, &mdl
->materials
, "mdl_material", lin_alloc
);
356 success
&= mdl_load_array( mdl
, &mdl
->textures
, "mdl_texture", lin_alloc
);
357 success
&= mdl_load_array( mdl
, &mdl
->armatures
, "mdl_armature", lin_alloc
);
358 success
&= mdl_load_array( mdl
, &mdl
->bones
, "mdl_bone", lin_alloc
);
359 success
&= mdl_load_array( mdl
, &mdl
->animations
,"mdl_animation",lin_alloc
);
364 VG_STATIC
int mdl_load_animation_block( mdl_context
*mdl
, void *lin_alloc
)
366 return mdl_load_array( mdl
, &mdl
->keyframes
, "mdl_keyframe", lin_alloc
);
370 * if calling mdl_open, and the file does not exist, the game will fatal quit
372 VG_STATIC
void mdl_open( mdl_context
*mdl
, const char *path
, void *lin_alloc
)
374 memset( mdl
, 0, sizeof( mdl_context
) );
375 mdl
->file
= fopen( path
, "rb" );
378 vg_error( "mdl_open('%s'): %s\n", path
, strerror(errno
) );
379 vg_fatal_error( "see above for details" );
382 u64 l
= fread( &mdl
->info
, sizeof(mdl_header
), 1, mdl
->file
);
384 mdl_load_fatal_corrupt( mdl
);
386 if( mdl
->info
.version
!= MDL_VERSION_NR
){
387 vg_warn( "For model: %s\n", path
);
388 vg_warn( " version: %u (current: %u)\n", mdl
->info
.version
,
391 vg_fatal_error( "Legacy model version incompatable" );
394 mdl_load_array_file( mdl
, &mdl
->index
, &mdl
->info
.index
, lin_alloc
);
396 mdl_array
*pack
= mdl_find_array( mdl
, "pack" );
397 if( pack
) mdl
->pack_base_offset
= pack
->file_offset
;
398 else mdl
->pack_base_offset
= 0;
404 VG_STATIC
void mdl_close( mdl_context
*mdl
)
410 /* useful things you can do with the model */
412 VG_STATIC
void mdl_transform_m4x3( mdl_transform
*transform
, m4x3f mtx
)
414 q_m3x3( transform
->q
, mtx
);
415 v3_muls( mtx
[0], transform
->s
[0], mtx
[0] );
416 v3_muls( mtx
[1], transform
->s
[1], mtx
[1] );
417 v3_muls( mtx
[2], transform
->s
[2], mtx
[2] );
418 v3_copy( transform
->co
, mtx
[3] );
421 VG_STATIC
const char *mdl_pstr( mdl_context
*mdl
, u32 pstr
)
423 return mdl_arritm( &mdl
->strings
, pstr
);
427 * Simple mesh interface for OpenGL
428 * ----------------------------------------------------------------------------
431 typedef struct glmesh glmesh
;
434 GLuint vao
, vbo
, ebo
;
439 VG_STATIC
void mesh_upload( glmesh
*mesh
,
440 mdl_vert
*verts
, u32 vert_count
,
441 u32
*indices
, u32 indice_count
)
443 //assert( mesh->loaded == 0 );
445 glGenVertexArrays( 1, &mesh
->vao
);
446 glGenBuffers( 1, &mesh
->vbo
);
447 glGenBuffers( 1, &mesh
->ebo
);
448 glBindVertexArray( mesh
->vao
);
450 size_t stride
= sizeof(mdl_vert
);
452 glBindBuffer( GL_ARRAY_BUFFER
, mesh
->vbo
);
453 glBufferData( GL_ARRAY_BUFFER
, vert_count
*stride
, verts
, GL_STATIC_DRAW
);
455 glBindVertexArray( mesh
->vao
);
456 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, mesh
->ebo
);
457 glBufferData( GL_ELEMENT_ARRAY_BUFFER
, indice_count
*sizeof(u32
),
458 indices
, GL_STATIC_DRAW
);
461 glVertexAttribPointer( 0, 3, GL_FLOAT
, GL_FALSE
, stride
, (void*)0 );
462 glEnableVertexAttribArray( 0 );
465 glVertexAttribPointer( 1, 3, GL_FLOAT
, GL_FALSE
,
466 stride
, (void *)offsetof(mdl_vert
, norm
) );
467 glEnableVertexAttribArray( 1 );
470 glVertexAttribPointer( 2, 2, GL_FLOAT
, GL_FALSE
,
471 stride
, (void *)offsetof(mdl_vert
, uv
) );
472 glEnableVertexAttribArray( 2 );
475 glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE
, GL_TRUE
,
476 stride
, (void *)offsetof(mdl_vert
, colour
) );
477 glEnableVertexAttribArray( 3 );
480 glVertexAttribPointer( 4, 4, GL_UNSIGNED_SHORT
, GL_TRUE
,
481 stride
, (void *)offsetof(mdl_vert
, weights
) );
482 glEnableVertexAttribArray( 4 );
485 glVertexAttribIPointer( 5, 4, GL_UNSIGNED_BYTE
,
486 stride
, (void *)offsetof(mdl_vert
, groups
) );
487 glEnableVertexAttribArray( 5 );
491 mesh
->indice_count
= indice_count
;
495 VG_STATIC
void mesh_bind( glmesh
*mesh
)
497 glBindVertexArray( mesh
->vao
);
500 VG_STATIC
void mesh_drawn( u32 start
, u32 count
)
502 glDrawElements( GL_TRIANGLES
, count
, GL_UNSIGNED_INT
,
503 (void *)(start
*sizeof(u32
)) );
506 VG_STATIC
void mesh_draw( glmesh
*mesh
)
508 mesh_drawn( 0, mesh
->indice_count
);
511 VG_STATIC
void mesh_free( glmesh
*mesh
)
514 glDeleteVertexArrays( 1, &mesh
->vao
);
515 glDeleteBuffers( 1, &mesh
->ebo
);
516 glDeleteBuffers( 1, &mesh
->vbo
);
521 VG_STATIC
void mdl_draw_submesh( mdl_submesh
*sm
)
523 mesh_drawn( sm
->indice_start
, sm
->indice_count
);
526 VG_STATIC mdl_mesh
*mdl_find_mesh( mdl_context
*mdl
, const char *name
)
528 for( u32 i
=0; i
<mdl_arrcount( &mdl
->meshs
); i
++ ){
529 mdl_mesh
*mesh
= mdl_arritm( &mdl
->meshs
, i
);
530 if( !strcmp( name
, mdl_pstr( mdl
, mesh
->pstr_name
))){
537 struct payload_glmesh_load
{
547 VG_STATIC
void async_mdl_load_glmesh( void *payload
, u32 size
)
549 struct payload_glmesh_load
*job
= payload
;
551 mesh_upload( job
->mesh
, job
->verts
, job
->vertex_count
,
552 job
->indices
, job
->indice_count
);
555 VG_STATIC
void mdl_async_load_glmesh( mdl_context
*mdl
, glmesh
*mesh
)
557 mdl_array
*arr_vertices
= mdl_find_array( mdl
, "mdl_vert" );
558 mdl_array
*arr_indices
= mdl_find_array( mdl
, "mdl_indice" );
560 if( arr_vertices
&& arr_indices
){
561 u32 size_verts
= vg_align8(mdl_query_array_size( arr_vertices
)),
562 size_indices
= vg_align8(mdl_query_array_size( arr_indices
)),
563 size_hdr
= vg_align8(sizeof(struct payload_glmesh_load
)),
564 total
= size_hdr
+ size_verts
+ size_indices
;
566 vg_async_item
*call
= vg_async_alloc( total
);
567 struct payload_glmesh_load
*job
= call
->payload
;
569 u8
*payload
= call
->payload
;
572 job
->verts
= (void*)(payload
+ size_hdr
);
573 job
->indices
= (void*)(payload
+ size_hdr
+ size_verts
);
574 job
->vertex_count
= arr_vertices
->item_count
;
575 job
->indice_count
= arr_indices
->item_count
;
577 mdl_load_array_file_buffer( mdl
, arr_vertices
, job
->verts
);
578 mdl_load_array_file_buffer( mdl
, arr_indices
, job
->indices
);
581 * Unpack the indices (if there are meshes)
582 * ---------------------------------------------------------
585 if( mdl_arrcount( &mdl
->submeshs
) ){
586 mdl_submesh
*sm
= mdl_arritm( &mdl
->submeshs
, 0 );
587 u32 offset
= sm
->vertex_count
;
589 for( u32 i
=1; i
<mdl_arrcount( &mdl
->submeshs
); i
++ ){
590 mdl_submesh
*sm
= mdl_arritm( &mdl
->submeshs
, i
);
591 u32
*indices
= job
->indices
+ sm
->indice_start
;
593 for( u32 j
=0; j
<sm
->indice_count
; j
++ )
594 indices
[j
] += offset
;
596 offset
+= sm
->vertex_count
;
602 * -------------------------
605 vg_async_dispatch( call
, async_mdl_load_glmesh
);
608 vg_fatal_error( "no vertex/indice data\n" );