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