routes
[carveJwlIkooP6JGAAIwe30JlM.git] / model.h
diff --git a/model.h b/model.h
index 467ad989bffab99637ea0e1e00db7a4ed7aa7bce..f8b09b85fcbdb2af8f8d5ae6f0753b6114bdc0dc 100644 (file)
--- a/model.h
+++ b/model.h
@@ -1,36 +1,35 @@
 #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 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,
@@ -38,44 +37,105 @@ struct submodel
        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;
+};
+
+struct mdl_node
+{
+   v3f co;
+   v4f q;
+   v3f s;
+   
+   union{ u32 submesh_start, sub_uid; };
+   u32 
+       submesh_count,
+       classtype,
+       offset,
+       pstr_name;
+};
 
-   char name[32];
+struct mdl_header
+{
+   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;
 };
 
-struct model_vert
+/* 
+ * Entity data structures
+ */
+
+struct classtype_block
 {
-   v3f co,
-       norm;
-   v4f colour;
-   v2f uv;
+   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 );
@@ -83,29 +143,29 @@ static void mesh_upload( glmesh *mesh,
    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();
@@ -128,60 +188,227 @@ static void mesh_draw( glmesh *mesh )
    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 = &regions[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 = &regions[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 void model_unpack( model *model, glmesh *mesh )
+static u32 *mdl_submesh_indices( mdl_header *mdl, mdl_submesh *sm )
 {
-   u32 offset = model_get_submodel( model, 0 )->vertex_count;
+   return ((u32 *)mdl_baseptr( mdl, mdl->indice_offset )) + sm->indice_start;
+}
 
-   for( int i=1; i<model->layer_count; i++ )
+static mdl_vert *mdl_submesh_vertices( mdl_header *mdl, mdl_submesh *sm )
+{
+   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;
+}
+
+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;
@@ -189,15 +416,59 @@ static void model_unpack( model *model, glmesh *mesh )
       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 mesh_free( glmesh *mesh )
+static void mdl_draw_submesh( mdl_submesh *sm )
 {
-   glDeleteVertexArrays( 1, &mesh->vao );
-   glDeleteBuffers( 1, &mesh->ebo );
-   glDeleteBuffers( 1, &mesh->vbo );
+   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