bc0ab66f5ab2e5a0da5cafd898161e74ad92526e
[carveJwlIkooP6JGAAIwe30JlM.git] / model.h
1 /*
2 * Copyright (C) 2021-2024 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 */
4
5 #pragma once
6
7 #include "vg/vg_io.h"
8 #include "vg/vg_async.h"
9 #include "vg/vg_tex.h"
10 #include <string.h>
11 #include <stdlib.h>
12 #include <errno.h>
13
14 #define MDL_VERSION_MIN 101
15 #define MDL_VERSION_NR 105
16
17 enum mdl_shader{
18 k_shader_standard = 0,
19 k_shader_standard_cutout = 1,
20 k_shader_terrain_blend = 2,
21 k_shader_standard_vertex_blend = 3,
22 k_shader_water = 4,
23 k_shader_invisible = 5,
24 k_shader_boundary = 6,
25 k_shader_fxglow = 7,
26 k_shader_cubemap = 8,
27 k_shader_walking = 9,
28 k_shader_foliage = 10,
29 k_shader_override = 30000
30 };
31
32 enum mdl_surface_prop{
33 k_surface_prop_concrete = 0,
34 k_surface_prop_wood = 1,
35 k_surface_prop_grass = 2,
36 k_surface_prop_tiles = 3,
37 k_surface_prop_metal = 4,
38 k_surface_prop_snow = 5,
39 k_surface_prop_sand = 6
40 };
41
42 enum material_flag{
43 k_material_flag_skate_target = 0x0001,
44 k_material_flag_collision = 0x0002,
45 k_material_flag_grow_grass = 0x0004,
46 k_material_flag_grindable = 0x0008,
47 k_material_flag_invisible = 0x0010,
48 k_material_flag_boundary = 0x0020,
49 k_material_flag_preview_visibile = 0x0040,
50 k_material_flag_walking = 0x0080,
51
52 k_material_flag_ghosts =
53 k_material_flag_boundary|
54 k_material_flag_invisible|
55 k_material_flag_walking
56 };
57
58 #pragma pack(push,1)
59
60 /* 48 byte */
61 struct mdl_vert
62 {
63 v3f co, /* 3*32 */
64 norm; /* 3*32 */
65 v2f uv; /* 2*32 */
66
67 u8 colour[4]; /* 4*8 */
68 u16 weights[4];/* 4*16 */
69 u8 groups[4]; /* 4*8 */
70 };
71
72 #pragma pack(pop)
73
74 typedef u32 mdl_indice;
75
76 typedef struct mdl_context mdl_context;
77 typedef struct mdl_array_ptr mdl_array_ptr;
78 typedef struct mdl_vert mdl_vert;
79 typedef struct mdl_transform mdl_transform;
80 typedef struct mdl_submesh mdl_submesh;
81 typedef struct mdl_material mdl_material;
82 typedef struct mdl_bone mdl_bone;
83 typedef struct mdl_armature mdl_armature;
84 typedef struct mdl_animation mdl_animation;
85 typedef struct mdl_transform mdl_keyframe;
86 typedef struct mdl_mesh mdl_mesh;
87 typedef struct mdl_file mdl_file;
88 typedef struct mdl_texture mdl_texture;
89 typedef struct mdl_array mdl_array;
90 typedef struct mdl_header mdl_header;
91
92 typedef struct glmesh glmesh;
93 struct glmesh
94 {
95 GLuint vao, vbo, ebo;
96 u32 indice_count;
97 u32 loaded;
98 };
99
100 struct mdl_transform
101 {
102 v3f co, s;
103 v4f q;
104 };
105
106 static void transform_identity( mdl_transform *transform )
107 {
108 v3_zero( transform->co );
109 q_identity( transform->q );
110 v3_fill( transform->s, 1.0f );
111 }
112
113 static void mdl_transform_vector( mdl_transform *transform, v3f vec, v3f dest )
114 {
115 v3_mul( transform->s, vec, dest );
116 q_mulv( transform->q, dest, dest );
117 }
118
119 static void mdl_transform_point( mdl_transform *transform, v3f co, v3f dest )
120 {
121 mdl_transform_vector( transform, co, dest );
122 v3_add( transform->co, dest, dest );
123 }
124
125 static void mdl_transform_mul( mdl_transform *a, mdl_transform *b,
126 mdl_transform *d )
127 {
128 mdl_transform_point( a, b->co, d->co );
129 q_mul( a->q, b->q, d->q );
130 q_normalize( d->q );
131 v3_mul( a->s, b->s, d->s );
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_none0,
146 tex_none1;
147 };
148
149 struct mdl_bone
150 {
151 v3f co, end;
152 u32 parent,
153 collider,
154 ik_target,
155 ik_pole,
156 flags,
157 pstr_name;
158
159 boxf hitbox;
160 v3f conevx, conevy, coneva;
161 float conet;
162 };
163
164 enum bone_flag
165 {
166 k_bone_flag_deform = 0x00000001,
167 k_bone_flag_ik = 0x00000002,
168 k_bone_flag_cone_constraint = 0x00000004
169 };
170
171 enum bone_collider
172 {
173 k_bone_collider_none = 0,
174 k_bone_collider_box = 1,
175 k_bone_collider_capsule = 2
176 };
177
178 struct mdl_armature
179 {
180 mdl_transform transform;
181 u32 bone_start,
182 bone_count,
183 anim_start,
184 anim_count;
185 };
186
187 struct mdl_animation
188 {
189 u32 pstr_name,
190 length;
191 float rate;
192 u32 offset;
193 };
194
195 struct mdl_submesh
196 {
197 u32 indice_start,
198 indice_count,
199 vertex_start,
200 vertex_count;
201
202 boxf bbx;
203 u16 material_id, flags;
204 };
205
206 enum esubmesh_flags
207 {
208 k_submesh_flag_none = 0x0000,
209 k_submesh_flag_consumed = 0x0001
210 };
211
212 struct mdl_mesh
213 {
214 mdl_transform transform;
215 u32 submesh_start,
216 submesh_count,
217 pstr_name,
218 entity_id, /* upper 16 bits: type, lower 16 bits: index */
219 armature_id;
220 };
221
222 struct mdl_file
223 {
224 u32 pstr_path,
225 pack_offset,
226 pack_size;
227 };
228
229 struct mdl_texture
230 {
231 mdl_file file;
232 u32 glname;
233 };
234
235 struct mdl_array
236 {
237 u32 file_offset,
238 item_count,
239 item_size;
240
241 char name[16];
242 };
243
244 struct mdl_header
245 {
246 u32 version;
247 mdl_array index;
248 };
249
250 struct mdl_context{
251 FILE *file;
252 mdl_header info;
253
254 struct mdl_array_ptr{
255 void *data;
256 u32 count, stride;
257 }
258 index,
259
260 /* metadata */
261 strings,
262 meshs,
263 submeshs,
264 materials,
265 textures,
266 armatures,
267 bones,
268 animations,
269
270 /* animation buffers */
271 keyframes,
272
273 /* mesh buffers */
274 verts,
275 indices;
276 u32 pack_base_offset;
277
278 /* runtime */
279 glmesh mesh;
280 };
281
282
283 static void mdl_load_fatal_corrupt( mdl_context *mdl )
284 {
285 fclose( mdl->file );
286 vg_file_print_invalid( mdl->file );
287 vg_fatal_error( "Corrupt model" );
288 }
289
290 /*
291 * Model implementation
292 */
293
294 static const char *mdl_pstr( mdl_context *mdl, u32 pstr );
295 static
296 void mdl_fread_pack_file( mdl_context *mdl, mdl_file *info, void *dst )
297 {
298 if( !info->pack_size ){
299 vg_warn( "path: %s\n", mdl_pstr( mdl, info->pstr_path ) );
300 vg_fatal_error( "Packed file is only a header; it is not packed" );
301 }
302
303 fseek( mdl->file, mdl->pack_base_offset+info->pack_offset, SEEK_SET );
304 u64 l = fread( dst, info->pack_size, 1, mdl->file );
305
306 if( l != 1 ) mdl_load_fatal_corrupt( mdl );
307 }
308
309 /* TODO: Rename these */
310 static void mdl_load_array_file_buffer( mdl_context *mdl, mdl_array *arr,
311 void *buffer, u32 stride )
312 {
313 if( arr->item_count ){
314 fseek( mdl->file, arr->file_offset, SEEK_SET );
315
316 if( stride == arr->item_size ){
317 u64 l = fread( buffer, arr->item_size*arr->item_count, 1, mdl->file );
318 if( l != 1 ) mdl_load_fatal_corrupt( mdl );
319 }
320 else {
321 vg_warn( "Applying alignment fixup to array @%p [%u -> %u] x %u\n",
322 buffer, arr->item_size, stride, arr->item_count );
323 if( stride < arr->item_size )
324 vg_fatal_error( "not safe\n" );
325
326 for( u32 i=0; i<arr->item_count; i++ ){
327 u64 l = fread( buffer+i*stride, arr->item_size, 1, mdl->file );
328 if( l != 1 ) mdl_load_fatal_corrupt( mdl );
329 }
330 }
331 }
332 }
333
334 static void mdl_load_array_file( mdl_context *mdl, mdl_array_ptr *ptr,
335 mdl_array *arr, void *lin_alloc, u32 stride )
336 {
337 if( stride < arr->item_size ){
338 vg_error( "Structure max: %u. Got: %u\n", stride, arr->item_size );
339 vg_fatal_error( "not safe\n" );
340 }
341
342 if( arr->item_count ){
343 u32 size = stride*arr->item_count;
344 ptr->data = vg_linear_alloc( lin_alloc, vg_align8(size) );
345 mdl_load_array_file_buffer( mdl, arr, ptr->data, stride );
346 }
347 else{
348 ptr->data = NULL;
349 }
350
351 ptr->stride = stride;
352 ptr->count = arr->item_count;
353 }
354
355 static void *mdl_arritm( mdl_array_ptr *arr, u32 index )
356 {
357 return ((u8 *)arr->data) + index*arr->stride;
358 }
359
360 static u32 mdl_arrcount( mdl_array_ptr *arr )
361 {
362 return arr->count;
363 }
364
365 static mdl_array *mdl_find_array( mdl_context *mdl, const char *name )
366 {
367 for( u32 i=0; i<mdl_arrcount(&mdl->index); i++ ){
368 mdl_array *arr = mdl_arritm( &mdl->index, i );
369
370 if( !strncmp(arr->name,name,16) ){
371 return arr;
372 }
373 }
374
375 return NULL;
376 }
377
378 static int _mdl_load_array( mdl_context *mdl, mdl_array_ptr *ptr,
379 const char *name, void *lin_alloc, u32 stride )
380 {
381 mdl_array *arr = mdl_find_array( mdl, name );
382
383 if( arr ){
384 mdl_load_array_file( mdl, ptr, arr, lin_alloc, stride );
385 return 1;
386 }
387 else{
388 ptr->data = NULL;
389 ptr->count = 0;
390 ptr->stride = 0;
391 return 0;
392 }
393 }
394
395 #define MDL_LOAD_ARRAY( MDL, PTR, STRUCT, ALLOCATOR ) \
396 _mdl_load_array( MDL, PTR, #STRUCT, ALLOCATOR, sizeof(STRUCT) )
397
398 static int mdl_load_mesh_block( mdl_context *mdl, void *lin_alloc ){
399 int success = 1;
400
401 success &= MDL_LOAD_ARRAY( mdl, &mdl->verts, mdl_vert, lin_alloc );
402 success &= MDL_LOAD_ARRAY( mdl, &mdl->indices, mdl_indice, lin_alloc );
403
404 return success;
405 }
406
407 static int mdl_load_metadata_block( mdl_context *mdl, void *lin_alloc ){
408 int success = 1;
409
410 success &= _mdl_load_array( mdl, &mdl->strings, "strings", lin_alloc, 1 );
411 success &= MDL_LOAD_ARRAY( mdl, &mdl->meshs, mdl_mesh, lin_alloc );
412 success &= MDL_LOAD_ARRAY( mdl, &mdl->submeshs, mdl_submesh, lin_alloc );
413 success &= MDL_LOAD_ARRAY( mdl, &mdl->materials, mdl_material, lin_alloc );
414 success &= MDL_LOAD_ARRAY( mdl, &mdl->textures, mdl_texture, lin_alloc );
415 success &= MDL_LOAD_ARRAY( mdl, &mdl->armatures, mdl_armature, lin_alloc );
416 success &= MDL_LOAD_ARRAY( mdl, &mdl->bones, mdl_bone, lin_alloc );
417 success &= MDL_LOAD_ARRAY( mdl, &mdl->animations,mdl_animation,lin_alloc );
418
419 return success;
420 }
421
422 static int mdl_load_animation_block( mdl_context *mdl, void *lin_alloc ){
423 return MDL_LOAD_ARRAY( mdl, &mdl->keyframes, mdl_keyframe, lin_alloc );
424 }
425
426 /*
427 * if calling mdl_open, and the file does not exist, the game will fatal quit
428 */
429 static void mdl_open( mdl_context *mdl, const char *path, void *lin_alloc )
430 {
431 memset( mdl, 0, sizeof( mdl_context ) );
432 mdl->file = fopen( path, "rb" );
433
434 if( !mdl->file ){
435 vg_error( "mdl_open('%s'): %s\n", path, strerror(errno) );
436 vg_fatal_error( "see above for details" );
437 }
438
439 u64 l = fread( &mdl->info, sizeof(mdl_header), 1, mdl->file );
440 if( l != 1 )
441 mdl_load_fatal_corrupt( mdl );
442
443 if( mdl->info.version < MDL_VERSION_MIN ){
444 vg_warn( "For model: %s\n", path );
445 vg_warn( " version: %u (min: %u, current: %u)\n",
446 mdl->info.version, MDL_VERSION_MIN, MDL_VERSION_NR );
447
448 vg_fatal_error( "Legacy model version incompatable" );
449 }
450
451 mdl_load_array_file( mdl, &mdl->index, &mdl->info.index, lin_alloc,
452 sizeof(mdl_array) );
453
454 mdl_array *pack = mdl_find_array( mdl, "pack" );
455 if( pack ) mdl->pack_base_offset = pack->file_offset;
456 else mdl->pack_base_offset = 0;
457 }
458
459 /*
460 * close file handle
461 */
462 static void mdl_close( mdl_context *mdl )
463 {
464 fclose( mdl->file );
465 mdl->file = NULL;
466 }
467
468 /* useful things you can do with the model */
469
470 static void mdl_transform_m4x3( mdl_transform *transform, m4x3f mtx )
471 {
472 q_m3x3( transform->q, mtx );
473 v3_muls( mtx[0], transform->s[0], mtx[0] );
474 v3_muls( mtx[1], transform->s[1], mtx[1] );
475 v3_muls( mtx[2], transform->s[2], mtx[2] );
476 v3_copy( transform->co, mtx[3] );
477 }
478
479 static const char *mdl_pstr( mdl_context *mdl, u32 pstr )
480 {
481 return ((char *)mdl_arritm( &mdl->strings, pstr )) + 4;
482 }
483
484
485 static int
486 mdl_pstreq( mdl_context *mdl, u32 pstr, const char *str, u32 djb2 )
487 {
488 u32 hash = *((u32 *)mdl_arritm( &mdl->strings, pstr ));
489 if( hash == djb2 ){
490 if( !strcmp( str, mdl_pstr( mdl, pstr ))) return 1;
491 else return 0;
492 }
493 else return 0;
494 }
495
496 #define MDL_CONST_PSTREQ( MDL, Q, CONSTSTR )\
497 mdl_pstreq( MDL, Q, CONSTSTR, vg_strdjb2( CONSTSTR ) )
498
499 /*
500 * Simple mesh interface for OpenGL
501 * ----------------------------------------------------------------------------
502 */
503
504 static void mesh_upload( glmesh *mesh,
505 mdl_vert *verts, u32 vert_count,
506 u32 *indices, u32 indice_count )
507 {
508 //assert( mesh->loaded == 0 );
509
510 glGenVertexArrays( 1, &mesh->vao );
511 glGenBuffers( 1, &mesh->vbo );
512 glGenBuffers( 1, &mesh->ebo );
513 glBindVertexArray( mesh->vao );
514
515 size_t stride = sizeof(mdl_vert);
516
517 glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo );
518 glBufferData( GL_ARRAY_BUFFER, vert_count*stride, verts, GL_STATIC_DRAW );
519
520 glBindVertexArray( mesh->vao );
521 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mesh->ebo );
522 glBufferData( GL_ELEMENT_ARRAY_BUFFER, indice_count*sizeof(u32),
523 indices, GL_STATIC_DRAW );
524
525 /* 0: coordinates */
526 glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0 );
527 glEnableVertexAttribArray( 0 );
528
529 /* 1: normal */
530 glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE,
531 stride, (void *)offsetof(mdl_vert, norm) );
532 glEnableVertexAttribArray( 1 );
533
534 /* 2: uv */
535 glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE,
536 stride, (void *)offsetof(mdl_vert, uv) );
537 glEnableVertexAttribArray( 2 );
538
539 /* 3: colour */
540 glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE, GL_TRUE,
541 stride, (void *)offsetof(mdl_vert, colour) );
542 glEnableVertexAttribArray( 3 );
543
544 /* 4: weights */
545 glVertexAttribPointer( 4, 4, GL_UNSIGNED_SHORT, GL_TRUE,
546 stride, (void *)offsetof(mdl_vert, weights) );
547 glEnableVertexAttribArray( 4 );
548
549 /* 5: groups */
550 glVertexAttribIPointer( 5, 4, GL_UNSIGNED_BYTE,
551 stride, (void *)offsetof(mdl_vert, groups) );
552 glEnableVertexAttribArray( 5 );
553
554 VG_CHECK_GL_ERR();
555
556 mesh->indice_count = indice_count;
557 mesh->loaded = 1;
558 }
559
560 static void mesh_bind( glmesh *mesh )
561 {
562 glBindVertexArray( mesh->vao );
563 }
564
565 static void mesh_drawn( u32 start, u32 count )
566 {
567 glDrawElements( GL_TRIANGLES, count, GL_UNSIGNED_INT,
568 (void *)(start*sizeof(u32)) );
569 }
570
571 static void mesh_draw( glmesh *mesh )
572 {
573 mesh_drawn( 0, mesh->indice_count );
574 }
575
576 static void mesh_free( glmesh *mesh )
577 {
578 if( mesh->loaded ){
579 glDeleteVertexArrays( 1, &mesh->vao );
580 glDeleteBuffers( 1, &mesh->ebo );
581 glDeleteBuffers( 1, &mesh->vbo );
582 mesh->loaded = 0;
583 }
584 }
585
586 static void mdl_draw_submesh( mdl_submesh *sm )
587 {
588 mesh_drawn( sm->indice_start, sm->indice_count );
589 }
590
591 static mdl_mesh *mdl_find_mesh( mdl_context *mdl, const char *name )
592 {
593 for( u32 i=0; i<mdl_arrcount( &mdl->meshs ); i++ ){
594 mdl_mesh *mesh = mdl_arritm( &mdl->meshs, i );
595 if( !strcmp( name, mdl_pstr( mdl, mesh->pstr_name ))){
596 return mesh;
597 }
598 }
599 return NULL;
600 }
601
602 struct payload_glmesh_load{
603 mdl_vert *verts;
604 u32 *indices;
605
606 u32 vertex_count,
607 indice_count;
608
609 glmesh *mesh;
610 };
611
612 static void async_mdl_load_glmesh( void *payload, u32 size )
613 {
614 struct payload_glmesh_load *job = payload;
615 mesh_upload( job->mesh, job->verts, job->vertex_count,
616 job->indices, job->indice_count );
617 }
618
619 static void mdl_async_load_glmesh( mdl_context *mdl, glmesh *mesh,
620 u32 *fixup_table ){
621 mdl_array *arr_vertices = mdl_find_array( mdl, "mdl_vert" );
622 mdl_array *arr_indices = mdl_find_array( mdl, "mdl_indice" );
623
624 if( arr_vertices && arr_indices ){
625 u32 size_verts = vg_align8(sizeof(mdl_vert)*arr_vertices->item_count),
626 size_indices = vg_align8(sizeof(mdl_indice)*arr_indices->item_count),
627 size_hdr = vg_align8(sizeof(struct payload_glmesh_load)),
628 total = size_hdr + size_verts + size_indices;
629
630 vg_async_item *call = vg_async_alloc( total );
631 struct payload_glmesh_load *job = call->payload;
632
633 u8 *payload = call->payload;
634
635 job->mesh = mesh;
636 job->verts = (void*)(payload + size_hdr);
637 job->indices = (void*)(payload + size_hdr + size_verts);
638 job->vertex_count = arr_vertices->item_count;
639 job->indice_count = arr_indices->item_count;
640
641 mdl_load_array_file_buffer( mdl, arr_vertices,
642 job->verts, sizeof(mdl_vert) );
643 mdl_load_array_file_buffer( mdl, arr_indices, job->indices,
644 sizeof(mdl_indice) );
645
646 if( fixup_table ){
647 for( u32 i=0; i<job->vertex_count; i ++ ){
648 mdl_vert *vert = &job->verts[i];
649
650 for( u32 j=0; j<4; j++ ){
651 vert->groups[j] = fixup_table[vert->groups[j]];
652 }
653 }
654 }
655
656 /*
657 * Unpack the indices (if there are meshes)
658 * ---------------------------------------------------------
659 */
660
661 if( mdl_arrcount( &mdl->submeshs ) ){
662 mdl_submesh *sm = mdl_arritm( &mdl->submeshs, 0 );
663 u32 offset = sm->vertex_count;
664
665 for( u32 i=1; i<mdl_arrcount( &mdl->submeshs ); i++ ){
666 mdl_submesh *sm = mdl_arritm( &mdl->submeshs, i );
667 u32 *indices = job->indices + sm->indice_start;
668
669 for( u32 j=0; j<sm->indice_count; j++ )
670 indices[j] += offset;
671
672 offset += sm->vertex_count;
673 }
674 }
675
676 /*
677 * Dispatch
678 * -------------------------
679 */
680
681 vg_async_dispatch( call, async_mdl_load_glmesh );
682 }
683 else{
684 vg_fatal_error( "no vertex/indice data\n" );
685 }
686 }
687
688 /* uploads the glmesh, and textures. everything is saved into the mdl_context */
689 static void mdl_async_full_load_std( mdl_context *mdl ){
690 mdl_async_load_glmesh( mdl, &mdl->mesh, NULL );
691
692 for( u32 i=0; i<mdl_arrcount( &mdl->textures ); i ++ ){
693 vg_linear_clear( vg_mem.scratch );
694 mdl_texture *tex = mdl_arritm( &mdl->textures, i );
695
696 void *data = vg_linear_alloc( vg_mem.scratch, tex->file.pack_size );
697 mdl_fread_pack_file( mdl, &tex->file, data );
698
699 vg_tex2d_load_qoi_async( data, tex->file.pack_size,
700 VG_TEX2D_CLAMP|VG_TEX2D_NEAREST, &tex->glname );
701 }
702 }