update helpers/location to 'frosted' ui
[carveJwlIkooP6JGAAIwe30JlM.git] / model.c
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
9 #ifndef C0_STOPGAP
10 #include "vg/vg_async.h"
11 #include "vg/vg_tex.h"
12 #endif
13 #include "vg/vg_msg.h"
14 #include "vg/vg_string.h"
15 #include <string.h>
16 #include <stdlib.h>
17 #include <errno.h>
18 #include "model.h"
19 #include "shader_props.h"
20
21 static void mdl_load_fatal_corrupt( mdl_context *mdl )
22 {
23 fclose( mdl->file );
24 vg_file_print_invalid( mdl->file );
25 vg_fatal_error( "Corrupt model" );
26 }
27
28 /*
29 * Model implementation
30 */
31
32 void mdl_fread_pack_file( mdl_context *mdl, mdl_file *info, void *dst )
33 {
34 if( !info->pack_size )
35 {
36 vg_warn( "path: %s\n", mdl_pstr( mdl, info->pstr_path ) );
37 vg_fatal_error( "Packed file is only a header; it is not packed" );
38 }
39
40 fseek( mdl->file, mdl->pack_base_offset+info->pack_offset, SEEK_SET );
41 u64 l = fread( dst, info->pack_size, 1, mdl->file );
42
43 if( l != 1 ) mdl_load_fatal_corrupt( mdl );
44 }
45
46 /* TODO: Rename these */
47 static void mdl_load_array_file_buffer( mdl_context *mdl, mdl_array *arr,
48 void *buffer, u32 stride )
49 {
50 if( arr->item_count )
51 {
52 fseek( mdl->file, arr->file_offset, SEEK_SET );
53
54 if( stride == arr->item_size )
55 {
56 u64 l = fread( buffer, arr->item_size*arr->item_count, 1, mdl->file );
57 if( l != 1 ) mdl_load_fatal_corrupt( mdl );
58 }
59 else
60 {
61 vg_warn( "Applying alignment fixup to array @%p [%u -> %u] x %u\n",
62 buffer, arr->item_size, stride, arr->item_count );
63
64 if( stride > arr->item_size )
65 memset( buffer, 0, stride*arr->item_count );
66
67 u32 read_size = VG_MIN( stride, arr->item_size );
68
69 for( u32 i=0; i<arr->item_count; i++ )
70 {
71 u64 l = fread( buffer+i*stride, read_size, 1, mdl->file );
72 if( stride < arr->item_size )
73 fseek( mdl->file, arr->item_size-stride, SEEK_CUR );
74
75 if( l != 1 ) mdl_load_fatal_corrupt( mdl );
76 }
77 }
78 }
79 }
80
81 static void mdl_load_array_file( mdl_context *mdl, mdl_array_ptr *ptr,
82 mdl_array *arr, void *lin_alloc, u32 stride )
83 {
84 if( arr->item_count )
85 {
86 u32 size = stride*arr->item_count;
87 ptr->data = lin_alloc? vg_linear_alloc( lin_alloc, vg_align8(size) ):
88 malloc( size );
89 mdl_load_array_file_buffer( mdl, arr, ptr->data, stride );
90 }
91 else
92 {
93 ptr->data = NULL;
94 }
95
96 ptr->stride = stride;
97 ptr->count = arr->item_count;
98 }
99
100 void *mdl_arritm( mdl_array_ptr *arr, u32 index )
101 {
102 return ((u8 *)arr->data) + index*arr->stride;
103 }
104
105 u32 mdl_arrcount( mdl_array_ptr *arr )
106 {
107 return arr->count;
108 }
109
110 static mdl_array *mdl_find_array( mdl_context *mdl, const char *name )
111 {
112 for( u32 i=0; i<mdl_arrcount(&mdl->index); i++ )
113 {
114 mdl_array *arr = mdl_arritm( &mdl->index, i );
115
116 if( !strncmp(arr->name,name,16) )
117 return arr;
118 }
119
120 return NULL;
121 }
122
123 int _mdl_load_array( mdl_context *mdl, mdl_array_ptr *ptr,
124 const char *name, void *lin_alloc, u32 stride )
125 {
126 mdl_array *arr = mdl_find_array( mdl, name );
127
128 if( arr )
129 {
130 mdl_load_array_file( mdl, ptr, arr, lin_alloc, stride );
131 return 1;
132 }
133 else
134 {
135 ptr->data = NULL;
136 ptr->count = 0;
137 ptr->stride = 0;
138 return 0;
139 }
140 }
141
142 int mdl_load_mesh_block( mdl_context *mdl, void *lin_alloc )
143 {
144 int success = 1;
145
146 success &= MDL_LOAD_ARRAY( mdl, &mdl->verts, mdl_vert, lin_alloc );
147 success &= MDL_LOAD_ARRAY( mdl, &mdl->indices, mdl_indice, lin_alloc );
148
149 return success;
150 }
151
152 int mdl_load_metadata_block( mdl_context *mdl, void *lin_alloc )
153 {
154 int success = 1;
155
156 success &= _mdl_load_array( mdl, &mdl->strings, "strings", lin_alloc, 1 );
157 success &= MDL_LOAD_ARRAY( mdl, &mdl->meshs, mdl_mesh, lin_alloc );
158 success &= MDL_LOAD_ARRAY( mdl, &mdl->submeshs, mdl_submesh, lin_alloc );
159 success &= MDL_LOAD_ARRAY( mdl, &mdl->textures, mdl_texture, lin_alloc );
160 success &= MDL_LOAD_ARRAY( mdl, &mdl->armatures, mdl_armature, lin_alloc );
161 success &= MDL_LOAD_ARRAY( mdl, &mdl->bones, mdl_bone, lin_alloc );
162 success &= MDL_LOAD_ARRAY( mdl, &mdl->animations,mdl_animation,lin_alloc );
163
164 success &= mdl_load_materials( mdl, lin_alloc );
165
166 return success;
167 }
168
169 int mdl_load_animation_block( mdl_context *mdl, void *lin_alloc )
170 {
171 return MDL_LOAD_ARRAY( mdl, &mdl->keyframes, mdl_keyframe, lin_alloc );
172 }
173
174 void *mdl_shader_standard( vg_msg *msg, void *alloc )
175 {
176 struct shader_props_standard *props =
177 vg_linear_alloc( alloc, sizeof(struct shader_props_standard) );
178
179 vg_msg_getkvintg( msg, "tex_diffuse", k_vg_msg_u32, &props->tex_diffuse,
180 NULL );
181
182 return props;
183 }
184
185 void *mdl_shader_terrain( vg_msg *msg, void *alloc )
186 {
187 struct shader_props_terrain *props =
188 vg_linear_alloc( alloc, sizeof(struct shader_props_terrain) );
189
190 vg_msg_getkvintg( msg, "tex_diffuse", k_vg_msg_u32, &props->tex_diffuse,
191 NULL );
192 vg_msg_getkvvecf( msg, "sand_colour", k_vg_msg_v4f,
193 props->sand_colour, (v4f){ 0.79, 0.63, 0.48, 1.0 } );
194 vg_msg_getkvvecf( msg, "blend_offset", k_vg_msg_v2f,
195 props->blend_offset, (v2f){ 0.5, 0.0 } );
196
197 return props;
198 }
199
200 void *mdl_shader_vertex_blend( vg_msg *msg, void *alloc )
201 {
202 struct shader_props_vertex_blend *props =
203 vg_linear_alloc( alloc, sizeof(struct shader_props_vertex_blend) );
204
205 vg_msg_getkvintg( msg, "tex_diffuse", k_vg_msg_u32, &props->tex_diffuse,
206 NULL );
207 vg_msg_getkvvecf( msg, "blend_offset", k_vg_msg_v2f,
208 props->blend_offset, (v2f){ 0.5, 0.0 } );
209 return props;
210 }
211
212 void *mdl_shader_water( vg_msg *msg, void *alloc )
213 {
214 struct shader_props_water *props =
215 vg_linear_alloc( alloc, sizeof(struct shader_props_water) );
216
217 vg_msg_getkvvecf( msg, "shore_colour", k_vg_msg_v4f,
218 props->shore_colour, (v4f){0.03,0.32,0.61,1.0} );
219 vg_msg_getkvvecf( msg, "deep_colour", k_vg_msg_v4f,
220 props->deep_colour, (v4f){0.0,0.006,0.03,1.0} );
221 vg_msg_getkvintg( msg, "fog_scale", k_vg_msg_f32, &props->fog_scale,
222 (f32[]){0.04} );
223 vg_msg_getkvintg( msg, "fresnel", k_vg_msg_f32, &props->fresnel,
224 (f32[]){5.0} );
225 vg_msg_getkvintg( msg, "water_scale", k_vg_msg_f32, &props->water_sale,
226 (f32[]){ 0.008 } );
227 vg_msg_getkvvecf( msg, "wave_speed", k_vg_msg_v4f,
228 props->wave_speed, (v4f){0.008,0.006,0.003,0.03} );
229 return props;
230 }
231
232 void *mdl_shader_cubemapped( vg_msg *msg, void *alloc )
233 {
234 struct shader_props_cubemapped *props =
235 vg_linear_alloc( alloc, sizeof(struct shader_props_cubemapped) );
236
237 vg_msg_getkvintg( msg, "tex_diffuse", k_vg_msg_u32, &props->tex_diffuse,
238 NULL );
239 vg_msg_getkvintg( msg, "cubemap_entity", k_vg_msg_u32,
240 &props->cubemap_entity, NULL );
241 vg_msg_getkvvecf( msg, "tint", k_vg_msg_v4f,
242 props->tint, (v4f){1.0,1.0,1.0,1.0} );
243 return props;
244 }
245
246 bool _mdl_legacy_v105_properties( struct mdl_material_v105 *mat, vg_msg *dst )
247 {
248 vg_msg_wkvnum( dst, "tex_diffuse", k_vg_msg_u32, 1, &mat->tex_diffuse );
249
250 if( mat->shader == k_shader_cubemap )
251 {
252 vg_msg_wkvnum( dst, "cubemap", k_vg_msg_u32, 1, &mat->tex_none0 );
253 vg_msg_wkvnum( dst, "tint", k_vg_msg_f32, 4, mat->colour );
254 }
255 else if( mat->shader == k_shader_terrain_blend )
256 {
257 vg_msg_wkvnum( dst, "sand_colour", k_vg_msg_f32, 4, mat->colour );
258 vg_msg_wkvnum( dst, "blend_offset", k_vg_msg_f32, 2, mat->colour1 );
259 }
260 else if( mat->shader == k_shader_standard_vertex_blend )
261 {
262 vg_msg_wkvnum( dst, "blend_offset", k_vg_msg_f32, 2, mat->colour1 );
263 }
264 else if( mat->shader == k_shader_water )
265 {
266 vg_msg_wkvnum( dst, "shore_colour", k_vg_msg_f32, 4, mat->colour );
267 vg_msg_wkvnum( dst, "deep_colour", k_vg_msg_f32, 4, mat->colour1 );
268 }
269
270 return 1;
271 }
272
273 int mdl_load_materials( mdl_context *mdl, void *lin_alloc )
274 {
275 MDL_LOAD_ARRAY( mdl, &mdl->materials, mdl_material, lin_alloc );
276
277 #if (MDL_VERSION_MIN <= 105)
278 /* load legacy material data into scratch */
279 mdl_array_ptr legacy_materials;
280 if( mdl->info.version <= 105 )
281 {
282 _mdl_load_array( mdl, &legacy_materials, "mdl_material", vg_mem.scratch,
283 sizeof(struct mdl_material_v105) );
284 }
285 #endif
286
287 mdl_array_ptr data;
288 _mdl_load_array( mdl, &data, "shader_data", vg_mem.scratch, 1 );
289
290 if( !lin_alloc )
291 return 1;
292
293 for( u32 i=0; i<mdl_arrcount(&mdl->materials); i ++ )
294 {
295 mdl_material *mat = mdl_arritm( &mdl->materials, i );
296 vg_msg msg;
297
298 #if (MDL_VERSION_MIN <= 105)
299 u8 legacy_buf[512];
300 if( mdl->info.version <= 105 )
301 {
302 vg_msg_init( &msg, legacy_buf, sizeof(legacy_buf) );
303 _mdl_legacy_v105_properties( mdl_arritm( &legacy_materials,i ), &msg );
304 vg_msg_init( &msg, legacy_buf, msg.cur.co );
305 }
306 else
307 #endif
308 {
309 vg_msg_init( &msg, data.data + mat->props.kvs.offset,
310 mat->props.kvs.size );
311 }
312
313 if( mat->shader == k_shader_standard ||
314 mat->shader == k_shader_standard_cutout ||
315 mat->shader == k_shader_foliage ||
316 mat->shader == k_shader_fxglow )
317 {
318 mat->props.compiled = mdl_shader_standard( &msg, lin_alloc );
319 }
320 else if( mat->shader == k_shader_standard_vertex_blend )
321 {
322 mat->props.compiled = mdl_shader_vertex_blend( &msg, lin_alloc );
323 }
324 else if( mat->shader == k_shader_cubemap )
325 {
326 mat->props.compiled = mdl_shader_cubemapped( &msg, lin_alloc );
327 }
328 else if( mat->shader == k_shader_terrain_blend )
329 {
330 mat->props.compiled = mdl_shader_terrain( &msg, lin_alloc );
331 }
332 else if( mat->shader == k_shader_water )
333 {
334 mat->props.compiled = mdl_shader_water( &msg, lin_alloc );
335 }
336 else
337 mat->props.compiled = NULL;
338 }
339
340 return 1;
341 }
342
343 /*
344 * if calling mdl_open, and the file does not exist, the game will fatal quit
345 */
346 void mdl_open( mdl_context *mdl, const char *path, void *lin_alloc )
347 {
348 memset( mdl, 0, sizeof( mdl_context ) );
349 mdl->file = fopen( path, "rb" );
350
351 if( !mdl->file )
352 {
353 vg_error( "mdl_open('%s'): %s\n", path, strerror(errno) );
354 vg_fatal_error( "see above for details" );
355 }
356
357 u64 l = fread( &mdl->info, sizeof(mdl_header), 1, mdl->file );
358 if( l != 1 )
359 mdl_load_fatal_corrupt( mdl );
360
361 if( mdl->info.version < MDL_VERSION_MIN )
362 {
363 vg_warn( "For model: %s\n", path );
364 vg_warn( " version: %u (min: %u, current: %u)\n",
365 mdl->info.version, MDL_VERSION_MIN, MDL_VERSION_NR );
366
367 vg_fatal_error( "Legacy model version incompatable" );
368 }
369
370 mdl_load_array_file( mdl, &mdl->index, &mdl->info.index, lin_alloc,
371 sizeof(mdl_array) );
372
373 mdl_array *pack = mdl_find_array( mdl, "pack" );
374 if( pack ) mdl->pack_base_offset = pack->file_offset;
375 else mdl->pack_base_offset = 0;
376 }
377
378 /*
379 * close file handle
380 */
381 void mdl_close( mdl_context *mdl )
382 {
383 fclose( mdl->file );
384 mdl->file = NULL;
385 }
386
387 /* useful things you can do with the model */
388
389 void mdl_transform_m4x3( mdl_transform *transform, m4x3f mtx )
390 {
391 q_m3x3( transform->q, mtx );
392 v3_muls( mtx[0], transform->s[0], mtx[0] );
393 v3_muls( mtx[1], transform->s[1], mtx[1] );
394 v3_muls( mtx[2], transform->s[2], mtx[2] );
395 v3_copy( transform->co, mtx[3] );
396 }
397
398 const char *mdl_pstr( mdl_context *mdl, u32 pstr )
399 {
400 return ((char *)mdl_arritm( &mdl->strings, pstr )) + 4;
401 }
402
403
404 int mdl_pstreq( mdl_context *mdl, u32 pstr, const char *str, u32 djb2 )
405 {
406 u32 hash = *((u32 *)mdl_arritm( &mdl->strings, pstr ));
407 if( hash == djb2 ){
408 if( !strcmp( str, mdl_pstr( mdl, pstr ))) return 1;
409 else return 0;
410 }
411 else return 0;
412 }
413
414 /*
415 * Simple mesh interface for OpenGL
416 * ----------------------------------------------------------------------------
417 */
418
419 #ifndef C0_STOPGAP
420 static void mesh_upload( glmesh *mesh,
421 mdl_vert *verts, u32 vert_count,
422 u32 *indices, u32 indice_count )
423 {
424 glGenVertexArrays( 1, &mesh->vao );
425 glGenBuffers( 1, &mesh->vbo );
426 glGenBuffers( 1, &mesh->ebo );
427 glBindVertexArray( mesh->vao );
428
429 size_t stride = sizeof(mdl_vert);
430
431 glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo );
432 glBufferData( GL_ARRAY_BUFFER, vert_count*stride, verts, GL_STATIC_DRAW );
433
434 glBindVertexArray( mesh->vao );
435 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mesh->ebo );
436 glBufferData( GL_ELEMENT_ARRAY_BUFFER, indice_count*sizeof(u32),
437 indices, GL_STATIC_DRAW );
438
439 /* 0: coordinates */
440 glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0 );
441 glEnableVertexAttribArray( 0 );
442
443 /* 1: normal */
444 glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE,
445 stride, (void *)offsetof(mdl_vert, norm) );
446 glEnableVertexAttribArray( 1 );
447
448 /* 2: uv */
449 glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE,
450 stride, (void *)offsetof(mdl_vert, uv) );
451 glEnableVertexAttribArray( 2 );
452
453 /* 3: colour */
454 glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE, GL_TRUE,
455 stride, (void *)offsetof(mdl_vert, colour) );
456 glEnableVertexAttribArray( 3 );
457
458 /* 4: weights */
459 glVertexAttribPointer( 4, 4, GL_UNSIGNED_SHORT, GL_TRUE,
460 stride, (void *)offsetof(mdl_vert, weights) );
461 glEnableVertexAttribArray( 4 );
462
463 /* 5: groups */
464 glVertexAttribIPointer( 5, 4, GL_UNSIGNED_BYTE,
465 stride, (void *)offsetof(mdl_vert, groups) );
466 glEnableVertexAttribArray( 5 );
467
468 VG_CHECK_GL_ERR();
469
470 mesh->indice_count = indice_count;
471 mesh->loaded = 1;
472 }
473
474 void mesh_bind( glmesh *mesh )
475 {
476 glBindVertexArray( mesh->vao );
477 }
478
479 void mesh_drawn( u32 start, u32 count )
480 {
481 glDrawElements( GL_TRIANGLES, count, GL_UNSIGNED_INT,
482 (void *)(start*sizeof(u32)) );
483 }
484
485 void mesh_draw( glmesh *mesh )
486 {
487 mesh_drawn( 0, mesh->indice_count );
488 }
489
490 void mesh_free( glmesh *mesh )
491 {
492 if( mesh->loaded )
493 {
494 glDeleteVertexArrays( 1, &mesh->vao );
495 glDeleteBuffers( 1, &mesh->ebo );
496 glDeleteBuffers( 1, &mesh->vbo );
497 mesh->loaded = 0;
498 }
499 }
500
501 void mdl_draw_submesh( mdl_submesh *sm )
502 {
503 mesh_drawn( sm->indice_start, sm->indice_count );
504 }
505 #endif
506
507 mdl_mesh *mdl_find_mesh( mdl_context *mdl, const char *name )
508 {
509 for( u32 i=0; i<mdl_arrcount( &mdl->meshs ); i++ )
510 {
511 mdl_mesh *mesh = mdl_arritm( &mdl->meshs, i );
512 if( !strcmp( name, mdl_pstr( mdl, mesh->pstr_name )))
513 {
514 return mesh;
515 }
516 }
517 return NULL;
518 }
519
520 mdl_submesh *mdl_find_submesh( mdl_context *mdl, const char *mesh_name )
521 {
522 mdl_mesh *mesh = mdl_find_mesh( mdl, mesh_name );
523
524 if( !mesh ) return NULL;
525 if( !mesh->submesh_count ) return NULL;
526
527 return mdl_arritm( &mdl->submeshs, mesh->submesh_start );
528 }
529
530 #ifndef C0_STOPGAP
531 struct payload_glmesh_load
532 {
533 mdl_vert *verts;
534 u32 *indices;
535
536 u32 vertex_count,
537 indice_count;
538
539 glmesh *mesh;
540 };
541
542 static void _sync_mdl_load_glmesh( void *payload, u32 size )
543 {
544 struct payload_glmesh_load *job = payload;
545 mesh_upload( job->mesh, job->verts, job->vertex_count,
546 job->indices, job->indice_count );
547 }
548
549 void mdl_async_load_glmesh( mdl_context *mdl, glmesh *mesh, u32 *fixup_table )
550 {
551 mdl_array *arr_vertices = mdl_find_array( mdl, "mdl_vert" );
552 mdl_array *arr_indices = mdl_find_array( mdl, "mdl_indice" );
553
554 if( arr_vertices && arr_indices )
555 {
556 u32 size_verts = vg_align8(sizeof(mdl_vert)*arr_vertices->item_count),
557 size_indices = vg_align8(sizeof(mdl_indice)*arr_indices->item_count),
558 size_hdr = vg_align8(sizeof(struct payload_glmesh_load)),
559 total = size_hdr + size_verts + size_indices;
560
561 vg_async_item *call = vg_async_alloc( total );
562 struct payload_glmesh_load *job = call->payload;
563
564 u8 *payload = call->payload;
565
566 job->mesh = mesh;
567 job->verts = (void*)(payload + size_hdr);
568 job->indices = (void*)(payload + size_hdr + size_verts);
569 job->vertex_count = arr_vertices->item_count;
570 job->indice_count = arr_indices->item_count;
571
572 mdl_load_array_file_buffer( mdl, arr_vertices,
573 job->verts, sizeof(mdl_vert) );
574 mdl_load_array_file_buffer( mdl, arr_indices, job->indices,
575 sizeof(mdl_indice) );
576
577 if( fixup_table )
578 {
579 for( u32 i=0; i<job->vertex_count; i ++ )
580 {
581 mdl_vert *vert = &job->verts[i];
582
583 for( u32 j=0; j<4; j++ )
584 {
585 vert->groups[j] = fixup_table[vert->groups[j]];
586 }
587 }
588 }
589
590 /*
591 * Unpack the indices (if there are meshes)
592 * ---------------------------------------------------------
593 */
594
595 if( mdl_arrcount( &mdl->submeshs ) )
596 {
597 mdl_submesh *sm = mdl_arritm( &mdl->submeshs, 0 );
598 u32 offset = sm->vertex_count;
599
600 for( u32 i=1; i<mdl_arrcount( &mdl->submeshs ); i++ )
601 {
602 mdl_submesh *sm = mdl_arritm( &mdl->submeshs, i );
603 u32 *indices = job->indices + sm->indice_start;
604
605 for( u32 j=0; j<sm->indice_count; j++ )
606 indices[j] += offset;
607
608 offset += sm->vertex_count;
609 }
610 }
611
612 /*
613 * Dispatch
614 * -------------------------
615 */
616
617 vg_async_dispatch( call, _sync_mdl_load_glmesh );
618 }
619 else
620 {
621 vg_fatal_error( "no vertex/indice data\n" );
622 }
623 }
624
625 /* uploads the glmesh, and textures. everything is saved into the mdl_context */
626 void mdl_async_full_load_std( mdl_context *mdl )
627 {
628 mdl_async_load_glmesh( mdl, &mdl->mesh, NULL );
629
630 for( u32 i=0; i<mdl_arrcount( &mdl->textures ); i ++ )
631 {
632 vg_linear_clear( vg_mem.scratch );
633 mdl_texture *tex = mdl_arritm( &mdl->textures, i );
634
635 void *data = vg_linear_alloc( vg_mem.scratch, tex->file.pack_size );
636 mdl_fread_pack_file( mdl, &tex->file, data );
637
638 vg_tex2d_load_qoi_async( data, tex->file.pack_size,
639 VG_TEX2D_CLAMP|VG_TEX2D_NEAREST, &tex->glname );
640 }
641 }
642 #endif