6f99fb61d77044d489147a9fa9a9a32b97569a3a
[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 = 000,
33 k_classtype_bone = 001,
34 k_classtype_skeleton = 002,
35 k_classtype_skin = 003,
36 k_classtype_world_light = 004,
37
38 k_classtype_gate = 100,
39 k_classtype_nonlocal_gate = 101,
40 k_classtype_spawn = 200,
41 k_classtype_water = 300,
42 k_classtype_route = 400,
43 k_classtype_route_node = 401,
44 k_classtype_audio = 500,
45 k_classtype_audio_sprite = 502,
46 k_classtype_volume_audio = 600,
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_bone /* 001 */
199 {
200 u32 flags,
201 ik_target,
202 ik_pole;
203
204 boxf hitbox;
205
206 v3f conevx, conevy, coneva;
207 float conet;
208 };
209
210 struct classtype_skeleton /* 002 */
211 {
212 u32 channels,
213 ik_count,
214 collider_count,
215 anim_start,
216 anim_count;
217 };
218
219 struct classtype_skin /* 003 */
220 {
221 u32 skeleton;
222 };
223
224 struct classtype_world_light /* 004 */
225 {
226 enum light_type
227 {
228 k_light_type_point,
229 k_light_type_spot,
230 k_light_type_point_nighttime_only,
231 k_light_type_spot_nighttime_only
232 }
233 type;
234
235 v4f colour; /* RGB, Energy */
236 float angle, range;
237 };
238
239
240 struct classtype_gate /* 100, 101 */
241 {
242 u32 target;
243 v3f dims;
244 };
245
246 struct classtype_spawn /* 200 */
247 {
248 u32 pstr_alias;
249 };
250
251 struct classtype_water /* 300 */
252 {
253 u32 temp;
254 };
255
256 struct classtype_route /* 400 */
257 {
258 u32 id_start;
259 u32 pstr_name;
260 v3f colour;
261 };
262
263 struct classtype_route_node /* 401 */
264 {
265 u32 target, target1;
266 };
267
268
269 struct classtype_audio /* 500 */
270 {
271 u32 pstr_file,
272 flags;
273
274 float volume;
275 };
276
277 struct classtype_audio_sprite /* 501 */
278 {
279 u32 audio,
280 category;
281
282 float probability;
283 };
284
285 struct classtype_volume_audio /* 600 */
286 {
287 u32 category;
288 };
289
290 struct classtype_volume_event /* 601 */
291 {
292 u32 event;
293 };
294
295 #pragma pack(pop)
296
297
298 struct mdl_context
299 {
300 FILE *file;
301 mdl_file_header info;
302
303 /* each buffer becomes availible after each _load function is called */
304 mdl_node *node_buffer; /* mdl_load_metadata() */
305 mdl_submesh *submesh_buffer;
306 mdl_material *material_buffer;
307 mdl_texture *texture_buffer;
308 mdl_animation *anim_buffer;
309 void *entdata_buffer;
310 const char *string_buffer;
311
312 mdl_keyframe *keyframe_buffer; /* mdl_load_anim_data() */
313
314 mdl_vert *vertex_buffer; /* mdl_load_mesh_data() */
315 u32 *index_buffer;
316
317 void *pack; /* mdl_load_pack_data() */
318 };
319
320 /*
321 * Simple mesh interface for OpenGL
322 */
323
324 struct glmesh
325 {
326 GLuint vao, vbo, ebo;
327 u32 indice_count;
328 u32 loaded;
329 };
330
331 VG_STATIC void mesh_upload( glmesh *mesh,
332 mdl_vert *verts, u32 vert_count,
333 u32 *indices, u32 indice_count )
334 {
335 //assert( mesh->loaded == 0 );
336
337 glGenVertexArrays( 1, &mesh->vao );
338 glGenBuffers( 1, &mesh->vbo );
339 glGenBuffers( 1, &mesh->ebo );
340 glBindVertexArray( mesh->vao );
341
342 size_t stride = sizeof(mdl_vert);
343
344 glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo );
345 glBufferData( GL_ARRAY_BUFFER, vert_count*stride, verts, GL_STATIC_DRAW );
346
347 glBindVertexArray( mesh->vao );
348 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mesh->ebo );
349 glBufferData( GL_ELEMENT_ARRAY_BUFFER, indice_count*sizeof(u32),
350 indices, GL_STATIC_DRAW );
351
352 /* 0: coordinates */
353 glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0 );
354 glEnableVertexAttribArray( 0 );
355
356 /* 1: normal */
357 glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE,
358 stride, (void *)offsetof(mdl_vert, norm) );
359 glEnableVertexAttribArray( 1 );
360
361 /* 2: uv */
362 glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE,
363 stride, (void *)offsetof(mdl_vert, uv) );
364 glEnableVertexAttribArray( 2 );
365
366 /* 3: colour */
367 glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE, GL_TRUE,
368 stride, (void *)offsetof(mdl_vert, colour) );
369 glEnableVertexAttribArray( 3 );
370
371 /* 4: weights */
372 glVertexAttribPointer( 4, 4, GL_UNSIGNED_SHORT, GL_TRUE,
373 stride, (void *)offsetof(mdl_vert, weights) );
374 glEnableVertexAttribArray( 4 );
375
376 /* 5: groups */
377 glVertexAttribIPointer( 5, 4, GL_UNSIGNED_BYTE,
378 stride, (void *)offsetof(mdl_vert, groups) );
379 glEnableVertexAttribArray( 5 );
380
381 VG_CHECK_GL_ERR();
382
383 mesh->indice_count = indice_count;
384 mesh->loaded = 1;
385 }
386
387 VG_STATIC void mesh_bind( glmesh *mesh )
388 {
389 glBindVertexArray( mesh->vao );
390 }
391
392 VG_STATIC void mesh_drawn( u32 start, u32 count )
393 {
394 glDrawElements( GL_TRIANGLES, count, GL_UNSIGNED_INT,
395 (void *)(start*sizeof(u32)) );
396 }
397
398 VG_STATIC void mesh_draw( glmesh *mesh )
399 {
400 mesh_drawn( 0, mesh->indice_count );
401 }
402
403 VG_STATIC void mesh_free( glmesh *mesh )
404 {
405 if( mesh->loaded )
406 {
407 glDeleteVertexArrays( 1, &mesh->vao );
408 glDeleteBuffers( 1, &mesh->ebo );
409 glDeleteBuffers( 1, &mesh->vbo );
410 mesh->loaded = 0;
411 }
412 }
413
414 VG_STATIC void mdl_load_fatal_corrupt( mdl_context *mdl )
415 {
416 fclose( mdl->file );
417 vg_file_print_invalid( mdl->file );
418 vg_fatal_exit_loop( "Corrupt model" );
419 }
420
421 /*
422 * Model implementation
423 *
424 * TODO.
425 *
426 * you have two api options for loading a model, first, the easy way:
427 * mdl_load ...
428 * will put the entire model straight into the linear_alloc
429 *
430 * or, to target different allocators:
431 *
432 * mdl_open
433 * mdl_load_metadata
434 * mdl_load_vertex_data
435 * mdl_load_indice_data
436 * mdl_close
437 *
438 * these should ideally be called in quick succession to limit stalls.
439 */
440
441 /*
442 * if calling mdl_open, and the file does not exist, the game will fatal quit
443 */
444 VG_STATIC void mdl_open( mdl_context *mdl, const char *path )
445 {
446 memset( mdl, 0, sizeof( mdl_context ) );
447 mdl->file = fopen( path, "rb" );
448
449 if( !mdl->file )
450 {
451 vg_error( "mdl_open('%s'): %s\n", path, strerror(errno) );
452 vg_fatal_exit_loop( "see above for details" );
453 }
454
455 u64 l = fread( &mdl->info, sizeof(mdl_file_header), 1, mdl->file );
456 if( l != 1 )
457 mdl_load_fatal_corrupt( mdl );
458 }
459
460 /*
461 * Load all metadata (everything up until the large buffers). Probs at most 50k
462 */
463 VG_STATIC void mdl_load_metadata( mdl_context *mdl, void *lin_alloc )
464 {
465 assert( mdl->file );
466
467 u64 lheader = sizeof(mdl_file_header),
468 ldata = mdl->info.keyframe_offset - lheader;
469
470 void *all_data = vg_linear_alloc( lin_alloc, ldata );
471
472 fseek( mdl->file, lheader, SEEK_SET );
473 u64 l = fread( all_data, ldata, 1, mdl->file );
474
475 if( l != 1 )
476 {
477 vg_file_print_invalid( mdl->file );
478 vg_fatal_exit_loop( "Corrupt model" );
479 }
480
481 mdl->node_buffer = all_data + (mdl->info.node_offset - lheader);
482 mdl->submesh_buffer = all_data + (mdl->info.submesh_offset - lheader);
483 mdl->material_buffer = all_data + (mdl->info.material_offset - lheader);
484 mdl->texture_buffer = all_data + (mdl->info.texture_offset - lheader);
485 mdl->anim_buffer = all_data + (mdl->info.anim_offset - lheader);
486 mdl->entdata_buffer = all_data + (mdl->info.entdata_offset - lheader);
487 mdl->string_buffer = all_data + (mdl->info.strings_offset - lheader);
488 }
489
490 /*
491 * Load just the mesh data
492 */
493 VG_STATIC void mdl_load_mesh_data( mdl_context *mdl, void *lin_alloc )
494 {
495 assert( mdl->file );
496
497 u64 size_verts = vg_align8( mdl->info.vertex_count * sizeof(mdl_vert) ),
498 size_index = vg_align8( mdl->info.indice_count * sizeof(u32) );
499
500 mdl->vertex_buffer = vg_linear_alloc( lin_alloc, size_verts );
501 mdl->index_buffer = vg_linear_alloc( lin_alloc, size_index );
502
503 {
504 fseek( mdl->file, mdl->info.vertex_offset, SEEK_SET );
505 u64 l = fread( mdl->vertex_buffer, size_verts, 1, mdl->file );
506 if( l != 1 )
507 mdl_load_fatal_corrupt( mdl );
508 }
509 {
510 fseek( mdl->file, mdl->info.indice_offset, SEEK_SET );
511 u64 l = fread( mdl->index_buffer, size_index, 1, mdl->file );
512 if( l != 1 )
513 mdl_load_fatal_corrupt( mdl );
514 }
515 }
516
517 /*
518 * Load animation data
519 */
520 VG_STATIC void mdl_load_anim_data( mdl_context *mdl, void *lin_alloc )
521 {
522 assert( mdl->file );
523
524 if( mdl->info.keyframe_count == 0 )
525 return;
526
527 u64 size_kf = vg_align8( mdl->info.keyframe_count * sizeof(mdl_keyframe) );
528 mdl->keyframe_buffer = vg_linear_alloc( lin_alloc, size_kf );
529
530 fseek( mdl->file, mdl->info.keyframe_offset, SEEK_SET );
531 u64 l = fread( mdl->keyframe_buffer, size_kf, 1, mdl->file );
532 if( l != 1 )
533 mdl_load_fatal_corrupt( mdl );
534 }
535
536 /*
537 * Load pack contents
538 *
539 * TODO request specific files (low)
540 */
541 VG_STATIC void mdl_load_pack_data( mdl_context *mdl, void *lin_alloc )
542 {
543 assert( mdl->file );
544
545 if( mdl->info.pack_size == 0 )
546 return;
547
548 mdl->pack = vg_linear_alloc( lin_alloc, vg_align8( mdl->info.pack_size ) );
549 fseek( mdl->file, mdl->info.pack_offset, SEEK_SET );
550
551 u64 l = fread( mdl->pack, mdl->info.pack_size, 1, mdl->file );
552 if( l != 1 )
553 mdl_load_fatal_corrupt( mdl );
554 }
555
556 /*
557 * close file handle
558 */
559 VG_STATIC void mdl_close( mdl_context *mdl )
560 {
561 fclose( mdl->file );
562 mdl->file = NULL;
563 }
564
565 /* open a model. TODO: make this flags ( ANIM_DATA|MESH_DATA ... ) */
566 VG_STATIC mdl_context *mdl_load_full( void *lin_alloc, const char *path )
567 {
568 /* Inspect the header by opening it, give us the size needed */
569 mdl_context temp_ctx;
570 mdl_open( &temp_ctx, path );
571
572 /* create allocator */
573 u32 tot_size = temp_ctx.info.file_length + sizeof( mdl_context ) + 64;
574 void *data = vg_create_linear_allocator( lin_alloc, tot_size,
575 VG_MEMORY_SYSTEM );
576
577 /* copy context and load all other data */
578 mdl_context *ctx = vg_linear_alloc( data, sizeof(mdl_context) );
579 memcpy( ctx, &temp_ctx, sizeof(mdl_context) );
580
581 mdl_load_metadata( ctx, data );
582 mdl_load_anim_data( ctx, data );
583 mdl_load_mesh_data( ctx, data );
584 mdl_load_pack_data( ctx, data );
585 mdl_close( ctx );
586
587 return ctx;
588 }
589
590 /*
591 * Item getters
592 * ----------------------------------------------------------------------------
593 * TODO: Clamp access and oob errors
594 */
595 VG_STATIC const char *mdl_pstr( mdl_context *mdl, u32 pstr )
596 {
597 return mdl->string_buffer + pstr;
598 }
599
600 VG_STATIC mdl_node *mdl_node_from_id( mdl_context *mdl, u32 id )
601 {
602 return &mdl->node_buffer[id];
603 }
604
605 VG_STATIC mdl_node *mdl_node_from_name( mdl_context *mdl, const char *name )
606 {
607 for( int i=0; i < mdl->info.node_count; i++ )
608 {
609 mdl_node *pnode = mdl_node_from_id( mdl, i );
610
611 if( !strcmp( name, mdl_pstr( mdl, pnode->pstr_name )) )
612 return pnode;
613 }
614
615 return NULL;
616 }
617
618 VG_STATIC mdl_submesh *mdl_node_submesh( mdl_context *mdl,
619 mdl_node *node, u32 i )
620 {
621 return &mdl->submesh_buffer[ node->submesh_start+i ];
622 }
623
624 VG_STATIC u32 *mdl_submesh_indices( mdl_context *mdl, mdl_submesh *sm )
625 {
626 return &mdl->index_buffer[ sm->indice_start ];
627 }
628
629 VG_STATIC mdl_vert *mdl_submesh_vertices( mdl_context *mdl, mdl_submesh *sm )
630 {
631 return &mdl->vertex_buffer[ sm->vertex_start ];
632 }
633
634 VG_STATIC void mdl_node_transform( mdl_node *pnode, m4x3f transform )
635 {
636 q_m3x3( pnode->q, transform );
637 v3_muls( transform[0], pnode->s[0], transform[0] );
638 v3_muls( transform[1], pnode->s[1], transform[1] );
639 v3_muls( transform[2], pnode->s[2], transform[2] );
640 v3_copy( pnode->co, transform[3] );
641 }
642
643 /* upload a mesh based on file submesh */
644 VG_STATIC void mdl_unpack_submesh( mdl_context *mdl, glmesh *mesh,
645 mdl_submesh *sm )
646 {
647 mesh_upload( mesh, mdl_submesh_vertices( mdl, sm ), sm->vertex_count,
648 mdl_submesh_indices( mdl, sm ), sm->indice_count );
649 }
650
651 /* upload entire mesh from model */
652 VG_STATIC void mdl_unpack_glmesh( mdl_context *mdl, glmesh *mesh )
653 {
654 u32 offset = mdl->submesh_buffer[0].vertex_count;
655
656 for( int i=1; i< mdl->info.submesh_count; i++ )
657 {
658 mdl_submesh *sm = &mdl->submesh_buffer[i];
659 u32 *indices = mdl_submesh_indices( mdl, sm );
660
661 for( u32 j=0; j<sm->indice_count; j++ )
662 indices[j] += offset;
663
664 offset += sm->vertex_count;
665 }
666
667 mesh_upload( mesh, mdl->vertex_buffer, mdl->info.vertex_count,
668 mdl->index_buffer, mdl->info.indice_count );
669 }
670
671 VG_STATIC void mdl_draw_submesh( mdl_submesh *sm )
672 {
673 mesh_drawn( sm->indice_start, sm->indice_count );
674 }
675
676 VG_STATIC void *mdl_get_entdata( mdl_context *mdl, mdl_node *pnode )
677 {
678 return mdl->entdata_buffer + pnode->offset;
679 }
680
681 VG_STATIC mdl_keyframe *mdl_get_animdata( mdl_context *mdl, mdl_animation *anim )
682 {
683 return mdl->keyframe_buffer + anim->offset;
684 }
685
686 VG_STATIC void mdl_link_materials( mdl_context *root, mdl_context *child )
687 {
688 u32 lookup[MDL_MATERIAL_MAX];
689
690 for( int i=0; i<child->info.material_count; i++ )
691 {
692 mdl_material *mi = &child->material_buffer[i];
693 const char *si = mdl_pstr( child, mi->pstr_name );
694
695 lookup[i] = 0;
696
697 for( int j=0; j<root->info.material_count; j++ )
698 {
699 mdl_material *mj = &root->material_buffer[j];
700 const char *sj = mdl_pstr( root, mj->pstr_name );
701
702 if( !strcmp( si, sj ) )
703 {
704 lookup[i] = j;
705 break;
706 }
707 }
708
709 if( lookup[i] == 0 && i != 0 )
710 {
711 vg_warn( "Could not link material '%s' (not present in root model)\n",
712 si );
713 }
714 }
715
716 for( int i=0; i<child->info.submesh_count; i++ )
717 {
718 mdl_submesh *sm = &child->submesh_buffer[i];
719 sm->material_id = lookup[sm->material_id];
720 }
721 }
722
723 VG_STATIC void mdl_invert_uv_coordinates( mdl_context *mdl )
724 {
725 for( int i=0; i<mdl->info.vertex_count; i++ )
726 {
727 mdl_vert *vert = &mdl->vertex_buffer[i];
728 vert->uv[1] = 1.0f-vert->uv[1];
729 }
730 }
731
732 #endif