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