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