2 * Copyright (C) 2021-2024 Mt.ZERO Software, Harry Godden - All Rights Reserved
10 #include "vg/vg_async.h"
11 #include "vg/vg_tex.h"
13 #include "vg/vg_msg.h"
14 #include "vg/vg_string.h"
19 #include "shader_props.h"
21 static void mdl_load_fatal_corrupt( mdl_context
*mdl
)
24 vg_file_print_invalid( mdl
->file
);
25 vg_fatal_error( "Corrupt model" );
29 * Model implementation
32 void mdl_fread_pack_file( mdl_context
*mdl
, mdl_file
*info
, void *dst
)
34 if( !info
->pack_size
)
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" );
40 fseek( mdl
->file
, mdl
->pack_base_offset
+info
->pack_offset
, SEEK_SET
);
41 u64 l
= fread( dst
, info
->pack_size
, 1, mdl
->file
);
43 if( l
!= 1 ) mdl_load_fatal_corrupt( mdl
);
46 /* TODO: Rename these */
47 static void mdl_load_array_file_buffer( mdl_context
*mdl
, mdl_array
*arr
,
48 void *buffer
, u32 stride
)
52 fseek( mdl
->file
, arr
->file_offset
, SEEK_SET
);
54 if( stride
== arr
->item_size
)
56 u64 l
= fread( buffer
, arr
->item_size
*arr
->item_count
, 1, mdl
->file
);
57 if( l
!= 1 ) mdl_load_fatal_corrupt( mdl
);
61 vg_warn( "Applying alignment fixup to array @%p [%u -> %u] x %u\n",
62 buffer
, arr
->item_size
, stride
, arr
->item_count
);
64 if( stride
> arr
->item_size
)
65 memset( buffer
, 0, stride
*arr
->item_count
);
67 u32 read_size
= VG_MIN( stride
, arr
->item_size
);
69 for( u32 i
=0; i
<arr
->item_count
; i
++ )
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
);
75 if( l
!= 1 ) mdl_load_fatal_corrupt( mdl
);
81 static void mdl_load_array_file( mdl_context
*mdl
, mdl_array_ptr
*ptr
,
82 mdl_array
*arr
, void *lin_alloc
, u32 stride
)
86 u32 size
= stride
*arr
->item_count
;
87 ptr
->data
= lin_alloc
? vg_linear_alloc( lin_alloc
, vg_align8(size
) ):
89 mdl_load_array_file_buffer( mdl
, arr
, ptr
->data
, stride
);
97 ptr
->count
= arr
->item_count
;
100 void *mdl_arritm( mdl_array_ptr
*arr
, u32 index
)
102 return ((u8
*)arr
->data
) + index
*arr
->stride
;
105 u32
mdl_arrcount( mdl_array_ptr
*arr
)
110 static mdl_array
*mdl_find_array( mdl_context
*mdl
, const char *name
)
112 for( u32 i
=0; i
<mdl_arrcount(&mdl
->index
); i
++ )
114 mdl_array
*arr
= mdl_arritm( &mdl
->index
, i
);
116 if( !strncmp(arr
->name
,name
,16) )
123 int _mdl_load_array( mdl_context
*mdl
, mdl_array_ptr
*ptr
,
124 const char *name
, void *lin_alloc
, u32 stride
)
126 mdl_array
*arr
= mdl_find_array( mdl
, name
);
130 mdl_load_array_file( mdl
, ptr
, arr
, lin_alloc
, stride
);
142 int mdl_load_mesh_block( mdl_context
*mdl
, void *lin_alloc
)
146 success
&= MDL_LOAD_ARRAY( mdl
, &mdl
->verts
, mdl_vert
, lin_alloc
);
147 success
&= MDL_LOAD_ARRAY( mdl
, &mdl
->indices
, mdl_indice
, lin_alloc
);
152 int mdl_load_metadata_block( mdl_context
*mdl
, void *lin_alloc
)
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
);
164 success
&= mdl_load_materials( mdl
, lin_alloc
);
169 int mdl_load_animation_block( mdl_context
*mdl
, void *lin_alloc
)
171 return MDL_LOAD_ARRAY( mdl
, &mdl
->keyframes
, mdl_keyframe
, lin_alloc
);
174 void *mdl_shader_standard( vg_msg
*msg
, void *alloc
)
176 struct shader_props_standard
*props
=
177 vg_linear_alloc( alloc
, sizeof(struct shader_props_standard
) );
179 vg_msg_getkvintg( msg
, "tex_diffuse", k_vg_msg_u32
, &props
->tex_diffuse
,
185 void *mdl_shader_terrain( vg_msg
*msg
, void *alloc
)
187 struct shader_props_terrain
*props
=
188 vg_linear_alloc( alloc
, sizeof(struct shader_props_terrain
) );
190 vg_msg_getkvintg( msg
, "tex_diffuse", k_vg_msg_u32
, &props
->tex_diffuse
,
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 } );
200 void *mdl_shader_vertex_blend( vg_msg
*msg
, void *alloc
)
202 struct shader_props_vertex_blend
*props
=
203 vg_linear_alloc( alloc
, sizeof(struct shader_props_vertex_blend
) );
205 vg_msg_getkvintg( msg
, "tex_diffuse", k_vg_msg_u32
, &props
->tex_diffuse
,
207 vg_msg_getkvvecf( msg
, "blend_offset", k_vg_msg_v2f
,
208 props
->blend_offset
, (v2f
){ 0.5, 0.0 } );
212 void *mdl_shader_water( vg_msg
*msg
, void *alloc
)
214 struct shader_props_water
*props
=
215 vg_linear_alloc( alloc
, sizeof(struct shader_props_water
) );
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
,
223 vg_msg_getkvintg( msg
, "fresnel", k_vg_msg_f32
, &props
->fresnel
,
225 vg_msg_getkvintg( msg
, "water_scale", k_vg_msg_f32
, &props
->water_sale
,
227 vg_msg_getkvvecf( msg
, "wave_speed", k_vg_msg_v4f
,
228 props
->wave_speed
, (v4f
){0.008,0.006,0.003,0.03} );
232 void *mdl_shader_cubemapped( vg_msg
*msg
, void *alloc
)
234 struct shader_props_cubemapped
*props
=
235 vg_linear_alloc( alloc
, sizeof(struct shader_props_cubemapped
) );
237 vg_msg_getkvintg( msg
, "tex_diffuse", k_vg_msg_u32
, &props
->tex_diffuse
,
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} );
246 bool _mdl_legacy_v105_properties( struct mdl_material_v105
*mat
, vg_msg
*dst
)
248 vg_msg_wkvnum( dst
, "tex_diffuse", k_vg_msg_u32
, 1, &mat
->tex_diffuse
);
250 if( mat
->shader
== k_shader_cubemap
)
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
);
255 else if( mat
->shader
== k_shader_terrain_blend
)
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
);
260 else if( mat
->shader
== k_shader_standard_vertex_blend
)
262 vg_msg_wkvnum( dst
, "blend_offset", k_vg_msg_f32
, 2, mat
->colour1
);
264 else if( mat
->shader
== k_shader_water
)
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
);
273 int mdl_load_materials( mdl_context
*mdl
, void *lin_alloc
)
275 MDL_LOAD_ARRAY( mdl
, &mdl
->materials
, mdl_material
, lin_alloc
);
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 )
282 _mdl_load_array( mdl
, &legacy_materials
, "mdl_material", vg_mem
.scratch
,
283 sizeof(struct mdl_material_v105
) );
288 _mdl_load_array( mdl
, &data
, "shader_data", vg_mem
.scratch
, 1 );
293 for( u32 i
=0; i
<mdl_arrcount(&mdl
->materials
); i
++ )
295 mdl_material
*mat
= mdl_arritm( &mdl
->materials
, i
);
298 #if (MDL_VERSION_MIN <= 105)
300 if( mdl
->info
.version
<= 105 )
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
);
309 vg_msg_init( &msg
, data
.data
+ mat
->props
.kvs
.offset
,
310 mat
->props
.kvs
.size
);
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
)
318 mat
->props
.compiled
= mdl_shader_standard( &msg
, lin_alloc
);
320 else if( mat
->shader
== k_shader_standard_vertex_blend
)
322 mat
->props
.compiled
= mdl_shader_vertex_blend( &msg
, lin_alloc
);
324 else if( mat
->shader
== k_shader_cubemap
)
326 mat
->props
.compiled
= mdl_shader_cubemapped( &msg
, lin_alloc
);
328 else if( mat
->shader
== k_shader_terrain_blend
)
330 mat
->props
.compiled
= mdl_shader_terrain( &msg
, lin_alloc
);
332 else if( mat
->shader
== k_shader_water
)
334 mat
->props
.compiled
= mdl_shader_water( &msg
, lin_alloc
);
337 mat
->props
.compiled
= NULL
;
344 * if calling mdl_open, and the file does not exist, the game will fatal quit
346 void mdl_open( mdl_context
*mdl
, const char *path
, void *lin_alloc
)
348 memset( mdl
, 0, sizeof( mdl_context
) );
349 mdl
->file
= fopen( path
, "rb" );
353 vg_error( "mdl_open('%s'): %s\n", path
, strerror(errno
) );
354 vg_fatal_error( "see above for details" );
357 u64 l
= fread( &mdl
->info
, sizeof(mdl_header
), 1, mdl
->file
);
359 mdl_load_fatal_corrupt( mdl
);
361 if( mdl
->info
.version
< MDL_VERSION_MIN
)
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
);
367 vg_fatal_error( "Legacy model version incompatable" );
370 mdl_load_array_file( mdl
, &mdl
->index
, &mdl
->info
.index
, lin_alloc
,
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;
381 void mdl_close( mdl_context
*mdl
)
387 /* useful things you can do with the model */
389 void mdl_transform_m4x3( mdl_transform
*transform
, m4x3f mtx
)
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] );
398 const char *mdl_pstr( mdl_context
*mdl
, u32 pstr
)
400 return ((char *)mdl_arritm( &mdl
->strings
, pstr
)) + 4;
404 int mdl_pstreq( mdl_context
*mdl
, u32 pstr
, const char *str
, u32 djb2
)
406 u32 hash
= *((u32
*)mdl_arritm( &mdl
->strings
, pstr
));
408 if( !strcmp( str
, mdl_pstr( mdl
, pstr
))) return 1;
415 * Simple mesh interface for OpenGL
416 * ----------------------------------------------------------------------------
420 static void mesh_upload( glmesh
*mesh
,
421 mdl_vert
*verts
, u32 vert_count
,
422 u32
*indices
, u32 indice_count
)
424 glGenVertexArrays( 1, &mesh
->vao
);
425 glGenBuffers( 1, &mesh
->vbo
);
426 glGenBuffers( 1, &mesh
->ebo
);
427 glBindVertexArray( mesh
->vao
);
429 size_t stride
= sizeof(mdl_vert
);
431 glBindBuffer( GL_ARRAY_BUFFER
, mesh
->vbo
);
432 glBufferData( GL_ARRAY_BUFFER
, vert_count
*stride
, verts
, GL_STATIC_DRAW
);
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
);
440 glVertexAttribPointer( 0, 3, GL_FLOAT
, GL_FALSE
, stride
, (void*)0 );
441 glEnableVertexAttribArray( 0 );
444 glVertexAttribPointer( 1, 3, GL_FLOAT
, GL_FALSE
,
445 stride
, (void *)offsetof(mdl_vert
, norm
) );
446 glEnableVertexAttribArray( 1 );
449 glVertexAttribPointer( 2, 2, GL_FLOAT
, GL_FALSE
,
450 stride
, (void *)offsetof(mdl_vert
, uv
) );
451 glEnableVertexAttribArray( 2 );
454 glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE
, GL_TRUE
,
455 stride
, (void *)offsetof(mdl_vert
, colour
) );
456 glEnableVertexAttribArray( 3 );
459 glVertexAttribPointer( 4, 4, GL_UNSIGNED_SHORT
, GL_TRUE
,
460 stride
, (void *)offsetof(mdl_vert
, weights
) );
461 glEnableVertexAttribArray( 4 );
464 glVertexAttribIPointer( 5, 4, GL_UNSIGNED_BYTE
,
465 stride
, (void *)offsetof(mdl_vert
, groups
) );
466 glEnableVertexAttribArray( 5 );
470 mesh
->indice_count
= indice_count
;
474 void mesh_bind( glmesh
*mesh
)
476 glBindVertexArray( mesh
->vao
);
479 void mesh_drawn( u32 start
, u32 count
)
481 glDrawElements( GL_TRIANGLES
, count
, GL_UNSIGNED_INT
,
482 (void *)(start
*sizeof(u32
)) );
485 void mesh_draw( glmesh
*mesh
)
487 mesh_drawn( 0, mesh
->indice_count
);
490 void mesh_free( glmesh
*mesh
)
494 glDeleteVertexArrays( 1, &mesh
->vao
);
495 glDeleteBuffers( 1, &mesh
->ebo
);
496 glDeleteBuffers( 1, &mesh
->vbo
);
501 void mdl_draw_submesh( mdl_submesh
*sm
)
503 mesh_drawn( sm
->indice_start
, sm
->indice_count
);
507 mdl_mesh
*mdl_find_mesh( mdl_context
*mdl
, const char *name
)
509 for( u32 i
=0; i
<mdl_arrcount( &mdl
->meshs
); i
++ )
511 mdl_mesh
*mesh
= mdl_arritm( &mdl
->meshs
, i
);
512 if( !strcmp( name
, mdl_pstr( mdl
, mesh
->pstr_name
)))
520 mdl_submesh
*mdl_find_submesh( mdl_context
*mdl
, const char *mesh_name
)
522 mdl_mesh
*mesh
= mdl_find_mesh( mdl
, mesh_name
);
524 if( !mesh
) return NULL
;
525 if( !mesh
->submesh_count
) return NULL
;
527 return mdl_arritm( &mdl
->submeshs
, mesh
->submesh_start
);
531 struct payload_glmesh_load
542 static void _sync_mdl_load_glmesh( void *payload
, u32 size
)
544 struct payload_glmesh_load
*job
= payload
;
545 mesh_upload( job
->mesh
, job
->verts
, job
->vertex_count
,
546 job
->indices
, job
->indice_count
);
549 void mdl_async_load_glmesh( mdl_context
*mdl
, glmesh
*mesh
, u32
*fixup_table
)
551 mdl_array
*arr_vertices
= mdl_find_array( mdl
, "mdl_vert" );
552 mdl_array
*arr_indices
= mdl_find_array( mdl
, "mdl_indice" );
554 if( arr_vertices
&& arr_indices
)
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
;
561 vg_async_item
*call
= vg_async_alloc( total
);
562 struct payload_glmesh_load
*job
= call
->payload
;
564 u8
*payload
= call
->payload
;
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
;
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
) );
579 for( u32 i
=0; i
<job
->vertex_count
; i
++ )
581 mdl_vert
*vert
= &job
->verts
[i
];
583 for( u32 j
=0; j
<4; j
++ )
585 vert
->groups
[j
] = fixup_table
[vert
->groups
[j
]];
591 * Unpack the indices (if there are meshes)
592 * ---------------------------------------------------------
595 if( mdl_arrcount( &mdl
->submeshs
) )
597 mdl_submesh
*sm
= mdl_arritm( &mdl
->submeshs
, 0 );
598 u32 offset
= sm
->vertex_count
;
600 for( u32 i
=1; i
<mdl_arrcount( &mdl
->submeshs
); i
++ )
602 mdl_submesh
*sm
= mdl_arritm( &mdl
->submeshs
, i
);
603 u32
*indices
= job
->indices
+ sm
->indice_start
;
605 for( u32 j
=0; j
<sm
->indice_count
; j
++ )
606 indices
[j
] += offset
;
608 offset
+= sm
->vertex_count
;
614 * -------------------------
617 vg_async_dispatch( call
, _sync_mdl_load_glmesh
);
621 vg_fatal_error( "no vertex/indice data\n" );
625 /* uploads the glmesh, and textures. everything is saved into the mdl_context */
626 void mdl_async_full_load_std( mdl_context
*mdl
)
628 mdl_async_load_glmesh( mdl
, &mdl
->mesh
, NULL
);
630 for( u32 i
=0; i
<mdl_arrcount( &mdl
->textures
); i
++ )
632 vg_linear_clear( vg_mem
.scratch
);
633 mdl_texture
*tex
= mdl_arritm( &mdl
->textures
, i
);
635 void *data
= vg_linear_alloc( vg_mem
.scratch
, tex
->file
.pack_size
);
636 mdl_fread_pack_file( mdl
, &tex
->file
, data
);
638 vg_tex2d_load_qoi_async( data
, tex
->file
.pack_size
,
639 VG_TEX2D_CLAMP
|VG_TEX2D_NEAREST
, &tex
->glname
);