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