2 * Copyright (C) 2021-2024 Mt.ZERO Software, Harry Godden - All Rights Reserved
9 #include "vg/vg_async.h"
10 #include "vg/vg_tex.h"
15 static void mdl_load_fatal_corrupt( mdl_context
*mdl
)
18 vg_file_print_invalid( mdl
->file
);
19 vg_fatal_error( "Corrupt model" );
23 * Model implementation
26 void mdl_fread_pack_file( mdl_context
*mdl
, mdl_file
*info
, void *dst
)
28 if( !info
->pack_size
)
30 vg_warn( "path: %s\n", mdl_pstr( mdl
, info
->pstr_path
) );
31 vg_fatal_error( "Packed file is only a header; it is not packed" );
34 fseek( mdl
->file
, mdl
->pack_base_offset
+info
->pack_offset
, SEEK_SET
);
35 u64 l
= fread( dst
, info
->pack_size
, 1, mdl
->file
);
37 if( l
!= 1 ) mdl_load_fatal_corrupt( mdl
);
40 /* TODO: Rename these */
41 static void mdl_load_array_file_buffer( mdl_context
*mdl
, mdl_array
*arr
,
42 void *buffer
, u32 stride
)
46 fseek( mdl
->file
, arr
->file_offset
, SEEK_SET
);
48 if( stride
== arr
->item_size
)
50 u64 l
= fread( buffer
, arr
->item_size
*arr
->item_count
, 1, mdl
->file
);
51 if( l
!= 1 ) mdl_load_fatal_corrupt( mdl
);
55 vg_warn( "Applying alignment fixup to array @%p [%u -> %u] x %u\n",
56 buffer
, arr
->item_size
, stride
, arr
->item_count
);
57 if( stride
< arr
->item_size
)
58 vg_fatal_error( "not safe\n" );
60 for( u32 i
=0; i
<arr
->item_count
; i
++ )
62 u64 l
= fread( buffer
+i
*stride
, arr
->item_size
, 1, mdl
->file
);
63 if( l
!= 1 ) mdl_load_fatal_corrupt( mdl
);
69 static void mdl_load_array_file( mdl_context
*mdl
, mdl_array_ptr
*ptr
,
70 mdl_array
*arr
, void *lin_alloc
, u32 stride
)
72 if( stride
< arr
->item_size
)
74 vg_error( "Structure max: %u. Got: %u\n", stride
, arr
->item_size
);
75 vg_fatal_error( "not safe\n" );
80 u32 size
= stride
*arr
->item_count
;
81 ptr
->data
= vg_linear_alloc( lin_alloc
, vg_align8(size
) );
82 mdl_load_array_file_buffer( mdl
, arr
, ptr
->data
, stride
);
90 ptr
->count
= arr
->item_count
;
93 void *mdl_arritm( mdl_array_ptr
*arr
, u32 index
)
95 return ((u8
*)arr
->data
) + index
*arr
->stride
;
98 u32
mdl_arrcount( mdl_array_ptr
*arr
)
103 static mdl_array
*mdl_find_array( mdl_context
*mdl
, const char *name
)
105 for( u32 i
=0; i
<mdl_arrcount(&mdl
->index
); i
++ )
107 mdl_array
*arr
= mdl_arritm( &mdl
->index
, i
);
109 if( !strncmp(arr
->name
,name
,16) )
116 int _mdl_load_array( mdl_context
*mdl
, mdl_array_ptr
*ptr
,
117 const char *name
, void *lin_alloc
, u32 stride
)
119 mdl_array
*arr
= mdl_find_array( mdl
, name
);
123 mdl_load_array_file( mdl
, ptr
, arr
, lin_alloc
, stride
);
135 int mdl_load_mesh_block( mdl_context
*mdl
, void *lin_alloc
)
139 success
&= MDL_LOAD_ARRAY( mdl
, &mdl
->verts
, mdl_vert
, lin_alloc
);
140 success
&= MDL_LOAD_ARRAY( mdl
, &mdl
->indices
, mdl_indice
, lin_alloc
);
145 int mdl_load_metadata_block( mdl_context
*mdl
, void *lin_alloc
)
149 success
&= _mdl_load_array( mdl
, &mdl
->strings
, "strings", lin_alloc
, 1 );
150 success
&= MDL_LOAD_ARRAY( mdl
, &mdl
->meshs
, mdl_mesh
, lin_alloc
);
151 success
&= MDL_LOAD_ARRAY( mdl
, &mdl
->submeshs
, mdl_submesh
, lin_alloc
);
152 success
&= MDL_LOAD_ARRAY( mdl
, &mdl
->materials
, mdl_material
, lin_alloc
);
153 success
&= MDL_LOAD_ARRAY( mdl
, &mdl
->textures
, mdl_texture
, lin_alloc
);
154 success
&= MDL_LOAD_ARRAY( mdl
, &mdl
->armatures
, mdl_armature
, lin_alloc
);
155 success
&= MDL_LOAD_ARRAY( mdl
, &mdl
->bones
, mdl_bone
, lin_alloc
);
156 success
&= MDL_LOAD_ARRAY( mdl
, &mdl
->animations
,mdl_animation
,lin_alloc
);
161 int mdl_load_animation_block( mdl_context
*mdl
, void *lin_alloc
)
163 return MDL_LOAD_ARRAY( mdl
, &mdl
->keyframes
, mdl_keyframe
, lin_alloc
);
167 * if calling mdl_open, and the file does not exist, the game will fatal quit
169 void mdl_open( mdl_context
*mdl
, const char *path
, void *lin_alloc
)
171 memset( mdl
, 0, sizeof( mdl_context
) );
172 mdl
->file
= fopen( path
, "rb" );
176 vg_error( "mdl_open('%s'): %s\n", path
, strerror(errno
) );
177 vg_fatal_error( "see above for details" );
180 u64 l
= fread( &mdl
->info
, sizeof(mdl_header
), 1, mdl
->file
);
182 mdl_load_fatal_corrupt( mdl
);
184 if( mdl
->info
.version
< MDL_VERSION_MIN
)
186 vg_warn( "For model: %s\n", path
);
187 vg_warn( " version: %u (min: %u, current: %u)\n",
188 mdl
->info
.version
, MDL_VERSION_MIN
, MDL_VERSION_NR
);
190 vg_fatal_error( "Legacy model version incompatable" );
193 mdl_load_array_file( mdl
, &mdl
->index
, &mdl
->info
.index
, lin_alloc
,
196 mdl_array
*pack
= mdl_find_array( mdl
, "pack" );
197 if( pack
) mdl
->pack_base_offset
= pack
->file_offset
;
198 else mdl
->pack_base_offset
= 0;
204 void mdl_close( mdl_context
*mdl
)
210 /* useful things you can do with the model */
212 void mdl_transform_m4x3( mdl_transform
*transform
, m4x3f mtx
)
214 q_m3x3( transform
->q
, mtx
);
215 v3_muls( mtx
[0], transform
->s
[0], mtx
[0] );
216 v3_muls( mtx
[1], transform
->s
[1], mtx
[1] );
217 v3_muls( mtx
[2], transform
->s
[2], mtx
[2] );
218 v3_copy( transform
->co
, mtx
[3] );
221 const char *mdl_pstr( mdl_context
*mdl
, u32 pstr
)
223 return ((char *)mdl_arritm( &mdl
->strings
, pstr
)) + 4;
227 int mdl_pstreq( mdl_context
*mdl
, u32 pstr
, const char *str
, u32 djb2
)
229 u32 hash
= *((u32
*)mdl_arritm( &mdl
->strings
, pstr
));
231 if( !strcmp( str
, mdl_pstr( mdl
, pstr
))) return 1;
238 * Simple mesh interface for OpenGL
239 * ----------------------------------------------------------------------------
242 static void mesh_upload( glmesh
*mesh
,
243 mdl_vert
*verts
, u32 vert_count
,
244 u32
*indices
, u32 indice_count
)
246 glGenVertexArrays( 1, &mesh
->vao
);
247 glGenBuffers( 1, &mesh
->vbo
);
248 glGenBuffers( 1, &mesh
->ebo
);
249 glBindVertexArray( mesh
->vao
);
251 size_t stride
= sizeof(mdl_vert
);
253 glBindBuffer( GL_ARRAY_BUFFER
, mesh
->vbo
);
254 glBufferData( GL_ARRAY_BUFFER
, vert_count
*stride
, verts
, GL_STATIC_DRAW
);
256 glBindVertexArray( mesh
->vao
);
257 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, mesh
->ebo
);
258 glBufferData( GL_ELEMENT_ARRAY_BUFFER
, indice_count
*sizeof(u32
),
259 indices
, GL_STATIC_DRAW
);
262 glVertexAttribPointer( 0, 3, GL_FLOAT
, GL_FALSE
, stride
, (void*)0 );
263 glEnableVertexAttribArray( 0 );
266 glVertexAttribPointer( 1, 3, GL_FLOAT
, GL_FALSE
,
267 stride
, (void *)offsetof(mdl_vert
, norm
) );
268 glEnableVertexAttribArray( 1 );
271 glVertexAttribPointer( 2, 2, GL_FLOAT
, GL_FALSE
,
272 stride
, (void *)offsetof(mdl_vert
, uv
) );
273 glEnableVertexAttribArray( 2 );
276 glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE
, GL_TRUE
,
277 stride
, (void *)offsetof(mdl_vert
, colour
) );
278 glEnableVertexAttribArray( 3 );
281 glVertexAttribPointer( 4, 4, GL_UNSIGNED_SHORT
, GL_TRUE
,
282 stride
, (void *)offsetof(mdl_vert
, weights
) );
283 glEnableVertexAttribArray( 4 );
286 glVertexAttribIPointer( 5, 4, GL_UNSIGNED_BYTE
,
287 stride
, (void *)offsetof(mdl_vert
, groups
) );
288 glEnableVertexAttribArray( 5 );
292 mesh
->indice_count
= indice_count
;
296 void mesh_bind( glmesh
*mesh
)
298 glBindVertexArray( mesh
->vao
);
301 void mesh_drawn( u32 start
, u32 count
)
303 glDrawElements( GL_TRIANGLES
, count
, GL_UNSIGNED_INT
,
304 (void *)(start
*sizeof(u32
)) );
307 void mesh_draw( glmesh
*mesh
)
309 mesh_drawn( 0, mesh
->indice_count
);
312 void mesh_free( glmesh
*mesh
)
316 glDeleteVertexArrays( 1, &mesh
->vao
);
317 glDeleteBuffers( 1, &mesh
->ebo
);
318 glDeleteBuffers( 1, &mesh
->vbo
);
323 void mdl_draw_submesh( mdl_submesh
*sm
)
325 mesh_drawn( sm
->indice_start
, sm
->indice_count
);
328 mdl_mesh
*mdl_find_mesh( mdl_context
*mdl
, const char *name
)
330 for( u32 i
=0; i
<mdl_arrcount( &mdl
->meshs
); i
++ )
332 mdl_mesh
*mesh
= mdl_arritm( &mdl
->meshs
, i
);
333 if( !strcmp( name
, mdl_pstr( mdl
, mesh
->pstr_name
)))
341 struct payload_glmesh_load
352 static void _sync_mdl_load_glmesh( void *payload
, u32 size
)
354 struct payload_glmesh_load
*job
= payload
;
355 mesh_upload( job
->mesh
, job
->verts
, job
->vertex_count
,
356 job
->indices
, job
->indice_count
);
359 void mdl_async_load_glmesh( mdl_context
*mdl
, glmesh
*mesh
, u32
*fixup_table
)
361 mdl_array
*arr_vertices
= mdl_find_array( mdl
, "mdl_vert" );
362 mdl_array
*arr_indices
= mdl_find_array( mdl
, "mdl_indice" );
364 if( arr_vertices
&& arr_indices
)
366 u32 size_verts
= vg_align8(sizeof(mdl_vert
)*arr_vertices
->item_count
),
367 size_indices
= vg_align8(sizeof(mdl_indice
)*arr_indices
->item_count
),
368 size_hdr
= vg_align8(sizeof(struct payload_glmesh_load
)),
369 total
= size_hdr
+ size_verts
+ size_indices
;
371 vg_async_item
*call
= vg_async_alloc( total
);
372 struct payload_glmesh_load
*job
= call
->payload
;
374 u8
*payload
= call
->payload
;
377 job
->verts
= (void*)(payload
+ size_hdr
);
378 job
->indices
= (void*)(payload
+ size_hdr
+ size_verts
);
379 job
->vertex_count
= arr_vertices
->item_count
;
380 job
->indice_count
= arr_indices
->item_count
;
382 mdl_load_array_file_buffer( mdl
, arr_vertices
,
383 job
->verts
, sizeof(mdl_vert
) );
384 mdl_load_array_file_buffer( mdl
, arr_indices
, job
->indices
,
385 sizeof(mdl_indice
) );
389 for( u32 i
=0; i
<job
->vertex_count
; i
++ )
391 mdl_vert
*vert
= &job
->verts
[i
];
393 for( u32 j
=0; j
<4; j
++ )
395 vert
->groups
[j
] = fixup_table
[vert
->groups
[j
]];
401 * Unpack the indices (if there are meshes)
402 * ---------------------------------------------------------
405 if( mdl_arrcount( &mdl
->submeshs
) )
407 mdl_submesh
*sm
= mdl_arritm( &mdl
->submeshs
, 0 );
408 u32 offset
= sm
->vertex_count
;
410 for( u32 i
=1; i
<mdl_arrcount( &mdl
->submeshs
); i
++ )
412 mdl_submesh
*sm
= mdl_arritm( &mdl
->submeshs
, i
);
413 u32
*indices
= job
->indices
+ sm
->indice_start
;
415 for( u32 j
=0; j
<sm
->indice_count
; j
++ )
416 indices
[j
] += offset
;
418 offset
+= sm
->vertex_count
;
424 * -------------------------
427 vg_async_dispatch( call
, _sync_mdl_load_glmesh
);
431 vg_fatal_error( "no vertex/indice data\n" );
435 /* uploads the glmesh, and textures. everything is saved into the mdl_context */
436 void mdl_async_full_load_std( mdl_context
*mdl
)
438 mdl_async_load_glmesh( mdl
, &mdl
->mesh
, NULL
);
440 for( u32 i
=0; i
<mdl_arrcount( &mdl
->textures
); i
++ )
442 vg_linear_clear( vg_mem
.scratch
);
443 mdl_texture
*tex
= mdl_arritm( &mdl
->textures
, i
);
445 void *data
= vg_linear_alloc( vg_mem
.scratch
, tex
->file
.pack_size
);
446 mdl_fread_pack_file( mdl
, &tex
->file
, data
);
448 vg_tex2d_load_qoi_async( data
, tex
->file
.pack_size
,
449 VG_TEX2D_CLAMP
|VG_TEX2D_NEAREST
, &tex
->glname
);