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