#ifndef MODEL_H
#define MODEL_H
-#include "vg/vg.h"
+#include "common.h"
-typedef struct model model;
typedef struct glmesh glmesh;
-typedef struct submodel submodel;
-typedef struct model_vert model_vert;
-typedef struct scene scene;
-typedef struct sdf_primative sdf_primative;
-typedef enum esdf_type esdf_type;
-#pragma pack(push,1)
-struct model
-{
- u32 identifier;
+typedef struct mdl_vert mdl_vert;
+typedef struct mdl_submesh mdl_submesh;
+typedef struct mdl_material mdl_material;
+typedef struct mdl_node mdl_node;
+typedef struct mdl_header mdl_header;
- u32 vertex_count,
- indice_count,
- layer_count;
-};
+#define MDL_SIZE_MAX 0x1000000
+#define MDL_VERT_MAX 1000000
+#define MDL_INDICE_MAX 1000000
+#define MDL_MATERIAL_MAX 32
+#define MDL_NODE_MAX 4000
+#define MDL_SUBMESH_MAX 8000
+#define MDL_STRING_LENGTH_MAX 64
-struct sdf_primative
+#pragma pack(push,1)
+
+struct mdl_vert
{
- v4f origin; /* xyz, yaw */
- /* Cone:
- x base scale
- y height
- */
- v4f info;
+ v3f co,
+ norm;
+ v4f colour;
+ v2f uv;
};
-struct submodel
+struct mdl_submesh
{
u32 indice_start,
indice_count,
vertex_count;
boxf bbx;
- v3f pivot;
- sdf_primative sdf;
+ u32 material_id;
+};
- enum esdf_type
- {
- k_sdf_none = 0,
- k_sdf_cone,
- k_sdf_sphere,
- k_sdf_box
- }
- sdf_type;
+struct mdl_material
+{
+ u32 pstr_name;
+};
- char name[32];
+struct mdl_node
+{
+ v3f co;
+ v4f q;
+ v3f s;
+
+ union{ u32 submesh_start, sub_uid; };
+ u32
+ submesh_count,
+ classtype,
+ offset,
+ pstr_name;
};
-struct model_vert
+struct mdl_header
{
- v3f co,
- norm;
- v4f colour;
- v2f uv;
+ u32 identifier, version, file_length;
+
+ u32 vertex_count, vertex_offset,
+ indice_count, indice_offset,
+ submesh_count, submesh_offset,
+ material_count, material_offset,
+ node_count, node_offset,
+ strings_offset, entdata_offset;
};
+
+/*
+ * Entity data structures
+ */
+
+struct classtype_block
+{
+ boxf bbx;
+};
+
+struct classtype_gate
+{
+ u32 target;
+ v3f dims;
+};
+
+struct classtype_spawn
+{
+ u32 target;
+};
+
+struct classtype_water
+{
+ u32 temp;
+};
+
+struct classtype_car_path
+{
+ u32 target, target1;
+};
+
+struct classtype_instance
+{
+ u32 pstr_file;
+};
+
+struct classtype_capsule
+{
+ float height, radius;
+};
+
+struct classtype_route_node
+{
+ u32 target, target1;
+};
+
+struct classtype_route
+{
+ u32 pstr_name;
+ u32 id_start;
+};
+
#pragma pack(pop)
+/*
+ * Simple mesh interface for OpenGL
+ */
+
struct glmesh
{
GLuint vao, vbo, ebo;
u32 indice_count;
};
-#define VERTEX_STANDARD_ATTRIBUTES \
- "layout (location=0) in vec3 a_co;" \
- "layout (location=1) in vec3 a_norm;" \
- "layout (location=2) in vec4 a_colour;" \
- "layout (location=3) in vec2 a_uv;"
-
static void mesh_upload( glmesh *mesh,
- model_vert *verts, u32 vert_count,
+ mdl_vert *verts, u32 vert_count,
u32 *indices, u32 indice_count )
{
glGenVertexArrays( 1, &mesh->vao );
glGenBuffers( 1, &mesh->ebo );
glBindVertexArray( mesh->vao );
+ size_t stride = sizeof(mdl_vert);
+
glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo );
- glBufferData( GL_ARRAY_BUFFER, vert_count*sizeof(model_vert),
- verts, GL_STATIC_DRAW );
+ glBufferData( GL_ARRAY_BUFFER, vert_count*stride, verts, GL_STATIC_DRAW );
glBindVertexArray( mesh->vao );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mesh->ebo );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, indice_count*sizeof(u32),
indices, GL_STATIC_DRAW );
- glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE,
- sizeof(model_vert), (void*)0 );
+ glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0 );
glEnableVertexAttribArray( 0 );
glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE,
- sizeof(model_vert), (void *)offsetof(model_vert, norm) );
+ stride, (void *)offsetof(mdl_vert, norm) );
glEnableVertexAttribArray( 1 );
glVertexAttribPointer( 2, 4, GL_FLOAT, GL_FALSE,
- sizeof(model_vert), (void *)offsetof(model_vert, colour) );
+ stride, (void *)offsetof(mdl_vert, colour) );
glEnableVertexAttribArray( 2 );
glVertexAttribPointer( 3, 2, GL_FLOAT, GL_FALSE,
- sizeof(model_vert), (void *)offsetof(model_vert, uv) );
+ stride, (void *)offsetof(mdl_vert, uv) );
glEnableVertexAttribArray( 3 );
VG_CHECK_GL();
mesh_drawn( 0, mesh->indice_count );
}
-/*
- * Helper functions for file offsets
- */
-static submodel *model_get_submodel( model *mdl, int id )
+static void mesh_free( glmesh *mesh )
{
- return ((submodel*)(mdl+1)) + id;
+ glDeleteVertexArrays( 1, &mesh->vao );
+ glDeleteBuffers( 1, &mesh->ebo );
+ glDeleteBuffers( 1, &mesh->vbo );
}
-static model_vert *model_vertex_base( model *mdl )
+
+/*
+ * Model implementation
+ */
+
+static mdl_header *mdl_load( const char *path )
{
- return (model_vert *)model_get_submodel( mdl, mdl->layer_count );
+ i64 size;
+ mdl_header *header = vg_asset_read_s( path, &size );
+
+ /*
+ * Check file is valid
+ */
+ if( !header )
+ {
+ vg_error( "Could not open '%s'\n", path );
+ return NULL;
+ }
+
+ if( size < sizeof(mdl_header) )
+ {
+ free( header );
+ vg_error( "Invalid file '%s' (too small for header)\n", path );
+ return NULL;
+ }
+
+ if( header->file_length != size )
+ {
+ vg_error( "Invalid file '%s'"
+ "(wrong .file_length, %ub != real file size %ub)\n",
+ path, header->file_length, size );
+ free( header );
+ return NULL;
+ }
+
+ /*
+ * Validate offsets and memory sections, to ensure all arrays are in-bounds,
+ * and that they do not overlap.
+ */
+
+ struct memregion
+ {
+ const char *desc;
+ u32 count, max_count, size, offset;
+ }
+ regions[] = {
+ {
+ "Vertices",
+ header->vertex_count, MDL_VERT_MAX,
+ sizeof(mdl_vert), header->vertex_offset
+ },
+ {
+ "Indices",
+ header->indice_count, MDL_INDICE_MAX,
+ sizeof(u32), header->indice_offset
+ },
+ {
+ "Submesh",
+ header->submesh_count, MDL_SUBMESH_MAX,
+ sizeof(mdl_submesh), header->submesh_offset
+ },
+ {
+ "Materials",
+ header->material_count, MDL_MATERIAL_MAX,
+ sizeof(mdl_material), header->material_offset
+ },
+ {
+ "Nodes",
+ header->node_count, MDL_NODE_MAX,
+ sizeof(mdl_node), header->node_count
+ }
+ };
+
+ for( int i=0; i<vg_list_size(regions); i++ )
+ {
+ struct memregion *ri = ®ions[i];
+
+ if( ri->count == 0 )
+ continue;
+
+ if( ri->count > ri->max_count )
+ {
+ free( header );
+ vg_error( "'%s': '%s' buffer exceeds the maximum (%u/%u)\n",
+ path, ri->desc, ri->count, ri->max_count );
+ return NULL;
+ }
+
+ if( ri->offset >= header->file_length )
+ {
+ free( header );
+ vg_error( "'%s': '%s' buffer offset is out of range\n",
+ path, ri->desc );
+ return NULL;
+ }
+
+ if( ri->offset + ri->size*ri->count > header->file_length )
+ {
+ free( header );
+ vg_error( "'%s': '%s' buffer size is out of range\n",
+ path, ri->desc );
+ return NULL;
+ }
+
+ for( int j=0; j<vg_list_size(regions); j++ )
+ {
+ struct memregion *rj = ®ions[j];
+ if( rj->count == 0 )
+ continue;
+
+ if( ri->offset >= rj->offset &&
+ (ri->offset+ri->size*ri->count < rj->offset+rj->size*rj->count))
+ {
+ free( header );
+ vg_error( "'%s': '%s' buffer overlaps '%s'\n",
+ path, ri->desc, rj->desc );
+ return NULL;
+ }
+ }
+ }
+
+ /*
+ * Pointer validation TODO(workshop)
+ */
+
+ /*
+ * strings TODO(workshop)
+ */
+
+ return header;
}
-static u32 *model_indice_base( model *mdl )
+static void *mdl_baseptr( mdl_header *mdl, u32 offset )
{
- return (u32 *)(model_vertex_base( mdl ) + mdl->vertex_count);
+ return (void *)mdl + offset;
}
-static model_vert *submodel_vert_data( model *mdl, submodel *sub )
+static const char *mdl_pstr( mdl_header *mdl, u32 pstr )
{
- return model_vertex_base(mdl) + sub->vertex_start;
+ return (const char *)(mdl_baseptr( mdl, mdl->strings_offset )) + pstr;
}
-static u32 *submodel_indice_data( model *mdl, submodel *sub )
+static mdl_node *mdl_node_from_id( mdl_header *mdl, u32 id )
{
- return model_indice_base(mdl) + sub->indice_start;
+ return ((mdl_node *)mdl_baseptr( mdl, mdl->node_offset )) + id;
}
-static submodel *submodel_get( model *mdl, const char *name )
+static mdl_node *mdl_node_from_name( mdl_header *mdl, const char *name )
{
- for( int i=0; i<mdl->layer_count; i++ )
+ for( int i=0; i<mdl->node_count; i++ )
{
- submodel *pmdl =model_get_submodel(mdl,i);
-
- if( !strcmp( pmdl->name, name ) )
- return pmdl;
+ mdl_node *pnode = mdl_node_from_id( mdl, i );
+
+ if( !strcmp( name, mdl_pstr( mdl, pnode->pstr_name )) )
+ return pnode;
}
return NULL;
}
-static void submodel_draw( submodel *sm )
+static mdl_submesh *mdl_submesh_from_id( mdl_header *mdl, u32 id )
{
- mesh_drawn( sm->indice_start, sm->indice_count );
+ if( id >= mdl->submesh_count )
+ return NULL;
+
+ return ((mdl_submesh *)mdl_baseptr( mdl, mdl->submesh_offset )) + id;
+}
+
+static mdl_submesh *mdl_node_submesh( mdl_header *mdl, mdl_node *node, u32 i )
+{
+ if( i >= node->submesh_count )
+ return NULL;
+
+ return mdl_submesh_from_id( mdl, node->submesh_start+i );
+}
+
+static u32 *mdl_submesh_indices( mdl_header *mdl, mdl_submesh *sm )
+{
+ return ((u32 *)mdl_baseptr( mdl, mdl->indice_offset )) + sm->indice_start;
}
-static void model_unpack( model *model, glmesh *mesh )
+static mdl_vert *mdl_submesh_vertices( mdl_header *mdl, mdl_submesh *sm )
{
- u32 offset = model_get_submodel( model, 0 )->vertex_count;
+ return ((mdl_vert *)mdl_baseptr(mdl,mdl->vertex_offset)) + sm->vertex_start;
+}
+
+static mdl_material *mdl_material_from_id( mdl_header *mdl, u32 id )
+{
+ return ((mdl_material *)mdl_baseptr(mdl,mdl->material_offset)) + id;
+}
- for( int i=1; i<model->layer_count; i++ )
+static void mdl_node_transform( mdl_node *pnode, m4x3f transform )
+{
+ q_m3x3( pnode->q, transform );
+ v3_muls( transform[0], pnode->s[0], transform[0] );
+ v3_muls( transform[1], pnode->s[1], transform[1] );
+ v3_muls( transform[2], pnode->s[2], transform[2] );
+ v3_copy( pnode->co, transform[3] );
+}
+
+static void mdl_unpack_submesh( mdl_header *mdl, glmesh *mesh, mdl_submesh *sm )
+{
+ mesh_upload( mesh, mdl_submesh_vertices( mdl, sm ), sm->vertex_count,
+ mdl_submesh_indices( mdl, sm ), sm->indice_count );
+}
+
+static void mdl_unpack_glmesh( mdl_header *mdl, glmesh *mesh )
+{
+ u32 offset = mdl_submesh_from_id( mdl, 0 )->vertex_count;
+
+ for( int i=1; i< mdl->submesh_count; i++ )
{
- submodel *sm = model_get_submodel( model, i );
- u32 *indices = submodel_indice_data( model, sm );
+ mdl_submesh *sm = mdl_submesh_from_id( mdl, i );
+ u32 *indices = mdl_submesh_indices( mdl, sm );
for( u32 j=0; j<sm->indice_count; j++ )
indices[j] += offset;
offset += sm->vertex_count;
}
- mesh_upload( mesh, model_vertex_base( model ), model->vertex_count,
- model_indice_base( model ), model->indice_count );
+ mdl_vert *vertex_base = mdl_baseptr( mdl, mdl->vertex_offset );
+ u32 *indice_base = mdl_baseptr( mdl, mdl->indice_offset );
+
+ mesh_upload( mesh, vertex_base, mdl->vertex_count,
+ indice_base, mdl->indice_count );
+}
+
+static void mdl_draw_submesh( mdl_submesh *sm )
+{
+ mesh_drawn( sm->indice_start, sm->indice_count );
+}
+
+static void *mdl_get_entdata( mdl_header *mdl, mdl_node *pnode )
+{
+ return mdl_baseptr( mdl, mdl->entdata_offset ) + pnode->offset;
+}
+
+static void mdl_link_materials( mdl_header *root, mdl_header *child )
+{
+ u32 lookup[MDL_MATERIAL_MAX];
+
+ for( int i=0; i<child->material_count; i++ )
+ {
+ mdl_material *mi = mdl_material_from_id( child, i );
+ const char *si = mdl_pstr( child, mi->pstr_name );
+
+ lookup[i] = 0;
+
+ for( int j=0; j<root->material_count; j++ )
+ {
+ mdl_material *mj = mdl_material_from_id( root, j );
+ const char *sj = mdl_pstr( root, mj->pstr_name );
+
+ if( !strcmp( si, sj ) )
+ {
+ lookup[i] = j;
+ break;
+ }
+ }
+
+ if( lookup[i] == 0 && i != 0 )
+ {
+ vg_warn( "Could not link material '%s' (not present in root model)\n",
+ si );
+ }
+ }
+
+ for( int i=0; i<child->submesh_count; i++ )
+ {
+ mdl_submesh *sm = mdl_submesh_from_id( child, i );
+ sm->material_id = lookup[sm->material_id];
+ }
}
+
#endif