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