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