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