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