some audio things
[carveJwlIkooP6JGAAIwe30JlM.git] / model.h
1 /*
2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 */
4
5 #ifndef MODEL_H
6 #define MODEL_H
7
8 #include "common.h"
9
10 typedef struct glmesh glmesh;
11
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;
21
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
29
30 enum classtype
31 {
32 k_classtype_none = 0,
33 k_classtype_gate = 1,
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_world_light = 200,
46 k_classtype_nonlocal_gate = 300
47 };
48
49 enum mdl_shader
50 {
51 k_shader_standard = 0,
52 k_shader_standard_cutout = 1,
53 k_shader_terrain_blend = 2,
54 k_shader_standard_vertex_blend = 3,
55 k_shader_water = 4
56 };
57
58 enum mdl_surface_prop
59 {
60 k_surface_prop_concrete = 0,
61 k_surface_prop_wood = 1,
62 k_surface_prop_grass = 2,
63 k_surface_prop_tiles = 3
64 };
65
66 enum material_flag
67 {
68 k_material_flag_skate_surface = 0x1,
69 k_material_flag_collision = 0x2,
70 k_material_flag_grow_grass = 0x4,
71 k_material_flag_grind_surface = 0x8
72 };
73
74 enum bone_flag
75 {
76 k_bone_flag_deform = 0x1,
77 k_bone_flag_ik = 0x2,
78 k_bone_flag_collider_box = 0x4,
79 k_bone_flag_collider_capsule = 0x8,
80 k_bone_flag_collider_reserved0 = 0x10,
81 k_bone_flag_collider_reserved1 = 0x20,
82 k_bone_flag_collider_reserved2 = 0x40,
83 k_bone_flag_collider_reserved3 = 0x80,
84 k_bone_flag_collider_any = k_bone_flag_collider_box |
85 k_bone_flag_collider_capsule |
86 k_bone_flag_collider_reserved0 |
87 k_bone_flag_collider_reserved1 |
88 k_bone_flag_collider_reserved2 |
89 k_bone_flag_collider_reserved3,
90 k_bone_flag_cone_constraint = 0x100,
91 k_bone_flag_force_u32 = 0xffffffff
92 };
93
94 #pragma pack(push,1)
95
96 /* 48 byte */
97 struct mdl_vert
98 {
99 v3f co, /* 3*32 */
100 norm; /* 3*32 */
101 v2f uv; /* 2*32 */
102
103 u8 colour[4]; /* 4*8 */
104 u16 weights[4];/* 4*16 */
105 u8 groups[4]; /* 4*8 */
106 };
107
108 struct mdl_submesh
109 {
110 u32 indice_start,
111 indice_count,
112 vertex_start,
113 vertex_count;
114
115 boxf bbx;
116 u32 material_id;
117 };
118
119 struct mdl_texture
120 {
121 u32 pstr_name,
122 pack_offset,
123 pack_length;
124 };
125
126 struct mdl_material
127 {
128 u32 pstr_name,
129 shader,
130 flags,
131 surface_prop;
132
133 v4f colour,
134 colour1;
135
136 u32 tex_diffuse,
137 tex_decal,
138 tex_normal;
139 };
140
141 struct mdl_node
142 {
143 v3f co;
144 v4f q;
145 v3f s;
146
147 u32 sub_uid, /* allocated in-file... too bad. */
148 submesh_start,
149 submesh_count,
150 classtype,
151 offset,
152 parent,
153 pstr_name;
154 };
155
156 struct mdl_keyframe
157 {
158 v3f co;
159 v4f q;
160 v3f s;
161 };
162
163 struct mdl_animation
164 {
165 u32 pstr_name,
166 length;
167
168 float rate;
169
170 u32 offset;
171 };
172
173 struct mdl_file_header
174 {
175 u32 identifier, version, file_length, pad0;
176
177 u32
178 node_count, node_offset,
179 submesh_count, submesh_offset,
180 material_count, material_offset,
181 texture_count, texture_offset,
182 anim_count, anim_offset,
183 entdata_size, entdata_offset,
184 strings_size, strings_offset,
185
186 keyframe_count, keyframe_offset,
187
188 vertex_count, vertex_offset,
189 indice_count, indice_offset,
190
191 pack_size, pack_offset;
192 };
193
194 /*
195 * Entity data structures
196 */
197
198 struct classtype_gate
199 {
200 u32 target;
201 v3f dims;
202 };
203
204 struct classtype_spawn
205 {
206 u32 pstr_alias;
207 };
208
209 struct classtype_water
210 {
211 u32 temp;
212 };
213
214 struct classtype_route_node
215 {
216 u32 target, target1;
217 };
218
219 struct classtype_route
220 {
221 u32 id_start;
222 u32 pstr_name;
223 v3f colour;
224 };
225
226 struct classtype_bone
227 {
228 u32 flags,
229 ik_target,
230 ik_pole;
231
232 boxf hitbox;
233
234 v3f conevx, conevy, coneva;
235 float conet;
236 };
237
238 struct classtype_skeleton
239 {
240 u32 channels,
241 ik_count,
242 collider_count,
243 anim_start,
244 anim_count;
245 };
246
247 struct classtype_skin
248 {
249 u32 skeleton;
250 };
251
252 struct classtype_trigger
253 {
254 u32 target;
255 };
256
257 struct classtype_logic_relay
258 {
259 u32 targets[4];
260 };
261
262 struct classtype_logic_achievement
263 {
264 u32 pstr_name;
265 };
266
267 struct classtype_audio
268 {
269 u32 pstr_file,
270 flags;
271
272 float volume;
273 };
274
275 struct classtype_world_light
276 {
277 enum light_type
278 {
279 k_light_type_point,
280 k_light_type_spot,
281 k_light_type_point_nighttime_only,
282 k_light_type_spot_nighttime_only
283 }
284 type;
285
286 v4f colour; /* RGB, Energy */
287 float angle, range;
288 };
289
290 #pragma pack(pop)
291
292
293 struct mdl_context
294 {
295 FILE *file;
296 mdl_file_header info;
297
298 /* each buffer becomes availible after each _load function is called */
299 mdl_node *node_buffer; /* mdl_load_metadata() */
300 mdl_submesh *submesh_buffer;
301 mdl_material *material_buffer;
302 mdl_texture *texture_buffer;
303 mdl_animation *anim_buffer;
304 void *entdata_buffer;
305 const char *string_buffer;
306
307 mdl_keyframe *keyframe_buffer; /* mdl_load_anim_data() */
308
309 mdl_vert *vertex_buffer; /* mdl_load_mesh_data() */
310 u32 *index_buffer;
311
312 void *pack; /* mdl_load_pack_data() */
313 };
314
315 /*
316 * Simple mesh interface for OpenGL
317 */
318
319 struct glmesh
320 {
321 GLuint vao, vbo, ebo;
322 u32 indice_count;
323 u32 loaded;
324 };
325
326 VG_STATIC void mesh_upload( glmesh *mesh,
327 mdl_vert *verts, u32 vert_count,
328 u32 *indices, u32 indice_count )
329 {
330 //assert( mesh->loaded == 0 );
331
332 glGenVertexArrays( 1, &mesh->vao );
333 glGenBuffers( 1, &mesh->vbo );
334 glGenBuffers( 1, &mesh->ebo );
335 glBindVertexArray( mesh->vao );
336
337 size_t stride = sizeof(mdl_vert);
338
339 glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo );
340 glBufferData( GL_ARRAY_BUFFER, vert_count*stride, verts, GL_STATIC_DRAW );
341
342 glBindVertexArray( mesh->vao );
343 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mesh->ebo );
344 glBufferData( GL_ELEMENT_ARRAY_BUFFER, indice_count*sizeof(u32),
345 indices, GL_STATIC_DRAW );
346
347 /* 0: coordinates */
348 glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0 );
349 glEnableVertexAttribArray( 0 );
350
351 /* 1: normal */
352 glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE,
353 stride, (void *)offsetof(mdl_vert, norm) );
354 glEnableVertexAttribArray( 1 );
355
356 /* 2: uv */
357 glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE,
358 stride, (void *)offsetof(mdl_vert, uv) );
359 glEnableVertexAttribArray( 2 );
360
361 /* 3: colour */
362 glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE, GL_TRUE,
363 stride, (void *)offsetof(mdl_vert, colour) );
364 glEnableVertexAttribArray( 3 );
365
366 /* 4: weights */
367 glVertexAttribPointer( 4, 4, GL_UNSIGNED_SHORT, GL_TRUE,
368 stride, (void *)offsetof(mdl_vert, weights) );
369 glEnableVertexAttribArray( 4 );
370
371 /* 5: groups */
372 glVertexAttribIPointer( 5, 4, GL_UNSIGNED_BYTE,
373 stride, (void *)offsetof(mdl_vert, groups) );
374 glEnableVertexAttribArray( 5 );
375
376 VG_CHECK_GL_ERR();
377
378 mesh->indice_count = indice_count;
379 mesh->loaded = 1;
380 }
381
382 VG_STATIC void mesh_bind( glmesh *mesh )
383 {
384 glBindVertexArray( mesh->vao );
385 }
386
387 VG_STATIC void mesh_drawn( u32 start, u32 count )
388 {
389 glDrawElements( GL_TRIANGLES, count, GL_UNSIGNED_INT,
390 (void *)(start*sizeof(u32)) );
391 }
392
393 VG_STATIC void mesh_draw( glmesh *mesh )
394 {
395 mesh_drawn( 0, mesh->indice_count );
396 }
397
398 VG_STATIC void mesh_free( glmesh *mesh )
399 {
400 if( mesh->loaded )
401 {
402 glDeleteVertexArrays( 1, &mesh->vao );
403 glDeleteBuffers( 1, &mesh->ebo );
404 glDeleteBuffers( 1, &mesh->vbo );
405 mesh->loaded = 0;
406 }
407 }
408
409 VG_STATIC void mdl_load_fatal_corrupt( mdl_context *mdl )
410 {
411 fclose( mdl->file );
412 vg_file_print_invalid( mdl->file );
413 vg_fatal_exit_loop( "Corrupt model" );
414 }
415
416 /*
417 * Model implementation
418 *
419 * TODO.
420 *
421 * you have two api options for loading a model, first, the easy way:
422 * mdl_load ...
423 * will put the entire model straight into the linear_alloc
424 *
425 * or, to target different allocators:
426 *
427 * mdl_open
428 * mdl_load_metadata
429 * mdl_load_vertex_data
430 * mdl_load_indice_data
431 * mdl_close
432 *
433 * these should ideally be called in quick succession to limit stalls.
434 */
435
436 /*
437 * if calling mdl_open, and the file does not exist, the game will fatal quit
438 */
439 VG_STATIC void mdl_open( mdl_context *mdl, const char *path )
440 {
441 memset( mdl, 0, sizeof( mdl_context ) );
442 mdl->file = fopen( path, "rb" );
443
444 if( !mdl->file )
445 {
446 vg_error( "mdl_open('%s'): %s\n", path, strerror(errno) );
447 vg_fatal_exit_loop( "see above for details" );
448 }
449
450 u64 l = fread( &mdl->info, sizeof(mdl_file_header), 1, mdl->file );
451 if( l != 1 )
452 mdl_load_fatal_corrupt( mdl );
453 }
454
455 /*
456 * Load all metadata (everything up until the large buffers). Probs at most 50k
457 */
458 VG_STATIC void mdl_load_metadata( mdl_context *mdl, void *lin_alloc )
459 {
460 assert( mdl->file );
461
462 u64 lheader = sizeof(mdl_file_header),
463 ldata = mdl->info.keyframe_offset - lheader;
464
465 void *all_data = vg_linear_alloc( lin_alloc, ldata );
466
467 fseek( mdl->file, lheader, SEEK_SET );
468 u64 l = fread( all_data, ldata, 1, mdl->file );
469
470 if( l != 1 )
471 {
472 vg_file_print_invalid( mdl->file );
473 vg_fatal_exit_loop( "Corrupt model" );
474 }
475
476 mdl->node_buffer = all_data + (mdl->info.node_offset - lheader);
477 mdl->submesh_buffer = all_data + (mdl->info.submesh_offset - lheader);
478 mdl->material_buffer = all_data + (mdl->info.material_offset - lheader);
479 mdl->texture_buffer = all_data + (mdl->info.texture_offset - lheader);
480 mdl->anim_buffer = all_data + (mdl->info.anim_offset - lheader);
481 mdl->entdata_buffer = all_data + (mdl->info.entdata_offset - lheader);
482 mdl->string_buffer = all_data + (mdl->info.strings_offset - lheader);
483 }
484
485 /*
486 * Load just the mesh data
487 */
488 VG_STATIC void mdl_load_mesh_data( mdl_context *mdl, void *lin_alloc )
489 {
490 assert( mdl->file );
491
492 u64 size_verts = vg_align8( mdl->info.vertex_count * sizeof(mdl_vert) ),
493 size_index = vg_align8( mdl->info.indice_count * sizeof(u32) );
494
495 mdl->vertex_buffer = vg_linear_alloc( lin_alloc, size_verts );
496 mdl->index_buffer = vg_linear_alloc( lin_alloc, size_index );
497
498 {
499 fseek( mdl->file, mdl->info.vertex_offset, SEEK_SET );
500 u64 l = fread( mdl->vertex_buffer, size_verts, 1, mdl->file );
501 if( l != 1 )
502 mdl_load_fatal_corrupt( mdl );
503 }
504 {
505 fseek( mdl->file, mdl->info.indice_offset, SEEK_SET );
506 u64 l = fread( mdl->index_buffer, size_index, 1, mdl->file );
507 if( l != 1 )
508 mdl_load_fatal_corrupt( mdl );
509 }
510 }
511
512 /*
513 * Load animation data
514 */
515 VG_STATIC void mdl_load_anim_data( mdl_context *mdl, void *lin_alloc )
516 {
517 assert( mdl->file );
518
519 if( mdl->info.keyframe_count == 0 )
520 return;
521
522 u64 size_kf = vg_align8( mdl->info.keyframe_count * sizeof(mdl_keyframe) );
523 mdl->keyframe_buffer = vg_linear_alloc( lin_alloc, size_kf );
524
525 fseek( mdl->file, mdl->info.keyframe_offset, SEEK_SET );
526 u64 l = fread( mdl->keyframe_buffer, size_kf, 1, mdl->file );
527 if( l != 1 )
528 mdl_load_fatal_corrupt( mdl );
529 }
530
531 /*
532 * Load pack contents
533 *
534 * TODO request specific files (low)
535 */
536 VG_STATIC void mdl_load_pack_data( mdl_context *mdl, void *lin_alloc )
537 {
538 assert( mdl->file );
539
540 if( mdl->info.pack_size == 0 )
541 return;
542
543 mdl->pack = vg_linear_alloc( lin_alloc, vg_align8( mdl->info.pack_size ) );
544 fseek( mdl->file, mdl->info.pack_offset, SEEK_SET );
545
546 u64 l = fread( mdl->pack, mdl->info.pack_size, 1, mdl->file );
547 if( l != 1 )
548 mdl_load_fatal_corrupt( mdl );
549 }
550
551 /*
552 * close file handle
553 */
554 VG_STATIC void mdl_close( mdl_context *mdl )
555 {
556 fclose( mdl->file );
557 mdl->file = NULL;
558 }
559
560 /* open a model. TODO: make this flags ( ANIM_DATA|MESH_DATA ... ) */
561 VG_STATIC mdl_context *mdl_load_full( void *lin_alloc, const char *path )
562 {
563 /* Inspect the header by opening it, give us the size needed */
564 mdl_context temp_ctx;
565 mdl_open( &temp_ctx, path );
566
567 /* create allocator */
568 u32 tot_size = temp_ctx.info.file_length + sizeof( mdl_context ) + 64;
569 void *data = vg_create_linear_allocator( lin_alloc, tot_size,
570 VG_MEMORY_SYSTEM );
571
572 /* copy context and load all other data */
573 mdl_context *ctx = vg_linear_alloc( data, sizeof(mdl_context) );
574 memcpy( ctx, &temp_ctx, sizeof(mdl_context) );
575
576 mdl_load_metadata( ctx, data );
577 mdl_load_anim_data( ctx, data );
578 mdl_load_mesh_data( ctx, data );
579 mdl_load_pack_data( ctx, data );
580 mdl_close( ctx );
581
582 return ctx;
583 }
584
585 /*
586 * Item getters
587 * ----------------------------------------------------------------------------
588 * TODO: Clamp access and oob errors
589 */
590 VG_STATIC const char *mdl_pstr( mdl_context *mdl, u32 pstr )
591 {
592 return mdl->string_buffer + pstr;
593 }
594
595 VG_STATIC mdl_node *mdl_node_from_id( mdl_context *mdl, u32 id )
596 {
597 return &mdl->node_buffer[id];
598 }
599
600 VG_STATIC mdl_node *mdl_node_from_name( mdl_context *mdl, const char *name )
601 {
602 for( int i=0; i < mdl->info.node_count; i++ )
603 {
604 mdl_node *pnode = mdl_node_from_id( mdl, i );
605
606 if( !strcmp( name, mdl_pstr( mdl, pnode->pstr_name )) )
607 return pnode;
608 }
609
610 return NULL;
611 }
612
613 VG_STATIC mdl_submesh *mdl_node_submesh( mdl_context *mdl,
614 mdl_node *node, u32 i )
615 {
616 return &mdl->submesh_buffer[ node->submesh_start+i ];
617 }
618
619 VG_STATIC u32 *mdl_submesh_indices( mdl_context *mdl, mdl_submesh *sm )
620 {
621 return &mdl->index_buffer[ sm->indice_start ];
622 }
623
624 VG_STATIC mdl_vert *mdl_submesh_vertices( mdl_context *mdl, mdl_submesh *sm )
625 {
626 return &mdl->vertex_buffer[ sm->vertex_start ];
627 }
628
629 VG_STATIC void mdl_node_transform( mdl_node *pnode, m4x3f transform )
630 {
631 q_m3x3( pnode->q, transform );
632 v3_muls( transform[0], pnode->s[0], transform[0] );
633 v3_muls( transform[1], pnode->s[1], transform[1] );
634 v3_muls( transform[2], pnode->s[2], transform[2] );
635 v3_copy( pnode->co, transform[3] );
636 }
637
638 /* upload a mesh based on file submesh */
639 VG_STATIC void mdl_unpack_submesh( mdl_context *mdl, glmesh *mesh,
640 mdl_submesh *sm )
641 {
642 mesh_upload( mesh, mdl_submesh_vertices( mdl, sm ), sm->vertex_count,
643 mdl_submesh_indices( mdl, sm ), sm->indice_count );
644 }
645
646 /* upload entire mesh from model */
647 VG_STATIC void mdl_unpack_glmesh( mdl_context *mdl, glmesh *mesh )
648 {
649 u32 offset = mdl->submesh_buffer[0].vertex_count;
650
651 for( int i=1; i< mdl->info.submesh_count; i++ )
652 {
653 mdl_submesh *sm = &mdl->submesh_buffer[i];
654 u32 *indices = mdl_submesh_indices( mdl, sm );
655
656 for( u32 j=0; j<sm->indice_count; j++ )
657 indices[j] += offset;
658
659 offset += sm->vertex_count;
660 }
661
662 mesh_upload( mesh, mdl->vertex_buffer, mdl->info.vertex_count,
663 mdl->index_buffer, mdl->info.indice_count );
664 }
665
666 VG_STATIC void mdl_draw_submesh( mdl_submesh *sm )
667 {
668 mesh_drawn( sm->indice_start, sm->indice_count );
669 }
670
671 VG_STATIC void *mdl_get_entdata( mdl_context *mdl, mdl_node *pnode )
672 {
673 return mdl->entdata_buffer + pnode->offset;
674 }
675
676 VG_STATIC mdl_keyframe *mdl_get_animdata( mdl_context *mdl, mdl_animation *anim )
677 {
678 return mdl->keyframe_buffer + anim->offset;
679 }
680
681 VG_STATIC void mdl_link_materials( mdl_context *root, mdl_context *child )
682 {
683 u32 lookup[MDL_MATERIAL_MAX];
684
685 for( int i=0; i<child->info.material_count; i++ )
686 {
687 mdl_material *mi = &child->material_buffer[i];
688 const char *si = mdl_pstr( child, mi->pstr_name );
689
690 lookup[i] = 0;
691
692 for( int j=0; j<root->info.material_count; j++ )
693 {
694 mdl_material *mj = &root->material_buffer[j];
695 const char *sj = mdl_pstr( root, mj->pstr_name );
696
697 if( !strcmp( si, sj ) )
698 {
699 lookup[i] = j;
700 break;
701 }
702 }
703
704 if( lookup[i] == 0 && i != 0 )
705 {
706 vg_warn( "Could not link material '%s' (not present in root model)\n",
707 si );
708 }
709 }
710
711 for( int i=0; i<child->info.submesh_count; i++ )
712 {
713 mdl_submesh *sm = &child->submesh_buffer[i];
714 sm->material_id = lookup[sm->material_id];
715 }
716 }
717
718 VG_STATIC void mdl_invert_uv_coordinates( mdl_context *mdl )
719 {
720 for( int i=0; i<mdl->info.vertex_count; i++ )
721 {
722 mdl_vert *vert = &mdl->vertex_buffer[i];
723 vert->uv[1] = 1.0f-vert->uv[1];
724 }
725 }
726
727 #endif