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