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