d067b8fd8e6e8fb69eb29e563b9056b812454b98
[carveJwlIkooP6JGAAIwe30JlM.git] / model.h
1 /*
2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 */
4
5 #ifndef MODEL_H
6 #define MODEL_H
7
8 #include "common.h"
9
10 typedef struct glmesh glmesh;
11
12 typedef struct mdl_vert mdl_vert;
13 typedef struct mdl_submesh mdl_submesh;
14 typedef struct mdl_material mdl_material;
15 typedef struct mdl_node mdl_node;
16 typedef struct mdl_header mdl_header;
17 typedef struct mdl_animation mdl_animation;
18 typedef struct mdl_keyframe mdl_keyframe;
19
20 #define MDL_SIZE_MAX 0x1000000
21 #define MDL_VERT_MAX 1000000
22 #define MDL_INDICE_MAX 1000000
23 #define MDL_MATERIAL_MAX 32
24 #define MDL_NODE_MAX 4000
25 #define MDL_SUBMESH_MAX 8000
26 #define MDL_STRING_LENGTH_MAX 64
27
28 enum classtype
29 {
30 k_classtype_none = 0,
31 k_classtype_gate = 1,
32 k_classtype_block = 2,
33 k_classtype_spawn = 3,
34 k_classtype_water = 4,
35 k_classtype_car_path = 5,
36 k_classtype_instance = 6,
37 k_classtype_capsule = 7,
38 k_classtype_route_node = 8,
39 k_classtype_route = 9,
40 k_classtype_bone = 10,
41 k_classtype_skeleton = 11,
42 k_classtype_skin = 12,
43 k_classtype_achievement_box = 13
44 };
45
46
47 #pragma pack(push,1)
48
49 struct mdl_vert
50 {
51 v3f co,
52 norm;
53 v2f uv;
54 u8 colour[4];
55 u16 weights[4];
56 u8 groups[4];
57 };
58
59 struct mdl_submesh
60 {
61 u32 indice_start,
62 indice_count,
63 vertex_start,
64 vertex_count;
65
66 boxf bbx;
67 u32 material_id;
68 };
69
70 struct mdl_material
71 {
72 u32 pstr_name;
73 };
74
75 struct mdl_node
76 {
77 v3f co;
78 v4f q;
79 v3f s;
80
81 union{ u32 submesh_start, sub_uid; };
82
83 u32
84 submesh_count,
85 classtype,
86 offset,
87 parent,
88 pstr_name;
89 };
90
91 struct mdl_keyframe
92 {
93 v3f co;
94 v4f q;
95 v3f s;
96 };
97
98 struct mdl_animation
99 {
100 u32 pstr_name,
101 length;
102
103 float rate;
104
105 u32 offset;
106 };
107
108 struct mdl_header
109 {
110 u32 identifier, version, file_length;
111
112 u32 vertex_count, vertex_offset,
113 indice_count, indice_offset,
114 submesh_count, submesh_offset,
115 material_count, material_offset,
116 node_count, node_offset,
117 anim_count, anim_offset,
118 strings_offset, entdata_offset, animdata_offset;
119 };
120
121 /*
122 * Entity data structures
123 */
124
125 struct classtype_block
126 {
127 boxf bbx;
128 };
129
130 struct classtype_gate
131 {
132 u32 target;
133 v3f dims;
134 };
135
136 struct classtype_spawn
137 {
138 u32 target;
139 };
140
141 struct classtype_water
142 {
143 u32 temp;
144 };
145
146 struct classtype_car_path
147 {
148 u32 target, target1;
149 };
150
151 struct classtype_instance
152 {
153 u32 pstr_file;
154 };
155
156 struct classtype_capsule
157 {
158 float height, radius;
159 };
160
161 struct classtype_route_node
162 {
163 u32 target, target1;
164 };
165
166 struct classtype_route
167 {
168 u32 id_start;
169 v3f colour;
170 };
171
172 struct classtype_bone
173 {
174 u32 deform,
175 ik_target,
176 ik_pole,
177 collider,
178 use_limits;
179
180 v3f angle_limits[2];
181 boxf hitbox;
182 };
183
184 struct classtype_skeleton
185 {
186 u32 channels,
187 ik_count,
188 collider_count,
189 anim_start,
190 anim_count;
191 };
192
193 struct classtype_skin
194 {
195 u32 skeleton;
196 };
197
198 struct classtype_achievement_box
199 {
200 u32 pstr_name;
201 };
202
203 #pragma pack(pop)
204
205 /*
206 * Simple mesh interface for OpenGL
207 */
208
209 struct glmesh
210 {
211 GLuint vao, vbo, ebo;
212 u32 indice_count;
213 u32 loaded;
214 };
215
216 static void mesh_upload( glmesh *mesh,
217 mdl_vert *verts, u32 vert_count,
218 u32 *indices, u32 indice_count )
219 {
220 //assert( mesh->loaded == 0 );
221
222 glGenVertexArrays( 1, &mesh->vao );
223 glGenBuffers( 1, &mesh->vbo );
224 glGenBuffers( 1, &mesh->ebo );
225 glBindVertexArray( mesh->vao );
226
227 size_t stride = sizeof(mdl_vert);
228
229 glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo );
230 glBufferData( GL_ARRAY_BUFFER, vert_count*stride, verts, GL_STATIC_DRAW );
231
232 glBindVertexArray( mesh->vao );
233 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mesh->ebo );
234 glBufferData( GL_ELEMENT_ARRAY_BUFFER, indice_count*sizeof(u32),
235 indices, GL_STATIC_DRAW );
236
237 /* 0: coordinates */
238 glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0 );
239 glEnableVertexAttribArray( 0 );
240
241 /* 1: normal */
242 glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE,
243 stride, (void *)offsetof(mdl_vert, norm) );
244 glEnableVertexAttribArray( 1 );
245
246 /* 2: uv */
247 glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE,
248 stride, (void *)offsetof(mdl_vert, uv) );
249 glEnableVertexAttribArray( 2 );
250
251 /* 3: colour */
252 glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE, GL_TRUE,
253 stride, (void *)offsetof(mdl_vert, colour) );
254 glEnableVertexAttribArray( 3 );
255
256 /* 4: weights */
257 glVertexAttribPointer( 4, 4, GL_UNSIGNED_SHORT, GL_TRUE,
258 stride, (void *)offsetof(mdl_vert, weights) );
259 glEnableVertexAttribArray( 4 );
260
261 /* 5: groups */
262 glVertexAttribIPointer( 5, 4, GL_UNSIGNED_BYTE,
263 stride, (void *)offsetof(mdl_vert, groups) );
264 glEnableVertexAttribArray( 5 );
265
266 VG_CHECK_GL_ERR();
267
268 mesh->indice_count = indice_count;
269 mesh->loaded = 1;
270 }
271
272 static void mesh_bind( glmesh *mesh )
273 {
274 glBindVertexArray( mesh->vao );
275 }
276
277 static void mesh_drawn( u32 start, u32 count )
278 {
279 glDrawElements( GL_TRIANGLES, count, GL_UNSIGNED_INT,
280 (void *)(start*sizeof(u32)) );
281 }
282
283 static void mesh_draw( glmesh *mesh )
284 {
285 mesh_drawn( 0, mesh->indice_count );
286 }
287
288 static void mesh_free( glmesh *mesh )
289 {
290 if( mesh->loaded )
291 {
292 glDeleteVertexArrays( 1, &mesh->vao );
293 glDeleteBuffers( 1, &mesh->ebo );
294 glDeleteBuffers( 1, &mesh->vbo );
295 }
296 }
297
298
299 /*
300 * Model implementation
301 */
302
303 static mdl_header *mdl_load( const char *path )
304 {
305 i64 size;
306 mdl_header *header = vg_asset_read_s( path, &size );
307
308 /*
309 * Check file is valid
310 */
311 if( !header )
312 {
313 vg_error( "Could not open '%s'\n", path );
314 return NULL;
315 }
316
317 if( size < sizeof(mdl_header) )
318 {
319 vg_free( header );
320 vg_error( "Invalid file '%s' (too small for header)\n", path );
321 return NULL;
322 }
323
324 if( header->file_length != size )
325 {
326 vg_error( "Invalid file '%s'"
327 "(wrong .file_length, %ub != real file size %ub)\n",
328 path, header->file_length, size );
329 vg_free( header );
330 return NULL;
331 }
332
333 /*
334 * Validate offsets and memory sections, to ensure all arrays are in-bounds,
335 * and that they do not overlap.
336 */
337
338 struct memregion
339 {
340 const char *desc;
341 u32 count, max_count, size, offset;
342 }
343 regions[] = {
344 {
345 "Vertices",
346 header->vertex_count, MDL_VERT_MAX,
347 sizeof(mdl_vert), header->vertex_offset
348 },
349 {
350 "Indices",
351 header->indice_count, MDL_INDICE_MAX,
352 sizeof(u32), header->indice_offset
353 },
354 {
355 "Submesh",
356 header->submesh_count, MDL_SUBMESH_MAX,
357 sizeof(mdl_submesh), header->submesh_offset
358 },
359 {
360 "Materials",
361 header->material_count, MDL_MATERIAL_MAX,
362 sizeof(mdl_material), header->material_offset
363 },
364 {
365 "Nodes",
366 header->node_count, MDL_NODE_MAX,
367 sizeof(mdl_node), header->node_count
368 }
369 };
370
371 for( int i=0; i<vg_list_size(regions); i++ )
372 {
373 struct memregion *ri = &regions[i];
374
375 if( ri->count == 0 )
376 continue;
377
378 if( ri->count > ri->max_count )
379 {
380 vg_free( header );
381 vg_error( "'%s': '%s' buffer exceeds the maximum (%u/%u)\n",
382 path, ri->desc, ri->count, ri->max_count );
383 return NULL;
384 }
385
386 if( ri->offset >= header->file_length )
387 {
388 vg_free( header );
389 vg_error( "'%s': '%s' buffer offset is out of range\n",
390 path, ri->desc );
391 return NULL;
392 }
393
394 if( ri->offset + ri->size*ri->count > header->file_length )
395 {
396 vg_free( header );
397 vg_error( "'%s': '%s' buffer size is out of range\n",
398 path, ri->desc );
399 return NULL;
400 }
401
402 for( int j=0; j<vg_list_size(regions); j++ )
403 {
404 struct memregion *rj = &regions[j];
405 if( rj->count == 0 )
406 continue;
407
408 if( ri->offset >= rj->offset &&
409 (ri->offset+ri->size*ri->count < rj->offset+rj->size*rj->count))
410 {
411 vg_free( header );
412 vg_error( "'%s': '%s' buffer overlaps '%s'\n",
413 path, ri->desc, rj->desc );
414 return NULL;
415 }
416 }
417 }
418
419 /*
420 * Pointer validation TODO(workshop)
421 */
422
423 /*
424 * strings TODO(workshop)
425 */
426
427 return header;
428 }
429
430 static void *mdl_baseptr( mdl_header *mdl, u32 offset )
431 {
432 return (void *)mdl + offset;
433 }
434
435 static const char *mdl_pstr( mdl_header *mdl, u32 pstr )
436 {
437 return (const char *)(mdl_baseptr( mdl, mdl->strings_offset )) + pstr;
438 }
439
440 static mdl_node *mdl_node_from_id( mdl_header *mdl, u32 id )
441 {
442 return ((mdl_node *)mdl_baseptr( mdl, mdl->node_offset )) + id;
443 }
444
445 static mdl_node *mdl_node_from_name( mdl_header *mdl, const char *name )
446 {
447 for( int i=0; i<mdl->node_count; i++ )
448 {
449 mdl_node *pnode = mdl_node_from_id( mdl, i );
450
451 if( !strcmp( name, mdl_pstr( mdl, pnode->pstr_name )) )
452 return pnode;
453 }
454
455 return NULL;
456 }
457
458 static mdl_submesh *mdl_submesh_from_id( mdl_header *mdl, u32 id )
459 {
460 if( id >= mdl->submesh_count )
461 return NULL;
462
463 return ((mdl_submesh *)mdl_baseptr( mdl, mdl->submesh_offset )) + id;
464 }
465
466 static mdl_submesh *mdl_node_submesh( mdl_header *mdl, mdl_node *node, u32 i )
467 {
468 if( i >= node->submesh_count )
469 return NULL;
470
471 return mdl_submesh_from_id( mdl, node->submesh_start+i );
472 }
473
474 static u32 *mdl_submesh_indices( mdl_header *mdl, mdl_submesh *sm )
475 {
476 return ((u32 *)mdl_baseptr( mdl, mdl->indice_offset )) + sm->indice_start;
477 }
478
479 static mdl_vert *mdl_submesh_vertices( mdl_header *mdl, mdl_submesh *sm )
480 {
481 return ((mdl_vert *)mdl_baseptr(mdl,mdl->vertex_offset)) + sm->vertex_start;
482 }
483
484 static mdl_material *mdl_material_from_id( mdl_header *mdl, u32 id )
485 {
486 return ((mdl_material *)mdl_baseptr(mdl,mdl->material_offset)) + id;
487 }
488
489 static mdl_animation *mdl_animation_from_id( mdl_header *mdl, u32 id )
490 {
491 return ((mdl_animation *)mdl_baseptr(mdl,mdl->anim_offset)) + id;
492 }
493
494 static void mdl_node_transform( mdl_node *pnode, m4x3f transform )
495 {
496 q_m3x3( pnode->q, transform );
497 v3_muls( transform[0], pnode->s[0], transform[0] );
498 v3_muls( transform[1], pnode->s[1], transform[1] );
499 v3_muls( transform[2], pnode->s[2], transform[2] );
500 v3_copy( pnode->co, transform[3] );
501 }
502
503 static void mdl_unpack_submesh( mdl_header *mdl, glmesh *mesh, mdl_submesh *sm )
504 {
505 mesh_upload( mesh, mdl_submesh_vertices( mdl, sm ), sm->vertex_count,
506 mdl_submesh_indices( mdl, sm ), sm->indice_count );
507 }
508
509 static void mdl_unpack_glmesh( mdl_header *mdl, glmesh *mesh )
510 {
511 u32 offset = mdl_submesh_from_id( mdl, 0 )->vertex_count;
512
513 for( int i=1; i< mdl->submesh_count; i++ )
514 {
515 mdl_submesh *sm = mdl_submesh_from_id( mdl, i );
516 u32 *indices = mdl_submesh_indices( mdl, sm );
517
518 for( u32 j=0; j<sm->indice_count; j++ )
519 indices[j] += offset;
520
521 offset += sm->vertex_count;
522 }
523
524 mdl_vert *vertex_base = mdl_baseptr( mdl, mdl->vertex_offset );
525 u32 *indice_base = mdl_baseptr( mdl, mdl->indice_offset );
526
527 mesh_upload( mesh, vertex_base, mdl->vertex_count,
528 indice_base, mdl->indice_count );
529 }
530
531 static void mdl_draw_submesh( mdl_submesh *sm )
532 {
533 mesh_drawn( sm->indice_start, sm->indice_count );
534 }
535
536 static void *mdl_get_entdata( mdl_header *mdl, mdl_node *pnode )
537 {
538 return mdl_baseptr( mdl, mdl->entdata_offset ) + pnode->offset;
539 }
540
541 static mdl_keyframe *mdl_get_animdata( mdl_header *mdl, mdl_animation *anim )
542 {
543 return mdl_baseptr( mdl, mdl->animdata_offset ) + anim->offset;
544 }
545
546 static void mdl_link_materials( mdl_header *root, mdl_header *child )
547 {
548 u32 lookup[MDL_MATERIAL_MAX];
549
550 for( int i=0; i<child->material_count; i++ )
551 {
552 mdl_material *mi = mdl_material_from_id( child, i );
553 const char *si = mdl_pstr( child, mi->pstr_name );
554
555 lookup[i] = 0;
556
557 for( int j=0; j<root->material_count; j++ )
558 {
559 mdl_material *mj = mdl_material_from_id( root, j );
560 const char *sj = mdl_pstr( root, mj->pstr_name );
561
562 if( !strcmp( si, sj ) )
563 {
564 lookup[i] = j;
565 break;
566 }
567 }
568
569 if( lookup[i] == 0 && i != 0 )
570 {
571 vg_warn( "Could not link material '%s' (not present in root model)\n",
572 si );
573 }
574 }
575
576 for( int i=0; i<child->submesh_count; i++ )
577 {
578 mdl_submesh *sm = mdl_submesh_from_id( child, i );
579 sm->material_id = lookup[sm->material_id];
580 }
581 }
582
583
584 #endif