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