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_texture mdl_texture
;
20 typedef struct mdl_context mdl_context
;
22 #define MDL_SIZE_MAX 0x1000000
23 #define MDL_VERT_MAX 1000000
24 #define MDL_INDICE_MAX 1000000
25 #define MDL_MATERIAL_MAX 32
26 #define MDL_NODE_MAX 4000
27 #define MDL_SUBMESH_MAX 8000
28 #define MDL_STRING_LENGTH_MAX 64
34 k_classtype_spawn
= 3,
35 k_classtype_water
= 4,
36 k_classtype_route_node
= 8,
37 k_classtype_route
= 9,
38 k_classtype_bone
= 10,
39 k_classtype_skeleton
= 11,
40 k_classtype_skin
= 12,
41 k_classtype_audio
= 14,
42 k_classtype_trigger
= 100,
43 k_classtype_logic_achievement
= 101,
44 k_classtype_logic_relay
= 102,
45 k_classtype_logic_script
= 103,
47 k_classtype_logic_wire
= 105,
48 k_classtype_soundscape
= 106,
49 k_classtype_logic_chances
= 107,
50 k_classtype_particle_box
= 108,
51 k_classtype_signal_splitter
= 109,
53 k_classtype_world_light
= 200,
54 k_classtype_nonlocal_gate
= 300
59 k_shader_standard
= 0,
60 k_shader_standard_cutout
= 1,
61 k_shader_terrain_blend
= 2,
62 k_shader_standard_vertex_blend
= 3,
68 k_surface_prop_concrete
= 0,
69 k_surface_prop_wood
= 1,
70 k_surface_prop_grass
= 2,
71 k_surface_prop_tiles
= 3
76 k_material_flag_skate_surface
= 0x1,
77 k_material_flag_collision
= 0x2,
78 k_material_flag_grow_grass
= 0x4,
79 k_material_flag_grind_surface
= 0x8
84 k_bone_flag_deform
= 0x1,
86 k_bone_flag_collider_box
= 0x4,
87 k_bone_flag_collider_capsule
= 0x8,
88 k_bone_flag_collider_reserved0
= 0x10,
89 k_bone_flag_collider_reserved1
= 0x20,
90 k_bone_flag_collider_reserved2
= 0x40,
91 k_bone_flag_collider_reserved3
= 0x80,
92 k_bone_flag_collider_any
= k_bone_flag_collider_box
|
93 k_bone_flag_collider_capsule
|
94 k_bone_flag_collider_reserved0
|
95 k_bone_flag_collider_reserved1
|
96 k_bone_flag_collider_reserved2
|
97 k_bone_flag_collider_reserved3
,
98 k_bone_flag_cone_constraint
= 0x100,
99 k_bone_flag_force_u32
= 0xffffffff
111 u8 colour
[4]; /* 4*8 */
112 u16 weights
[4];/* 4*16 */
113 u8 groups
[4]; /* 4*8 */
155 u32 sub_uid
, /* allocated in-file... too bad. */
181 struct mdl_file_header
183 u32 identifier
, version
, file_length
, pad0
;
186 node_count
, node_offset
,
187 submesh_count
, submesh_offset
,
188 material_count
, material_offset
,
189 texture_count
, texture_offset
,
190 anim_count
, anim_offset
,
191 entdata_size
, entdata_offset
,
192 strings_size
, strings_offset
,
194 keyframe_count
, keyframe_offset
,
196 vertex_count
, vertex_offset
,
197 indice_count
, indice_offset
,
199 pack_size
, pack_offset
;
203 * Entity data structures
206 struct classtype_gate
212 struct classtype_spawn
217 struct classtype_water
222 struct classtype_route_node
227 struct classtype_route
234 struct classtype_bone
242 v3f conevx
, conevy
, coneva
;
246 struct classtype_skeleton
255 struct classtype_skin
260 struct classtype_trigger
265 struct classtype_particle_box
271 struct classtype_signal_splitter
276 struct classtype_logic_relay
281 struct classtype_logic_achievement
286 struct classtype_audio
294 struct classtype_world_light
300 k_light_type_point_nighttime_only
,
301 k_light_type_spot_nighttime_only
305 v4f colour
; /* RGB, Energy */
309 struct classtype_logic_wire
314 union mdl_128bit_union
323 enum mdl_128bit_datatype
325 k_mdl_128bit_datatype_nothing
= 0u,
326 k_mdl_128bit_datatype_integer
= 1u,
327 k_mdl_128bit_datatype_number
= 2u,
328 k_mdl_128bit_datatype_target
= 3u,
329 k_mdl_128bit_datatype_string
= 4u,
330 k_mdl_128bit_datatype_vec2
= 5u,
331 k_mdl_128bit_datatype_vec3
= 6u,
332 k_mdl_128bit_datatype_vec4
= 7u
339 struct classtype_soundscape
344 float transition_duration
;
348 struct classtype_logic_chances
360 mdl_file_header info
;
362 /* each buffer becomes availible after each _load function is called */
363 mdl_node
*node_buffer
; /* mdl_load_metadata() */
364 mdl_submesh
*submesh_buffer
;
365 mdl_material
*material_buffer
;
366 mdl_texture
*texture_buffer
;
367 mdl_animation
*anim_buffer
;
368 void *entdata_buffer
;
369 const char *string_buffer
;
371 mdl_keyframe
*keyframe_buffer
; /* mdl_load_anim_data() */
373 mdl_vert
*vertex_buffer
; /* mdl_load_mesh_data() */
376 void *pack
; /* mdl_load_pack_data() */
380 * Simple mesh interface for OpenGL
385 GLuint vao
, vbo
, ebo
;
390 VG_STATIC
void mesh_upload( glmesh
*mesh
,
391 mdl_vert
*verts
, u32 vert_count
,
392 u32
*indices
, u32 indice_count
)
394 //assert( mesh->loaded == 0 );
396 glGenVertexArrays( 1, &mesh
->vao
);
397 glGenBuffers( 1, &mesh
->vbo
);
398 glGenBuffers( 1, &mesh
->ebo
);
399 glBindVertexArray( mesh
->vao
);
401 size_t stride
= sizeof(mdl_vert
);
403 glBindBuffer( GL_ARRAY_BUFFER
, mesh
->vbo
);
404 glBufferData( GL_ARRAY_BUFFER
, vert_count
*stride
, verts
, GL_STATIC_DRAW
);
406 glBindVertexArray( mesh
->vao
);
407 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, mesh
->ebo
);
408 glBufferData( GL_ELEMENT_ARRAY_BUFFER
, indice_count
*sizeof(u32
),
409 indices
, GL_STATIC_DRAW
);
412 glVertexAttribPointer( 0, 3, GL_FLOAT
, GL_FALSE
, stride
, (void*)0 );
413 glEnableVertexAttribArray( 0 );
416 glVertexAttribPointer( 1, 3, GL_FLOAT
, GL_FALSE
,
417 stride
, (void *)offsetof(mdl_vert
, norm
) );
418 glEnableVertexAttribArray( 1 );
421 glVertexAttribPointer( 2, 2, GL_FLOAT
, GL_FALSE
,
422 stride
, (void *)offsetof(mdl_vert
, uv
) );
423 glEnableVertexAttribArray( 2 );
426 glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE
, GL_TRUE
,
427 stride
, (void *)offsetof(mdl_vert
, colour
) );
428 glEnableVertexAttribArray( 3 );
431 glVertexAttribPointer( 4, 4, GL_UNSIGNED_SHORT
, GL_TRUE
,
432 stride
, (void *)offsetof(mdl_vert
, weights
) );
433 glEnableVertexAttribArray( 4 );
436 glVertexAttribIPointer( 5, 4, GL_UNSIGNED_BYTE
,
437 stride
, (void *)offsetof(mdl_vert
, groups
) );
438 glEnableVertexAttribArray( 5 );
442 mesh
->indice_count
= indice_count
;
446 VG_STATIC
void mesh_bind( glmesh
*mesh
)
448 glBindVertexArray( mesh
->vao
);
451 VG_STATIC
void mesh_drawn( u32 start
, u32 count
)
453 glDrawElements( GL_TRIANGLES
, count
, GL_UNSIGNED_INT
,
454 (void *)(start
*sizeof(u32
)) );
457 VG_STATIC
void mesh_draw( glmesh
*mesh
)
459 mesh_drawn( 0, mesh
->indice_count
);
462 VG_STATIC
void mesh_free( glmesh
*mesh
)
466 glDeleteVertexArrays( 1, &mesh
->vao
);
467 glDeleteBuffers( 1, &mesh
->ebo
);
468 glDeleteBuffers( 1, &mesh
->vbo
);
473 VG_STATIC
void mdl_load_fatal_corrupt( mdl_context
*mdl
)
476 vg_file_print_invalid( mdl
->file
);
477 vg_fatal_exit_loop( "Corrupt model" );
481 * Model implementation
485 * you have two api options for loading a model, first, the easy way:
487 * will put the entire model straight into the linear_alloc
489 * or, to target different allocators:
493 * mdl_load_vertex_data
494 * mdl_load_indice_data
497 * these should ideally be called in quick succession to limit stalls.
501 * if calling mdl_open, and the file does not exist, the game will fatal quit
503 VG_STATIC
void mdl_open( mdl_context
*mdl
, const char *path
)
505 memset( mdl
, 0, sizeof( mdl_context
) );
506 mdl
->file
= fopen( path
, "rb" );
510 vg_error( "mdl_open('%s'): %s\n", path
, strerror(errno
) );
511 vg_fatal_exit_loop( "see above for details" );
514 u64 l
= fread( &mdl
->info
, sizeof(mdl_file_header
), 1, mdl
->file
);
516 mdl_load_fatal_corrupt( mdl
);
520 * Load all metadata (everything up until the large buffers). Probs at most 50k
522 VG_STATIC
void mdl_load_metadata( mdl_context
*mdl
, void *lin_alloc
)
526 u64 lheader
= sizeof(mdl_file_header
),
527 ldata
= mdl
->info
.keyframe_offset
- lheader
;
529 void *all_data
= vg_linear_alloc( lin_alloc
, ldata
);
531 fseek( mdl
->file
, lheader
, SEEK_SET
);
532 u64 l
= fread( all_data
, ldata
, 1, mdl
->file
);
536 vg_file_print_invalid( mdl
->file
);
537 vg_fatal_exit_loop( "Corrupt model" );
540 mdl
->node_buffer
= all_data
+ (mdl
->info
.node_offset
- lheader
);
541 mdl
->submesh_buffer
= all_data
+ (mdl
->info
.submesh_offset
- lheader
);
542 mdl
->material_buffer
= all_data
+ (mdl
->info
.material_offset
- lheader
);
543 mdl
->texture_buffer
= all_data
+ (mdl
->info
.texture_offset
- lheader
);
544 mdl
->anim_buffer
= all_data
+ (mdl
->info
.anim_offset
- lheader
);
545 mdl
->entdata_buffer
= all_data
+ (mdl
->info
.entdata_offset
- lheader
);
546 mdl
->string_buffer
= all_data
+ (mdl
->info
.strings_offset
- lheader
);
550 * Load just the mesh data
552 VG_STATIC
void mdl_load_mesh_data( mdl_context
*mdl
, void *lin_alloc
)
556 u64 size_verts
= vg_align8( mdl
->info
.vertex_count
* sizeof(mdl_vert
) ),
557 size_index
= vg_align8( mdl
->info
.indice_count
* sizeof(u32
) );
559 mdl
->vertex_buffer
= vg_linear_alloc( lin_alloc
, size_verts
);
560 mdl
->index_buffer
= vg_linear_alloc( lin_alloc
, size_index
);
563 fseek( mdl
->file
, mdl
->info
.vertex_offset
, SEEK_SET
);
564 u64 l
= fread( mdl
->vertex_buffer
, size_verts
, 1, mdl
->file
);
566 mdl_load_fatal_corrupt( mdl
);
569 fseek( mdl
->file
, mdl
->info
.indice_offset
, SEEK_SET
);
570 u64 l
= fread( mdl
->index_buffer
, size_index
, 1, mdl
->file
);
572 mdl_load_fatal_corrupt( mdl
);
577 * Load animation data
579 VG_STATIC
void mdl_load_anim_data( mdl_context
*mdl
, void *lin_alloc
)
583 if( mdl
->info
.keyframe_count
== 0 )
586 u64 size_kf
= vg_align8( mdl
->info
.keyframe_count
* sizeof(mdl_keyframe
) );
587 mdl
->keyframe_buffer
= vg_linear_alloc( lin_alloc
, size_kf
);
589 fseek( mdl
->file
, mdl
->info
.keyframe_offset
, SEEK_SET
);
590 u64 l
= fread( mdl
->keyframe_buffer
, size_kf
, 1, mdl
->file
);
592 mdl_load_fatal_corrupt( mdl
);
598 * TODO request specific files (low)
600 VG_STATIC
void mdl_load_pack_data( mdl_context
*mdl
, void *lin_alloc
)
604 if( mdl
->info
.pack_size
== 0 )
607 mdl
->pack
= vg_linear_alloc( lin_alloc
, vg_align8( mdl
->info
.pack_size
) );
608 fseek( mdl
->file
, mdl
->info
.pack_offset
, SEEK_SET
);
610 u64 l
= fread( mdl
->pack
, mdl
->info
.pack_size
, 1, mdl
->file
);
612 mdl_load_fatal_corrupt( mdl
);
618 VG_STATIC
void mdl_close( mdl_context
*mdl
)
624 /* open a model. TODO: make this flags ( ANIM_DATA|MESH_DATA ... ) */
625 VG_STATIC mdl_context
*mdl_load_full( void *lin_alloc
, const char *path
)
627 /* Inspect the header by opening it, give us the size needed */
628 mdl_context temp_ctx
;
629 mdl_open( &temp_ctx
, path
);
631 /* create allocator */
632 u32 tot_size
= temp_ctx
.info
.file_length
+ sizeof( mdl_context
) + 64;
633 void *data
= vg_create_linear_allocator( lin_alloc
, tot_size
,
636 /* copy context and load all other data */
637 mdl_context
*ctx
= vg_linear_alloc( data
, sizeof(mdl_context
) );
638 memcpy( ctx
, &temp_ctx
, sizeof(mdl_context
) );
640 mdl_load_metadata( ctx
, data
);
641 mdl_load_anim_data( ctx
, data
);
642 mdl_load_mesh_data( ctx
, data
);
643 mdl_load_pack_data( ctx
, data
);
651 * ----------------------------------------------------------------------------
652 * TODO: Clamp access and oob errors
654 VG_STATIC
const char *mdl_pstr( mdl_context
*mdl
, u32 pstr
)
656 return mdl
->string_buffer
+ pstr
;
659 VG_STATIC mdl_node
*mdl_node_from_id( mdl_context
*mdl
, u32 id
)
661 return &mdl
->node_buffer
[id
];
664 VG_STATIC mdl_node
*mdl_node_from_name( mdl_context
*mdl
, const char *name
)
666 for( int i
=0; i
< mdl
->info
.node_count
; i
++ )
668 mdl_node
*pnode
= mdl_node_from_id( mdl
, i
);
670 if( !strcmp( name
, mdl_pstr( mdl
, pnode
->pstr_name
)) )
677 VG_STATIC mdl_submesh
*mdl_node_submesh( mdl_context
*mdl
,
678 mdl_node
*node
, u32 i
)
680 return &mdl
->submesh_buffer
[ node
->submesh_start
+i
];
683 VG_STATIC u32
*mdl_submesh_indices( mdl_context
*mdl
, mdl_submesh
*sm
)
685 return &mdl
->index_buffer
[ sm
->indice_start
];
688 VG_STATIC mdl_vert
*mdl_submesh_vertices( mdl_context
*mdl
, mdl_submesh
*sm
)
690 return &mdl
->vertex_buffer
[ sm
->vertex_start
];
693 VG_STATIC
void mdl_node_transform( mdl_node
*pnode
, m4x3f transform
)
695 q_m3x3( pnode
->q
, transform
);
696 v3_muls( transform
[0], pnode
->s
[0], transform
[0] );
697 v3_muls( transform
[1], pnode
->s
[1], transform
[1] );
698 v3_muls( transform
[2], pnode
->s
[2], transform
[2] );
699 v3_copy( pnode
->co
, transform
[3] );
702 /* upload a mesh based on file submesh */
703 VG_STATIC
void mdl_unpack_submesh( mdl_context
*mdl
, glmesh
*mesh
,
706 mesh_upload( mesh
, mdl_submesh_vertices( mdl
, sm
), sm
->vertex_count
,
707 mdl_submesh_indices( mdl
, sm
), sm
->indice_count
);
710 /* upload entire mesh from model */
711 VG_STATIC
void mdl_unpack_glmesh( mdl_context
*mdl
, glmesh
*mesh
)
713 u32 offset
= mdl
->submesh_buffer
[0].vertex_count
;
715 for( int i
=1; i
< mdl
->info
.submesh_count
; i
++ )
717 mdl_submesh
*sm
= &mdl
->submesh_buffer
[i
];
718 u32
*indices
= mdl_submesh_indices( mdl
, sm
);
720 for( u32 j
=0; j
<sm
->indice_count
; j
++ )
721 indices
[j
] += offset
;
723 offset
+= sm
->vertex_count
;
726 mesh_upload( mesh
, mdl
->vertex_buffer
, mdl
->info
.vertex_count
,
727 mdl
->index_buffer
, mdl
->info
.indice_count
);
730 VG_STATIC
void mdl_draw_submesh( mdl_submesh
*sm
)
732 mesh_drawn( sm
->indice_start
, sm
->indice_count
);
735 VG_STATIC
void *mdl_get_entdata( mdl_context
*mdl
, mdl_node
*pnode
)
737 return mdl
->entdata_buffer
+ pnode
->offset
;
740 VG_STATIC mdl_keyframe
*mdl_get_animdata( mdl_context
*mdl
, mdl_animation
*anim
)
742 return mdl
->keyframe_buffer
+ anim
->offset
;
745 VG_STATIC
void mdl_link_materials( mdl_context
*root
, mdl_context
*child
)
747 u32 lookup
[MDL_MATERIAL_MAX
];
749 for( int i
=0; i
<child
->info
.material_count
; i
++ )
751 mdl_material
*mi
= &child
->material_buffer
[i
];
752 const char *si
= mdl_pstr( child
, mi
->pstr_name
);
756 for( int j
=0; j
<root
->info
.material_count
; j
++ )
758 mdl_material
*mj
= &root
->material_buffer
[j
];
759 const char *sj
= mdl_pstr( root
, mj
->pstr_name
);
761 if( !strcmp( si
, sj
) )
768 if( lookup
[i
] == 0 && i
!= 0 )
770 vg_warn( "Could not link material '%s' (not present in root model)\n",
775 for( int i
=0; i
<child
->info
.submesh_count
; i
++ )
777 mdl_submesh
*sm
= &child
->submesh_buffer
[i
];
778 sm
->material_id
= lookup
[sm
->material_id
];
782 VG_STATIC
void mdl_invert_uv_coordinates( mdl_context
*mdl
)
784 for( int i
=0; i
<mdl
->info
.vertex_count
; i
++ )
786 mdl_vert
*vert
= &mdl
->vertex_buffer
[i
];
787 vert
->uv
[1] = 1.0f
-vert
->uv
[1];