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