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