2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
10 typedef struct glmesh glmesh
;
12 typedef struct mdl_vert mdl_vert
;
13 typedef struct mdl_submesh mdl_submesh
;
14 typedef struct mdl_material mdl_material
;
15 typedef struct mdl_node mdl_node
;
16 typedef struct mdl_file_header mdl_file_header
;
17 typedef struct mdl_animation mdl_animation
;
18 typedef struct mdl_keyframe mdl_keyframe
;
19 typedef struct mdl_context mdl_context
;
21 #define MDL_SIZE_MAX 0x1000000
22 #define MDL_VERT_MAX 1000000
23 #define MDL_INDICE_MAX 1000000
24 #define MDL_MATERIAL_MAX 32
25 #define MDL_NODE_MAX 4000
26 #define MDL_SUBMESH_MAX 8000
27 #define MDL_STRING_LENGTH_MAX 64
33 k_classtype_block
= 2,
34 k_classtype_spawn
= 3,
35 k_classtype_water
= 4,
36 k_classtype_car_path
= 5,
37 k_classtype_instance
= 6,
38 k_classtype_capsule
= 7,
39 k_classtype_route_node
= 8,
40 k_classtype_route
= 9,
41 k_classtype_bone
= 10,
42 k_classtype_skeleton
= 11,
43 k_classtype_skin
= 12,
44 k_classtype_achievement_box
= 13,
45 k_classtype_audio
= 14,
46 k_classtype_trigger
= 15
84 u32 sub_uid
, /* allocated in-file... too bad. */
110 struct mdl_file_header
112 u32 identifier
, version
, file_length
, pad0
;
114 u32 vertex_count
, vertex_offset
,
115 indice_count
, indice_offset
,
116 submesh_count
, submesh_offset
,
117 material_count
, material_offset
,
118 node_count
, node_offset
,
119 anim_count
, anim_offset
,
120 strings_length
, strings_offset
,
121 entdata_length
, entdata_offset
,
122 keyframe_count
, keyframe_offset
;
126 * Entity data structures
129 struct classtype_block
134 struct classtype_gate
140 struct classtype_spawn
145 struct classtype_water
150 struct classtype_car_path
155 struct classtype_instance
160 struct classtype_capsule
162 float height
, radius
;
165 struct classtype_route_node
170 struct classtype_route
176 struct classtype_bone
188 struct classtype_skeleton
197 struct classtype_skin
202 struct classtype_achievement_box
208 struct classtype_audio
222 mdl_file_header info
;
224 /* each buffer becomes availible after each _load function is called */
225 mdl_node
*node_buffer
; /* mdl_load_metadata() */
226 mdl_submesh
*submesh_buffer
;
227 mdl_material
*material_buffer
;
228 mdl_animation
*anim_buffer
;
229 void *entdata_buffer
;
230 const char *string_buffer
;
232 mdl_keyframe
*keyframe_buffer
; /* mdl_load_anim_data() */
234 mdl_vert
*vertex_buffer
; /* mdl_load_mesh_data() */
239 * Simple mesh interface for OpenGL
244 GLuint vao
, vbo
, ebo
;
249 VG_STATIC
void mesh_upload( glmesh
*mesh
,
250 mdl_vert
*verts
, u32 vert_count
,
251 u32
*indices
, u32 indice_count
)
253 //assert( mesh->loaded == 0 );
255 glGenVertexArrays( 1, &mesh
->vao
);
256 glGenBuffers( 1, &mesh
->vbo
);
257 glGenBuffers( 1, &mesh
->ebo
);
258 glBindVertexArray( mesh
->vao
);
260 size_t stride
= sizeof(mdl_vert
);
262 glBindBuffer( GL_ARRAY_BUFFER
, mesh
->vbo
);
263 glBufferData( GL_ARRAY_BUFFER
, vert_count
*stride
, verts
, GL_STATIC_DRAW
);
265 glBindVertexArray( mesh
->vao
);
266 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, mesh
->ebo
);
267 glBufferData( GL_ELEMENT_ARRAY_BUFFER
, indice_count
*sizeof(u32
),
268 indices
, GL_STATIC_DRAW
);
271 glVertexAttribPointer( 0, 3, GL_FLOAT
, GL_FALSE
, stride
, (void*)0 );
272 glEnableVertexAttribArray( 0 );
275 glVertexAttribPointer( 1, 3, GL_FLOAT
, GL_FALSE
,
276 stride
, (void *)offsetof(mdl_vert
, norm
) );
277 glEnableVertexAttribArray( 1 );
280 glVertexAttribPointer( 2, 2, GL_FLOAT
, GL_FALSE
,
281 stride
, (void *)offsetof(mdl_vert
, uv
) );
282 glEnableVertexAttribArray( 2 );
285 glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE
, GL_TRUE
,
286 stride
, (void *)offsetof(mdl_vert
, colour
) );
287 glEnableVertexAttribArray( 3 );
290 glVertexAttribPointer( 4, 4, GL_UNSIGNED_SHORT
, GL_TRUE
,
291 stride
, (void *)offsetof(mdl_vert
, weights
) );
292 glEnableVertexAttribArray( 4 );
295 glVertexAttribIPointer( 5, 4, GL_UNSIGNED_BYTE
,
296 stride
, (void *)offsetof(mdl_vert
, groups
) );
297 glEnableVertexAttribArray( 5 );
301 mesh
->indice_count
= indice_count
;
305 VG_STATIC
void mesh_bind( glmesh
*mesh
)
307 glBindVertexArray( mesh
->vao
);
310 VG_STATIC
void mesh_drawn( u32 start
, u32 count
)
312 glDrawElements( GL_TRIANGLES
, count
, GL_UNSIGNED_INT
,
313 (void *)(start
*sizeof(u32
)) );
316 VG_STATIC
void mesh_draw( glmesh
*mesh
)
318 mesh_drawn( 0, mesh
->indice_count
);
321 VG_STATIC
void mesh_free( glmesh
*mesh
)
325 glDeleteVertexArrays( 1, &mesh
->vao
);
326 glDeleteBuffers( 1, &mesh
->ebo
);
327 glDeleteBuffers( 1, &mesh
->vbo
);
331 VG_STATIC
void mdl_load_fatal_corrupt( mdl_context
*mdl
)
334 vg_file_print_invalid( mdl
->file
);
335 vg_fatal_exit_loop( "Corrupt model" );
339 * Model implementation
343 * you have two api options for loading a model, first, the easy way:
345 * will put the entire model straight into the linear_alloc
347 * or, to target different allocators:
351 * mdl_load_vertex_data
352 * mdl_load_indice_data
355 * these should ideally be called in quick succession to limit stalls.
359 * if calling mdl_open, and the file does not exist, the game will fatal quit
361 VG_STATIC
void mdl_open( mdl_context
*mdl
, const char *path
)
363 memset( mdl
, 0, sizeof( mdl_context
) );
364 mdl
->file
= fopen( path
, "rb" );
368 vg_error( "mdl_open('%s'): %s\n", path
, strerror(errno
) );
369 vg_fatal_exit_loop( "see above for details" );
372 u64 l
= fread( &mdl
->info
, sizeof(mdl_file_header
), 1, mdl
->file
);
374 mdl_load_fatal_corrupt( mdl
);
378 * Load all metadata (everything up until the large buffers). Probs at most 50k
380 VG_STATIC
void mdl_load_metadata( mdl_context
*mdl
, void *lin_alloc
)
384 u64 lheader
= sizeof(mdl_file_header
),
385 ldata
= mdl
->info
.keyframe_offset
- lheader
;
387 void *all_data
= vg_linear_alloc( lin_alloc
, ldata
);
389 fseek( mdl
->file
, lheader
, SEEK_SET
);
390 u64 l
= fread( all_data
, ldata
, 1, mdl
->file
);
394 vg_file_print_invalid( mdl
->file
);
395 vg_fatal_exit_loop( "Corrupt model" );
398 mdl
->node_buffer
= all_data
+ (mdl
->info
.node_offset
- lheader
);
399 mdl
->submesh_buffer
= all_data
+ (mdl
->info
.submesh_offset
- lheader
);
400 mdl
->material_buffer
= all_data
+ (mdl
->info
.material_offset
- lheader
);
401 mdl
->anim_buffer
= all_data
+ (mdl
->info
.anim_offset
- lheader
);
402 mdl
->entdata_buffer
= all_data
+ (mdl
->info
.entdata_offset
- lheader
);
403 mdl
->string_buffer
= all_data
+ (mdl
->info
.strings_offset
- lheader
);
407 * Load just the mesh data
409 VG_STATIC
void mdl_load_mesh_data( mdl_context
*mdl
, void *lin_alloc
)
413 u64 size_verts
= mdl
->info
.vertex_count
* sizeof(mdl_vert
),
414 size_index
= mdl
->info
.indice_count
* sizeof(u32
);
416 mdl
->vertex_buffer
= vg_linear_alloc( lin_alloc
, size_verts
);
417 mdl
->index_buffer
= vg_linear_alloc( lin_alloc
, size_index
);
420 fseek( mdl
->file
, mdl
->info
.vertex_offset
, SEEK_SET
);
421 u64 l
= fread( mdl
->vertex_buffer
, size_verts
, 1, mdl
->file
);
423 mdl_load_fatal_corrupt( mdl
);
426 fseek( mdl
->file
, mdl
->info
.indice_offset
, SEEK_SET
);
427 u64 l
= fread( mdl
->index_buffer
, size_index
, 1, mdl
->file
);
429 mdl_load_fatal_corrupt( mdl
);
434 * Load animation data
436 VG_STATIC
void mdl_load_anim_data( mdl_context
*mdl
, void *lin_alloc
)
440 if( mdl
->info
.keyframe_count
== 0 )
443 u64 size_kf
= mdl
->info
.keyframe_count
* sizeof(mdl_keyframe
);
444 mdl
->keyframe_buffer
= vg_linear_alloc( lin_alloc
, size_kf
);
446 fseek( mdl
->file
, mdl
->info
.keyframe_offset
, SEEK_SET
);
447 u64 l
= fread( mdl
->keyframe_buffer
, size_kf
, 1, mdl
->file
);
449 mdl_load_fatal_corrupt( mdl
);
455 VG_STATIC
void mdl_close( mdl_context
*mdl
)
462 VG_STATIC mdl_context
*mdl_load_full( void *lin_alloc
, const char *path
)
464 /* Inspect the header by opening it, give us the size needed */
465 mdl_context temp_ctx
;
466 mdl_open( &temp_ctx
, path
);
468 /* create allocator */
469 u32 tot_size
= temp_ctx
.info
.file_length
+ sizeof( mdl_context
);
470 void *data
= vg_create_linear_allocator( lin_alloc
, tot_size
);
472 /* copy context and load all other data */
473 mdl_context
*ctx
= vg_linear_alloc( data
, sizeof(mdl_context
) );
474 memcpy( ctx
, &temp_ctx
, sizeof(mdl_context
) );
476 mdl_load_metadata( ctx
, data
);
477 mdl_load_anim_data( ctx
, data
);
478 mdl_load_mesh_data( ctx
, data
);
486 * ----------------------------------------------------------------------------
487 * TODO: Clamp access and oob errors
489 VG_STATIC
const char *mdl_pstr( mdl_context
*mdl
, u32 pstr
)
491 return mdl
->string_buffer
+ pstr
;
494 VG_STATIC mdl_node
*mdl_node_from_id( mdl_context
*mdl
, u32 id
)
496 return &mdl
->node_buffer
[id
];
499 VG_STATIC mdl_node
*mdl_node_from_name( mdl_context
*mdl
, const char *name
)
501 for( int i
=0; i
< mdl
->info
.node_count
; i
++ )
503 mdl_node
*pnode
= mdl_node_from_id( mdl
, i
);
505 if( !strcmp( name
, mdl_pstr( mdl
, pnode
->pstr_name
)) )
512 VG_STATIC mdl_submesh
*mdl_node_submesh( mdl_context
*mdl
,
513 mdl_node
*node
, u32 i
)
515 return &mdl
->submesh_buffer
[ node
->submesh_start
+i
];
518 VG_STATIC u32
*mdl_submesh_indices( mdl_context
*mdl
, mdl_submesh
*sm
)
520 return &mdl
->index_buffer
[ sm
->indice_start
];
523 VG_STATIC mdl_vert
*mdl_submesh_vertices( mdl_context
*mdl
, mdl_submesh
*sm
)
525 return &mdl
->vertex_buffer
[ sm
->vertex_start
];
528 VG_STATIC
void mdl_node_transform( mdl_node
*pnode
, m4x3f transform
)
530 q_m3x3( pnode
->q
, transform
);
531 v3_muls( transform
[0], pnode
->s
[0], transform
[0] );
532 v3_muls( transform
[1], pnode
->s
[1], transform
[1] );
533 v3_muls( transform
[2], pnode
->s
[2], transform
[2] );
534 v3_copy( pnode
->co
, transform
[3] );
537 /* upload a mesh based on file submesh */
538 VG_STATIC
void mdl_unpack_submesh( mdl_context
*mdl
, glmesh
*mesh
,
541 mesh_upload( mesh
, mdl_submesh_vertices( mdl
, sm
), sm
->vertex_count
,
542 mdl_submesh_indices( mdl
, sm
), sm
->indice_count
);
545 /* upload entire mesh from model */
546 VG_STATIC
void mdl_unpack_glmesh( mdl_context
*mdl
, glmesh
*mesh
)
548 u32 offset
= mdl
->submesh_buffer
[0].vertex_count
;
550 for( int i
=1; i
< mdl
->info
.submesh_count
; i
++ )
552 mdl_submesh
*sm
= &mdl
->submesh_buffer
[i
];
553 u32
*indices
= mdl_submesh_indices( mdl
, sm
);
555 for( u32 j
=0; j
<sm
->indice_count
; j
++ )
556 indices
[j
] += offset
;
558 offset
+= sm
->vertex_count
;
561 mesh_upload( mesh
, mdl
->vertex_buffer
, mdl
->info
.vertex_count
,
562 mdl
->index_buffer
, mdl
->info
.indice_count
);
565 VG_STATIC
void mdl_draw_submesh( mdl_submesh
*sm
)
567 mesh_drawn( sm
->indice_start
, sm
->indice_count
);
570 VG_STATIC
void *mdl_get_entdata( mdl_context
*mdl
, mdl_node
*pnode
)
572 return mdl
->entdata_buffer
+ pnode
->offset
;
575 VG_STATIC mdl_keyframe
*mdl_get_animdata( mdl_context
*mdl
, mdl_animation
*anim
)
577 return mdl
->keyframe_buffer
+ anim
->offset
;
580 VG_STATIC
void mdl_link_materials( mdl_context
*root
, mdl_context
*child
)
582 u32 lookup
[MDL_MATERIAL_MAX
];
584 for( int i
=0; i
<child
->info
.material_count
; i
++ )
586 mdl_material
*mi
= &child
->material_buffer
[i
];
587 const char *si
= mdl_pstr( child
, mi
->pstr_name
);
591 for( int j
=0; j
<root
->info
.material_count
; j
++ )
593 mdl_material
*mj
= &root
->material_buffer
[j
];
594 const char *sj
= mdl_pstr( root
, mj
->pstr_name
);
596 if( !strcmp( si
, sj
) )
603 if( lookup
[i
] == 0 && i
!= 0 )
605 vg_warn( "Could not link material '%s' (not present in root model)\n",
610 for( int i
=0; i
<child
->info
.submesh_count
; i
++ )
612 mdl_submesh
*sm
= &child
->submesh_buffer
[i
];
613 sm
->material_id
= lookup
[sm
->material_id
];