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