largely loadable model assets
authorhgn <hgodden00@gmail.com>
Mon, 2 May 2022 00:29:06 +0000 (01:29 +0100)
committerhgn <hgodden00@gmail.com>
Mon, 2 May 2022 00:29:06 +0000 (01:29 +0100)
__init__.py
cxr/cxr.h
cxr/cxr_io.h
cxr/cxr_valve_bin.h
cxr/test.c

index 9eba53c76808714d79414d3a69485091795a47ea..8d9254e69b49a1ba2e5971712fbc4f2f4bb173a8 100644 (file)
@@ -53,6 +53,13 @@ cxr_jobs_batch = None
 cxr_jobs_inf = []
 cxr_error_inf = None
 
+cxr_asset_lib = \
+{
+   "models": {},
+   "materials": {},
+   "textures": {}
+}
+
 # Shaders
 cxr_view_shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
 
@@ -96,7 +103,7 @@ void main()
    vec3 worldPos = pWorldPos.xyz;
 
    gl_Position = viewProjectionMatrix * pWorldPos;
-   lNormal = aNormal; //mat3(transpose(inverse(modelMatrix))) * aNormal;
+   lNormal = normalize(mat3(transpose(inverse(modelMatrix))) * aNormal);
    lPos = worldPos;
 }
 ""","""
@@ -419,6 +426,33 @@ class cxr_vmf_context(Structure):
                ("entity_count",c_int32),
                ("face_count",c_int32)]
 
+# Valve wrapper types
+class fs_locator(Structure):
+   _fields_ = [("vpk_entry",c_void_p),
+               ("path",c_char_p*1024)]
+
+class valve_material(Structure):
+   _fields_ = [("basetexture",c_char_p),
+               ("bumpmap",c_char_p)]
+
+class valve_model_batch(Structure):
+   _fields_ = [("material",c_uint32),
+               ("ibstart",c_uint32),
+               ("ibcount",c_uint32)]
+
+class valve_model(Structure):
+   _fields_ = [("vertex_data",POINTER(c_float)),
+               ("indices",POINTER(c_uint32)),
+               ("indices_count",c_uint32),
+               ("vertex_count",c_uint32),
+               ("part_count",c_uint32),
+               ("material_count",c_uint32),
+               ("materials",POINTER(c_char_p)),
+               ("parts",POINTER(valve_model_batch)),
+               ("studiohdr",c_void_p),
+               ("vtxhdr",c_void_p),
+               ("vvdhdr",c_void_p)]
+
 # Convert blenders mesh format into CXR's static format (they are very similar)
 #
 def mesh_cxr_format(obj):
@@ -593,17 +627,30 @@ libcxr_lightpatch_bsp = extern( "cxr_lightpatch_bsp", [c_char_p], None )
 # Binary file formats and FS
 libcxr_fs_set_gameinfo = extern( "cxr_fs_set_gameinfo", [c_char_p], c_int32 )
 libcxr_fs_exit = extern( "cxr_fs_exit", [], None )
-libcxr_fs_get = extern( "cxr_fs_get", [c_char_p], c_char_p )
-libcxr_load_mdl = extern( "cxr_load_mdl", [c_char_p], POINTER(cxr_tri_mesh) )
+libcxr_fs_get = extern( "cxr_fs_get", [c_char_p, c_int32], c_char_p )
+libcxr_fs_find = extern( "cxr_fs_find", [c_char_p, POINTER(fs_locator)],\
+      c_int32 )
+
+libcxr_valve_load_model = extern( "valve_load_model", [c_char_p], \
+      POINTER(valve_model) )
+libcxr_valve_free_model = extern( "valve_free_model", [POINTER(valve_model)],\
+      None )
+
+libcxr_valve_load_material = extern( "valve_load_material", [c_char_p], \
+      POINTER(valve_material) )
+libcxr_valve_free_material = extern( "valve_free_material", \
+      [POINTER(valve_material)], None )
 
 libcxr_funcs = [ libcxr_decompose, libcxr_free_world, libcxr_begin_vmf, \
                  libcxr_vmf_begin_entities, libcxr_push_world_vmf, \
                  libcxr_end_vmf, libcxr_vdf_open, libcxr_vdf_close, \
-                 libcxr_vdf_put, libcxr_vdf_node, libcxr_vdf_edon, 
+                 libcxr_vdf_put, libcxr_vdf_node, libcxr_vdf_edon, \
                  libcxr_vdf_kv, libcxr_lightpatch_bsp, libcxr_write_test_data,\
                  libcxr_world_preview, libcxr_free_tri_mesh, \
                  libcxr_fs_set_gameinfo, libcxr_fs_exit, libcxr_fs_get, \
-                 libcxr_load_mdl ]
+                 libcxr_fs_find,\
+                 libcxr_valve_load_model, libcxr_valve_free_model,\
+                 libcxr_valve_load_material, libcxr_valve_free_material ]
 
 # Callbacks
 def libcxr_log_callback(logStr):
@@ -634,7 +681,8 @@ def libcxr_line_callback( p0,p1,colour ):
    cxr_line_colours += [(colour[0],colour[1],colour[2],colour[3])]
 
 def cxr_reset_all():
-   global cxr_jobs_inf, cxr_jobs_batch, cxr_error_inf, cxr_view_mesh
+   global cxr_jobs_inf, cxr_jobs_batch, cxr_error_inf, cxr_view_mesh, \
+          cxr_asset_lib
    cxr_jobs_inf = None
    cxr_jobs_batch = None
    cxr_error_inf = None
@@ -643,6 +691,10 @@ def cxr_reset_all():
    cxr_batch_lines()
    cxr_view_mesh = None
 
+   cxr_asset_lib['models'] = {}
+   cxr_asset_lib['materials'] = {}
+   cxr_asset_lib['textures'] = {}
+   
    scene_redraw()
 
 # libnbvtf
@@ -1718,40 +1770,115 @@ class CXR_INIT_FS_OPERATOR(bpy.types.Operator):
 
       return {'FINISHED'}
 
-class CXR_LOAD_MODEL_OPERATOR(bpy.types.Operator):
-   bl_idname="convexer.model_load"
-   bl_label="Load model"
+def cxr_load_texture( path ):
+   global cxr_asset_lib
 
-   def execute(_,context):
-      global cxr_mdl_mesh, cxr_mdl_shader
+   if path in cxr_asset_lib['textures']:
+      return cxr_asset_lib['textures'][path]
 
-      mdlpath = bpy.context.scene.cxr_data.dev_mdl.encode('utf-8')
-      pmesh = libcxr_load_mdl.call( mdlpath )
+   print( F"cxr_load_texture( '{path}' )" )
 
-      if not pmesh:
-         print( "Failed to load model" )
-         return {'FINISHED'}
-      
-      mesh = pmesh[0]
+   # TODO
 
-      #TODO: remove code dupe
-      vertices = mesh.vertices[:mesh.vertex_count]
-      vertices = [(_[0],_[1],_[2]) for _ in vertices]
+   tex = cxr_asset_lib['textures'][path] = None
+   return tex
+
+def cxr_load_material( path ):
+   global cxr_asset_lib
+
+   if path in cxr_asset_lib['materials']:
+      return cxr_asset_lib['materials'][path]
+
+   print( F"cxr_load_material( '{path}' )" )
+   
+   pvmt = libcxr_valve_load_material.call( path.encode( 'utf-8') )
+   vmt = pvmt[0]
+
+   mat = cxr_asset_lib['materials'][path] = {}
+
+   if vmt.basetexture:
+      mat['basetexture'] = cxr_load_texture( vmt.basetexture.decode('utf-8') )
+   
+   if vmt.bumpmap:
+      mat['bumpmap'] = cxr_load_texture( vmt.bumpmap.decode('utf-8') )
+
+   libcxr_valve_free_material.call( pvmt )
+
+   return mat
+
+def cxr_load_model_full( path ):
+   global cxr_asset_lib, cxr_mdl_shader
+
+   if path in cxr_asset_lib['models']:
+      return cxr_asset_lib['models'][path]
+   
+   pmdl = libcxr_valve_load_model.call( path.encode( 'utf-8' ) )
+
+   print( F"cxr_load_model_full( '{path}' )" )
+
+   if not pmdl:
+      print( "Failed to load model" )
+      cxr_asset_lib['models'][path] = None
+      return None
+
+   mdl = pmdl[0]
+
+   # Convert our lovely interleaved vertex stream into, whatever this is.
+   positions = [ (mdl.vertex_data[i*8+0], \
+                  mdl.vertex_data[i*8+1], \
+                  mdl.vertex_data[i*8+2]) for i in range(mdl.vertex_count) ]
+
+   normals = [ (mdl.vertex_data[i*8+3], \
+                mdl.vertex_data[i*8+4], \
+                mdl.vertex_data[i*8+5]) for i in range(mdl.vertex_count) ]
 
-      normals = mesh.normals[:mesh.vertex_count]
-      normals = [(_[0],_[1],_[2]) for _ in normals]
+   uvs = [ (mdl.vertex_data[i*8+6], \
+            mdl.vertex_data[i*8+7]) for i in range(mdl.vertex_count) ]
 
-      indices = mesh.indices[:mesh.indices_count]
+   fmt = gpu.types.GPUVertFormat()
+   fmt.attr_add(id="aPos", comp_type='F32', len=3, fetch_mode='FLOAT')
+   fmt.attr_add(id="aNormal", comp_type='F32', len=3, fetch_mode='FLOAT')
+   fmt.attr_add(id="aUv", comp_type='F32', len=2, fetch_mode='FLOAT')
+
+   vbo = gpu.types.GPUVertBuf(len=mdl.vertex_count, format=fmt)
+   vbo.attr_fill(id="aPos", data=positions )
+   vbo.attr_fill(id="aNormal", data=normals )
+   vbo.attr_fill(id="aUv", data=uvs )
+
+   batches = cxr_asset_lib['models'][path] = []
+
+   for p in range(mdl.part_count):
+      part = mdl.parts[p]
+      indices = mdl.indices[part.ibstart:part.ibstart+part.ibcount]
       indices = [ (indices[i*3+0],indices[i*3+1],indices[i*3+2]) \
-                  for i in range(int(mesh.indices_count/3)) ]
-      
-      cxr_mdl_mesh = batch_for_shader(
-         cxr_mdl_shader, 'TRIS',
-         { "aPos": vertices, "aNormal": normals },
-         indices = indices,
-      )
+                  for i in range(part.ibcount//3) ]
 
-      libcxr_free_tri_mesh.call( pmesh )
+      ibo = gpu.types.GPUIndexBuf( type='TRIS', seq=indices )
+
+      batch = gpu.types.GPUBatch( type='TRIS', buf=vbo, elem=ibo )
+      batch.program_set( cxr_mdl_shader )
+
+      mat_str = cast( mdl.materials[ part.material ], c_char_p )
+      batches += [( cxr_load_material( mat_str.value.decode('utf-8') ), batch )]
+
+   libcxr_valve_free_model.call( pmdl )
+
+   return batches
+
+class CXR_LOAD_MODEL_OPERATOR(bpy.types.Operator):
+   bl_idname="convexer.model_load"
+   bl_label="Load model"
+
+   def execute(_,context):
+      global cxr_mdl_mesh, cxr_mdl_shader, cxr_asset_lib
+
+      test_mdl = cxr_load_model_full( bpy.context.scene.cxr_data.dev_mdl )
+
+      if test_mdl != None:
+         # just draw first batch part for now
+         cxr_mdl_mesh = test_mdl[0][1]
+      else:
+         cxr_mdl_mesh = None
 
       scene_redraw()
       return {'FINISHED'}
index 2a4267f0a152ffa3e0b34fd3eb4c5f4fd8a36db0..644c8ed83e39381e88ceb5c3beeb4772e29f3be3 100644 (file)
--- a/cxr/cxr.h
+++ b/cxr/cxr.h
@@ -3510,143 +3510,5 @@ CXR_API int cxr_lightpatch_bsp( const char *path )
    return 1;
 }
 
-#ifdef CXR_VALVE_BIN
-
-CXR_API cxr_tri_mesh *cxr_load_mdl( const char *mdlname )
-{
-       char path[1024];
-       strcpy( path, mdlname );
-       cxr_stripext( path );
-       strcat( path, ".dx90.vtx" );
-       VTXFileHeader_t *pVtxHdr = (VTXFileHeader_t *)cxr_fs_get( path );
-       
-       if( !pVtxHdr )
-               return NULL;
-       
-       /* .VVD */
-       strcpy( path, mdlname );
-       cxr_stripext( path );
-       strcat( path, ".vvd" );
-       vertexFileHeader_t *pVvdHdr = (vertexFileHeader_t *)cxr_fs_get( path );
-       
-       if( !pVvdHdr )
-       {
-               free( pVtxHdr );
-               return 0;
-       }
-       
-       /* .MDL */
-       strcpy( path, mdlname );
-       cxr_stripext( path );
-       strcat( path, ".mdl" );
-       studiohdr_t *pMdl = (studiohdr_t *)cxr_fs_get( path );
-       
-       if( !pMdl )
-       {
-               free( pVtxHdr );
-               free( pVvdHdr );
-               return 0;
-       }
-       
-   cxr_tri_mesh *mesh = malloc( sizeof(cxr_tri_mesh) );
-   mesh->colours = NULL;
-   
-       mesh->indices_count = vtx_count_indices( pVtxHdr, pMdl );
-       mesh->indices = malloc( mesh->indices_count * sizeof( u32 ) );
-
-       mesh->vertex_count = pVvdHdr->numLodVertexes[0];
-       mesh->vertices = malloc( mesh->vertex_count * sizeof(v3f) );
-   mesh->uvs = malloc( mesh->vertex_count * sizeof(v2f) );
-   mesh->normals = malloc( mesh->vertex_count * sizeof(v3f) );
-       
-#if 0
-   mesh->bounds[0][0] = pMdl->hull_min[0];
-   mesh->bounds[0][1] = pMdl->hull_min[1];
-   mesh->bounds[0][2] = pMdl->hull_min[2];
-   mesh->bounds[0][0] = pMdl->hull_max[0];
-   mesh->bounds[0][1] = pMdl->hull_max[1];
-   mesh->bounds[0][2] = pMdl->hull_max[2];
-#endif
-       
-       mesh->indices_count = 0;
-       
-       for( int bodyID = 0; bodyID < pVtxHdr->numBodyParts; ++bodyID )
-       {
-               /* Body parts */
-               VTXBodyPartHeader_t* pVtxBodyPart = pBodyPartVTX( pVtxHdr, bodyID );
-               mstudiobodyparts_t *pBodyPart = studiohdr_pBodypart( pMdl, bodyID );
-               
-               for( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
-               {       
-         /* models */
-                       VTXModelHeader_t* pVtxModel = pModelVTX( pVtxBodyPart, modelID );
-                       mstudiomodel_t *pStudioModel = 
-            mstudiobodyparts_pModel( pBodyPart, modelID );
-
-                       int nLod = 0;
-                       VTXModelLODHeader_t *pVtxLOD = pLODVTX( pVtxModel, nLod );
-
-                       for( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
-                       {
-            /* meshes */
-                               VTXMeshHeader_t* pVtxMesh = pMeshVTX( pVtxLOD, nMesh );
-                               mstudiomesh_t* pMesh = studiomodel_pMesh( pStudioModel, nMesh );
-
-                               for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
-                               {
-               /* groups */
-                                       VTXStripGroupHeader_t* pStripGroup = 
-                  pStripGroupVTX( pVtxMesh, nGroup );
-
-                                       for ( int nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
-                                       {
-                  /* strips */
-                                               VTXStripHeader_t *pStrip = pStripVTX( pStripGroup, nStrip );
-
-                                               if ( pStrip->flags & STRIP_IS_TRILIST )
-                                               {
-                     /* indices */
-                                                       for ( int i = 0; i < pStrip->numIndices; i ++ )
-                                                       {
-                                                               u16 i1 = *pIndexVTX( pStripGroup, 
-                              pStrip->indexOffset + i );                                                               
-
-                                                               mesh->indices[ mesh->indices_count ++ ] = 
-                           pVertexVTX( pStripGroup, i1 )->origMeshVertID + 
-                           pMesh->vertexoffset;
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-               }
-       }
-       
-       mstudiovertex_t *vertexData = GetVertexData( pVvdHdr );
-       
-       for( int i = 0; i < mesh->vertex_count; i ++ )
-       {
-               mstudiovertex_t *vert = vertexData + i;
-
-      mesh->vertices[i][0] = vert->pos[0];
-      mesh->vertices[i][1] = vert->pos[1];
-      mesh->vertices[i][2] = vert->pos[2];
-
-      mesh->normals[i][0] = vert->norm[0];
-      mesh->normals[i][1] = vert->norm[1];
-      mesh->normals[i][2] = vert->norm[2];
-
-      mesh->uvs[i][0] = vert->uv[0];
-      mesh->uvs[i][1] = vert->uv[1];
-       }
-       
-       free( pVtxHdr );
-       free( pVvdHdr );
-       free( pMdl );
-       
-       return mesh;
-}
-#endif /* CXR_VALVE_BIN */
-
 #endif /* CXR_VALVE_MAP_FILE */
 #endif /* CXR_IMPLEMENTATION */
index ed28bb8716018632481cc10a6e402f1bcc60485a..2e944aa3bcb470251ddbd4e6fb0eac2793c29329 100644 (file)
@@ -19,7 +19,7 @@ static i64 cxr_file_size( FILE *fileptr );
 static char *cxr_findext( char *path, char const delim );
 static char *cxr_findsep( char *path );
 
-static void cxr_stripext( char *path );
+static char *cxr_stripext( char *path );
 static int cxr_path_is_abs( char const *path );
 static char *cxr_filename( char *path );
 
@@ -41,11 +41,25 @@ static i64 cxr_file_size( FILE *fileptr )
        return fsize;
 }
 
+static int cxr_file_exists( const char *path )
+{
+   FILE *fp;
+   if( (fp=fopen( path, "rb" )) )
+   {
+      fclose(fp);
+      return 1;
+   }
+
+   return 0;
+}
+
 static void *fs_disk_open_read( const char *path, int reserve_end, i64 *size )
 {
        FILE *f = fopen( path, "rb" );
        if( f )
        {
+      printf( "fs_open_read (%s)\n", path );
+
                i64 fsize = cxr_file_size( f );
                void *buf = malloc( fsize + reserve_end );
                
@@ -147,11 +161,11 @@ static char *cxr_findsep( char *path )
        return ptr;
 }
 
-static void cxr_stripext( char *path )
+static char *cxr_stripext( char *path )
 {
-       char *point, *start;
+       char *point, *start, *ret = NULL;
        
-       // Skip folders
+   /* Skip any folders */
        if( !(start = cxr_findsep( path )) )
        {
                start = path;
@@ -161,9 +175,12 @@ static void cxr_stripext( char *path )
        {
                if( point > path )
                {
-                       *(point-1) = 0x00;
+         ret = point-1;
+                       *(ret) = 0x00;
                }
        }
+
+   return ret;
 }
 
 static void cxr_downlvl( char *path )
@@ -204,8 +221,39 @@ static int cxr_path_is_abs( char const *path )
 #endif
 }
 
+static void cxr_lowercase( char *str )
+{
+   char *c = str;
+   while( *c )
+   {
+      int val = (int)*c;
+      if( val >= (int)'A' && val <= (int)'Z' )
+      {
+         val = (int)'a' + (val - (int)'A');
+         *c = (char)val;
+      }
+      
+      c ++;
+   }
+}
+
+static void cxr_unixpath( char *path )
+{
+       char *c = path;
+       while( *c )
+   {
+      if( *c == '\\' )
+         *c = '/';
+
+      c ++;
+   }
+}
+
 static char *cxr_str_clone( char const *str, int extra )
 {
+   if( !str )
+      return NULL;
+
    char *newstr = malloc(strlen(str)+1+extra);
    strcpy( newstr, str );
 
index da6c84d6e11f81c12e00fe7e7081f897babfba90..b11f71f5d7f1cd00b067fcd9a4af5851d3b0828e 100644 (file)
@@ -24,19 +24,66 @@ typedef struct VPKDirectoryEntry VPKDirectoryEntry;
 typedef struct vdf_kv vdf_kv;
 typedef struct vdf_node vdf_node;
 typedef struct vdf_ctx vdf_ctx;
+typedef struct fs_locator fs_locator;
+
+/*
+ * These are 'unofficial' representations of the original formats, more C
+ * friendly 
+ */
 typedef struct valve_file_system valve_file_system;
+typedef struct valve_model valve_model;
+typedef struct valve_model_batch valve_model_batch;
+typedef struct valve_material valve_material;
+
+/* Api */
+
+CXR_API i32 cxr_fs_set_gameinfo( const char *path ); /* Setup system */
+CXR_API void cxr_fs_exit(void);                       /* Clean up */
+CXR_API void *cxr_fs_get( const char *path, i32 stringbuffer ); /* Get a file */
+CXR_API i32 cxr_fs_find( const char *path, fs_locator *locator );
+
+CXR_API valve_model *valve_load_model( const char *relpath );
+CXR_API void valve_free_model( valve_model *model );
+CXR_API valve_material *valve_load_material( const char *path );
+CXR_API void valve_free_material( valve_material *material );
 
 /*
- * File system
+ * File system implementation
  */
 
+#pragma pack(push, 1)
+
+struct VPKHeader
+{
+       u32 Signature;
+       u32 Version;
+       u32 TreeSize;
+       u32 FileDataSectionSize;
+       u32 ArchiveMD5SectionSize;
+       u32 OtherMD5SectionSize;
+       u32 SignatureSectionSize;
+};
+
+struct VPKDirectoryEntry
+{
+       u32 CRC;
+       u16 PreloadBytes; 
+       u16 ArchiveIndex;
+       u32 EntryOffset;
+       u32 EntryLength;
+       u16 Terminator;
+};
+
+#pragma pack(pop)
+
 static struct valve_file_system
 {
    char *gamedir,
         *exedir;
 
    /* Runtime */
-   VPKHeader *vpk;
+   VPKHeader vpk;
+   char *directory_tree;
 
    cxr_abuffer searchpaths;
 
@@ -47,16 +94,17 @@ static struct valve_file_system
 }
 fs_global = { .initialized = 0 };
 
-CXR_API int cxr_fs_set_gameinfo( const char *path ); /* Setup system */
-CXR_API void cxr_fs_exit(void);                       /* Clean up */
-CXR_API char *cxr_fs_get( const char *path );         /* Get a file */
+struct fs_locator
+{
+   VPKDirectoryEntry *vpk_entry;
+   char path[ 1024 ];
+};
 
 /* 
  * VPK reading
  */
 
-static VPKDirectoryEntry *vpk_find( VPKHeader *self, const char *asset );
-static void vpk_free( VPKHeader *self );
+static VPKDirectoryEntry *vpk_find( const char *asset );
 
 /*
  * VDF reading
@@ -97,39 +145,13 @@ while( (AS = vdf_next( NODE, STR, &__vdf_it_##AS )) )
 int __kv_it_##AS = 0; \
 const char * AS;\
 while( (AS = kv_next( NODE, STR, &__kv_it_##AS )) )
-#pragma pack(push, 1)
 
-struct VPKHeader
+static VPKDirectoryEntry *vpk_find( const char *asset )
 {
-       u32 Signature;
-       u32 Version;
-       u32 TreeSize;
-       u32 FileDataSectionSize;
-       u32 ArchiveMD5SectionSize;
-       u32 OtherMD5SectionSize;
-       u32 SignatureSectionSize;
-};
-
-struct VPKDirectoryEntry
-{
-       u32 CRC;
-       u16 PreloadBytes; 
-       u16 ArchiveIndex;
-       u32 EntryOffset;
-       u32 EntryLength;
-       u16 Terminator;
-};
-#pragma pack(pop)
-
-static void vpk_free( VPKHeader *self )
-{
-       free( self );
-}
+   valve_file_system *fs = &fs_global;
 
-static VPKDirectoryEntry *vpk_find( VPKHeader *self, const char *asset )
-{
-       if( !self ) 
-               return NULL;
+   if( !fs->directory_tree )
+      return NULL;
        
        char wbuf[ 512 ];
        strcpy( wbuf, asset );
@@ -151,7 +173,7 @@ static VPKDirectoryEntry *vpk_find( VPKHeader *self, const char *asset )
        *(fn-1) = 0x00;
 
        char *dir = wbuf;
-       char *pCur = ((char *)self) + sizeof( VPKHeader );
+       char *pCur = fs->directory_tree;
        
        while( 1 )
        {
@@ -540,6 +562,7 @@ static void vdf_parse_string( vdf_ctx *ctx )
                if( vdf_line_control( ctx ) )
                {
          /* Unexpected end of line */
+         cxr_log( "vdf: unexpected EOL\n" );
                        return;
                }
        
@@ -554,6 +577,7 @@ static int vdf_parse_structure( vdf_ctx *ctx )
                if( ctx->st.tokens[0] || !ctx->st.expect_decl )
                {
          /* Unexpected token '{' */
+         cxr_log( "vdf: Unexpected token '{'\n" );
                        ctx->errors ++;
                }
                
@@ -569,6 +593,7 @@ static int vdf_parse_structure( vdf_ctx *ctx )
                if( !ctx->st.pnode->parent )
                {
          /* Unexpected token '}' */
+         cxr_log( "vdf: Unexpected token '}'\n" );
                        ctx->errors ++;
                }
                else
@@ -590,6 +615,7 @@ static void vdf_parse_begin_token( vdf_ctx *ctx, char *ptr )
        if( ctx->st.expect_decl )
        {
       /* Unexpected token 'name' */
+      cxr_log( "vdf: unexpected token 'name'\n" );
                ctx->errors ++;
        }
 }
@@ -657,37 +683,68 @@ static void vdf_parse_feedbuffer( vdf_ctx *ctx, char *buf )
        }
 }
 
-static int vdf_load_into( const char *fn, vdf_node *node )
+static void vdf_debug_indent( int level )
 {
-       char *text_src = cxr_textasset_read( fn );
-       
-       if( !text_src )
-       {
-               return 0;
-       }
-       
+   for(int i=0; i<level; i++)
+      cxr_log(" ");
+}
+
+static void vdf_debug_r( vdf_node *node, int level )
+{
+   vdf_debug_indent(level);
+   cxr_log( "vdf_node(%p, name: '%s')\n", node, node->name );
+
+   vdf_debug_indent(level);
+   cxr_log( "{\n" );
+
+   for( int i=0; i<node->abpairs.count; i++ )
+   {
+      vdf_kv *kv = cxr_ab_ptr( &node->abpairs, i );
+
+      vdf_debug_indent(level+1);
+      cxr_log( "vdf_kv(%p, k: '%s', v: '%s')\n",
+         kv, kv->key, kv->value );
+   }
+
+   for( int i=0; i<node->abnodes.count; i++ )
+   {
+      vdf_node **child = cxr_ab_ptr( &node->abnodes, i );
+      vdf_debug_r( *child, level+1 );
+   }
+
+   vdf_debug_indent(level);
+   cxr_log( "}\n" );
+}
+
+/* This will wreck the original buffer, but must stay alive! */
+static vdf_node *vdf_from_buffer( char *buffer )
+{
+   vdf_node *root = vdf_create_node( NULL, NULL );
+
        vdf_ctx ctx = {0};
-       ctx.root = ctx.st.pnode = node;
+       ctx.root = ctx.st.pnode = root;
        
        vdf_newln( &ctx );
-       vdf_parse_feedbuffer( &ctx, text_src );
-       free( text_src );
-       
-       return 1;
+       vdf_parse_feedbuffer( &ctx, buffer );
+   
+#if 0
+   vdf_debug_r( root, 0 );
+#endif
+
+   return root;
 }
 
 static vdf_node *vdf_open_file( const char *fn )
 {      
-       vdf_node *root = vdf_create_node( NULL, NULL );
-       if( vdf_load_into( fn, root ) )
-       {
-               return root;
-       }
-       else
-       {
-               vdf_free_r( root );
+       char *text_src = cxr_textasset_read( fn );
+       
+       if( !text_src )
                return NULL;
-       }
+
+   vdf_node *root = vdf_from_buffer( text_src );
+   free( text_src );
+
+   return root;
 }
 
 /*
@@ -761,18 +818,31 @@ CXR_API i32 cxr_fs_set_gameinfo( const char *path )
        
    /* Find pack diretory */
        char pack_path[512];
+   fs->current_archive = NULL;
+   fs->current_idx = 0x7fff;
+
        for( int i = 0; i < fs->searchpaths.count; i ++ )
        {
       char **sp = cxr_ab_ptr( &fs->searchpaths, i );
 
                strcpy( pack_path, *sp );
                strcat( pack_path, "pak01_dir.vpk" );
-       
-               if( (fs->vpk = (VPKHeader *)cxr_asset_read( pack_path )) )
-                       break;
+
+      fs->current_archive = fopen( pack_path, "rb" );
+   
+      /* Read vpk directory */
+      if( fs->current_archive )
+      {
+         fread( &fs->vpk, sizeof(VPKHeader), 1, fs->current_archive );
+
+         fs->directory_tree = malloc( fs->vpk.TreeSize );
+         fread( fs->directory_tree, fs->vpk.TreeSize, 1, fs->current_archive );
+
+         break;
+      }
        }
        
-       if( !fs->vpk )
+       if( !fs->current_archive )
        {
                cxr_log( "Could not locate pak01_dir.vpk in %i searchpaths. "
                "Stock models will not load!\n", fs->searchpaths.count );
@@ -794,10 +864,10 @@ CXR_API void cxr_fs_exit(void)
    
    cxr_ab_free( &fs->searchpaths );
 
-       if( fs->vpk )
+       if( fs->directory_tree )
        {
-               vpk_free( fs->vpk );
-               fs->vpk = NULL;
+      free( fs->directory_tree );
+      fs->directory_tree = NULL;
        }
        
        if( fs->current_archive )
@@ -812,77 +882,148 @@ CXR_API void cxr_fs_exit(void)
    memset( fs, 0, sizeof( valve_file_system ) );
 }
 
-CXR_API char *cxr_fs_get( const char *path )
+static char *cxr_vpk_read( VPKDirectoryEntry *entry, int stringbuffer )
 {
        valve_file_system *fs = &fs_global;
 
    if( !fs->initialized )
       return NULL;
+
+   char pak[1024];
+   
+   /* Check if we need to change file handle */
+   if( entry->ArchiveIndex != fs->current_idx )
+   {
+      if( fs->current_archive )
+         fclose( fs->current_archive );
+
+      fs->current_archive = NULL;
+      fs->current_idx = entry->ArchiveIndex;
+
+      if( entry->ArchiveIndex == 0x7fff )
+      {
+         snprintf( pak, 1023, "%scsgo/pak01_dir.vpk", fs->exedir );
+      }
+      else
+      {
+         snprintf( pak, 1023, "%scsgo/pak01_%03hu.vpk", 
+               fs->exedir, entry->ArchiveIndex );
+      }
+
+      fs->current_archive = fopen( pak, "rb" );
+
+      if( !fs->current_archive )
+         cxr_log( "Warning: could not locate %s\n", pak );
+   }
+
+   if( !fs->current_archive )
+      return NULL;
+
+   size_t offset = entry->EntryOffset,
+          length = entry->EntryLength;
+
+   /*
+    * File is stored in the index, after the tree
+    */
+   if( entry->ArchiveIndex == 0x7fff )
+      offset += fs->vpk.TreeSize + sizeof(VPKHeader);
+   
+   /* 
+    * Entire file is stored in the preload bytes;
+    * Backtrack offset from directory to get absolute offset 
+    */
+   if( length == 0 )
+   {
+      offset = (char *)entry - (char *)fs->directory_tree;
+      offset += sizeof( VPKHeader );
+      offset += sizeof( VPKDirectoryEntry );
+
+      length = entry->PreloadBytes;
+   }
+   else
+      length += entry->PreloadBytes;
+
+   fseek( fs->current_archive, offset, SEEK_SET );
+   
+   size_t alloc_size = stringbuffer? length+1: length;
+   char *filebuf = malloc( alloc_size );
+
+   if( stringbuffer )
+      filebuf[length] = 0x00;
+
+   if( fread( filebuf, 1, length, fs->current_archive ) == length )
+      return filebuf;
+   else
+   {
+      /* Invalid read */
+      free( filebuf );
+      return NULL;
+   }
+}
+
+CXR_API i32 cxr_fs_find( const char *path, fs_locator *locator )
+{
+       valve_file_system *fs = &fs_global;
+
+   if( !fs->initialized )
+      return 0;
        
        VPKDirectoryEntry *entry;
-       char pak[ 533 ];
 
-       if( fs->vpk )
+       if( fs->directory_tree )
        {
-               if( (entry = vpk_find( fs->vpk, path )) )
+               if( (entry = vpk_find( path )) )
                {
-                       if( entry->ArchiveIndex != fs->current_idx )
-                       {
-                               if( fs->current_archive )
-                               {
-                                       fclose( fs->current_archive );
-                                       fs->current_archive = NULL;
-                               }
-                               
-                               fs->current_idx = entry->ArchiveIndex;
-                       }
-                       
-                       if( !fs->current_archive )
-                       {
-                               sprintf( pak, "%scsgo/pak01_%03hu.vpk", fs->exedir, 
-                  fs->current_idx );
-                               fs->current_archive = fopen( pak, "rb" );
-                               
-                               if( !fs->current_archive )
-                               {
-                                       cxr_log( "Could not locate %s\n", pak );
-                                       return NULL;
-                               }
-                       }
-                       
-                       char *filebuf = malloc( entry->EntryLength );
-                       
-                       fseek( fs->current_archive, entry->EntryOffset, SEEK_SET );
-                       if( fread( filebuf, 1, entry->EntryLength, fs->current_archive ) 
-               == entry->EntryLength )
-                       {
-                               return filebuf;
-                       }
-                       else
-                       {
-                               free( filebuf );
-                               return NULL;
-                       }
+         locator->vpk_entry = entry;
+         locator->path[0] = 0x00;
+         return 1;
                }
        }
-       
+
+   locator->vpk_entry = NULL;
+
    /* Use physical search paths */
-       char path_buf[ 512 ];
-       
        for( int i = 0; i < fs->searchpaths.count; i ++ )
        {
       char **sp = cxr_ab_ptr( &fs->searchpaths, i );
 
-               strcpy( path_buf, *sp );
-               strcat( path_buf, path );
-               
-               char *filebuf;
-               if( (filebuf = cxr_asset_read( path_buf )) )
-                       return filebuf;
+      snprintf( locator->path, 1023, "%s%s", *sp, path );
+      
+      if( cxr_file_exists( locator->path ) )
+         return 1;
        }
        
    /* File not found */
-       return NULL;
+       return 0;
+}
+
+CXR_API void *cxr_fs_get( const char *path, i32 stringbuffer )
+{
+       valve_file_system *fs = &fs_global;
+
+   if( !fs->initialized )
+      return NULL;
+
+   fs_locator locator;
+
+   if( cxr_fs_find( path, &locator ) )
+   {
+      if( locator.vpk_entry )
+      {
+         return cxr_vpk_read( locator.vpk_entry, stringbuffer );
+      }
+      else
+      {
+         char *filebuf;
+
+         if( stringbuffer )
+            return cxr_textasset_read( locator.path );
+         else
+            return cxr_asset_read( locator.path );
+      }
+   }
+
+   return NULL;
 }
 
 /*
@@ -1264,16 +1405,44 @@ typedef struct
 } 
 studiohdr_t;
 
+static char *studiohdr_pCdtexture( studiohdr_t *t, int i )
+{ 
+   return (((char *)t) + *((int *)(((u8 *)t) + t->cdtextureindex) + i)); 
+}
+
 static mstudiobodyparts_t *studiohdr_pBodypart( studiohdr_t *t, int i ) 
 { 
        return (mstudiobodyparts_t *)(((char *)t) + t->bodypartindex) + i; 
 }
 
+typedef struct
+{
+   int                                         sznameindex;
+   int                                         flags;
+   int                                         used;
+   
+   /* There is some extra unused stuff that was here... 
+    * Luckily since byte offsets are used, structure size doesn't matter */
+} 
+mstudiotexture_t;
+
+static char *mstudiotexture_pszName( mstudiotexture_t *t )
+{ 
+   return ((char *)t) + t->sznameindex;
+}
+
+static mstudiotexture_t *studiohdr_pTexture( studiohdr_t *t, int i )
+{ 
+   return (mstudiotexture_t *)(((u8 *)t) + t->textureindex) + i; 
+} 
+
 #pragma pack(pop)
 
-static u32 vtx_count_indices( VTXFileHeader_t *pVtxHdr, studiohdr_t *pMdl )
+static void vtx_resource_counts( VTXFileHeader_t *pVtxHdr, studiohdr_t *pMdl,
+      u32 *indices, u32 *meshes )
 {
-   int indice_count = 0;
+   *indices = 0;
+   *meshes = 0;
 
        for( int bodyID = 0; bodyID < pVtxHdr->numBodyParts; ++bodyID )
        {
@@ -1297,6 +1466,8 @@ static u32 vtx_count_indices( VTXFileHeader_t *pVtxHdr, studiohdr_t *pMdl )
                                VTXMeshHeader_t* pVtxMesh = pMeshVTX( pVtxLOD, nMesh );
                                mstudiomesh_t* pMesh = studiomodel_pMesh( pStudioModel, nMesh );
 
+            (*meshes)++;
+
                                for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
                                {
                /* groups */
@@ -1310,15 +1481,293 @@ static u32 vtx_count_indices( VTXFileHeader_t *pVtxHdr, studiohdr_t *pMdl )
 
                                                if ( pStrip->flags & STRIP_IS_TRILIST )
                                                {
-                     indice_count += pStrip->numIndices;
+                     *indices += pStrip->numIndices;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+/* 
+ * The following section is the wrappers for the underlying types
+ */
+
+struct valve_material
+{
+   char *basetexture,
+        *bumpmap;
+};
+
+struct valve_model
+{
+   float *vertex_data;  /* pos xyz, norm xyz, uv xy */
+
+   u32 *indices,
+        indices_count,
+        vertex_count,
+        part_count,
+        material_count;
+
+   char **materials;
+
+   struct valve_model_batch
+   {
+      u32 material,
+          ibstart,
+          ibcount;
+   }
+   *parts;
+
+   /* Internal valve data */
+   studiohdr_t *studiohdr;
+   VTXFileHeader_t *vtxhdr;
+   vertexFileHeader_t *vvdhdr;
+};
+
+CXR_API valve_model *valve_load_model( const char *relpath )
+{
+   char path[1024];
+   strcpy( path, relpath );
+
+   char *ext = cxr_stripext( path );
+
+   if( !ext )
+      return NULL;
+
+   /* Load data files */
+   valve_model *model = malloc( sizeof( valve_model ) );
+   model->studiohdr = NULL;
+   model->vtxhdr = NULL;
+   model->vvdhdr = NULL;
+
+   strcpy( ext, ".dx90.vtx" );
+   model->vtxhdr = cxr_fs_get( path, 0 );
+
+   strcpy( ext, ".vvd" );
+   model->vvdhdr = cxr_fs_get( path, 0 );
+
+   strcpy( ext, ".mdl" );
+   model->studiohdr = cxr_fs_get( path, 0 );
+
+   if( !model->vvdhdr || !model->studiohdr || !model->vtxhdr )
+   {
+      cxr_log( "Error, failed to load: (%s)\n", relpath );
+
+      free( model->studiohdr );
+      free( model->vvdhdr );
+      free( model->studiohdr );
+      free( model );
+
+      return NULL;
+   }
+
+   /* allocate resources */
+   vtx_resource_counts( model->vtxhdr, model->studiohdr, 
+         &model->indices_count, &model->part_count );
+   model->vertex_count = model->vvdhdr->numLodVertexes[0];
+   model->material_count = model->studiohdr->numtextures;
+
+   model->materials = malloc( model->material_count * sizeof(char *) );
+   model->parts = malloc( sizeof( valve_model_batch ) * model->part_count );
+   model->indices = malloc( sizeof( u32 ) * model->indices_count );
+   model->vertex_data = malloc( sizeof( float ) * 8 * model->vertex_count );
+   
+   /* Find materials */
+   for( int i=0; i<model->studiohdr->numtextures; i++ )
+   {
+      char material_path[ 1024 ];
+      fs_locator locator;
+
+      mstudiotexture_t *tex = studiohdr_pTexture( model->studiohdr, i );
+      const char *name = mstudiotexture_pszName( tex );
+
+      model->materials[i] = NULL;
+
+      for( int j=0; j<model->studiohdr->numcdtextures; j++ )
+      {
+         char *cdpath = studiohdr_pCdtexture( model->studiohdr, j );
+         snprintf( material_path, 1023, "materials/%s%s.vmt", cdpath, name );
+         cxr_unixpath( material_path );
+
+         if( cxr_fs_find( material_path, &locator ))
+         {
+            model->materials[i] = cxr_str_clone( material_path, 0 );
+            break;
+         }
+      }
+   }
+
+   u32 i_index = 0,
+       i_mesh = 0;
+
+   /* Extract meshes */
+       for( int bodyID = 0; bodyID < model->studiohdr->numbodyparts; ++bodyID )
+       {
+               /* Body parts */
+               VTXBodyPartHeader_t* pVtxBodyPart = pBodyPartVTX( model->vtxhdr, bodyID );
+               mstudiobodyparts_t *pBodyPart = 
+         studiohdr_pBodypart( model->studiohdr, bodyID );
+               
+               for( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
+               {       
+         /* models */
+                       VTXModelHeader_t* pVtxModel = pModelVTX( pVtxBodyPart, modelID );
+                       mstudiomodel_t *pStudioModel = 
+            mstudiobodyparts_pModel( pBodyPart, modelID );
+
+                       int nLod = 0;
+                       VTXModelLODHeader_t *pVtxLOD = pLODVTX( pVtxModel, nLod );
+
+                       for( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
+                       {
+            /* meshes, each of these creates a new draw CMD */
+                               VTXMeshHeader_t* pVtxMesh = pMeshVTX( pVtxLOD, nMesh );
+                               mstudiomesh_t* pMesh = studiomodel_pMesh( pStudioModel, nMesh );
+
+            valve_model_batch *curBatch = &model->parts[ i_mesh ++ ];
+            curBatch->material = pMesh->material;
+            curBatch->ibstart = i_index;
+            curBatch->ibcount = 0;
+
+                               for( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
+                               {
+               /* groups */
+                                       VTXStripGroupHeader_t* pStripGroup = 
+                  pStripGroupVTX( pVtxMesh, nGroup );
+
+                                       for( int nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
+                                       {
+                  /* strips */
+                                               VTXStripHeader_t *pStrip = pStripVTX( pStripGroup, nStrip );
+
+                                               if( pStrip->flags & STRIP_IS_TRILIST )
+                                               {
+                     /* indices */
+                                                       for( int i = 0; i < pStrip->numIndices; i ++ )
+                                                       {
+                                                               u16 i1 = *pIndexVTX( pStripGroup, 
+                              pStrip->indexOffset + i );                                                               
+
+                                                               model->indices[ i_index ++ ] = 
+                           pVertexVTX( pStripGroup, i1 )->origMeshVertID + 
+                           pMesh->vertexoffset;
+
+                        curBatch->ibcount ++;
+                                                       }
                                                }
+                  else
+                  {
+                     /* This is unused? */
+                  }
                                        }
                                }
                        }
                }
        }
+       
+       mstudiovertex_t *vertexData = GetVertexData( model->vvdhdr );
+
+       for( int i = 0; i < model->vertex_count; i ++ )
+       {
+               mstudiovertex_t *vert = &vertexData[i];
+      
+      float *dest = &model->vertex_data[ i*8 ];
+
+      dest[0] = vert->pos[0];
+      dest[1] = vert->pos[1];
+      dest[2] = vert->pos[2];
 
-   return indice_count;
+      dest[3] = vert->norm[0];
+      dest[4] = vert->norm[1];
+      dest[5] = vert->norm[2];
+
+      dest[6] = vert->uv[0];
+      dest[7] = vert->uv[1];
+       }
+       
+   return model;
+}
+
+CXR_API void valve_free_model( valve_model *model )
+{
+   for( int i=0; i<model->material_count; i++ )
+      free( model->materials[i] );
+
+   free( model->materials );
+   free( model->parts );
+   free( model->indices );
+   free( model->vertex_data );
+
+   free( model->studiohdr );
+   free( model->vtxhdr );
+   free( model->vvdhdr );
+   free( model );
+}
+
+static char *valve_texture_path( const char *path )
+{
+   if( !path )
+      return NULL;
+
+   char *buf = cxr_str_clone( path, 4 );
+
+   strcat( buf, ".vtf" );
+   cxr_unixpath( buf );
+   cxr_lowercase( buf );
+
+   return buf;
+}
+
+CXR_API valve_material *valve_load_material( const char *path )
+{
+   char *vmt = cxr_fs_get( path, 1 );
+
+   if( vmt )
+   {
+      valve_material *material = malloc( sizeof(valve_material) );
+      vdf_node *vmt_root = vdf_from_buffer( vmt );
+
+      if( vmt_root->abnodes.count == 0 )
+      {
+         cxr_log( "Error: vmt has no nodes\n" );
+         free( vmt );
+         vdf_free_r( vmt_root );
+         return 0;
+      }
+      
+      vdf_node **body = cxr_ab_ptr( &vmt_root->abnodes, 0 );
+
+      /* Path semantics here are inconsistent
+       * I believe they should all just be converted to lowercase, though */
+
+      for( int i=0; i<(*body)->abpairs.count; i++ )
+      {
+         vdf_kv *kv = cxr_ab_ptr( &(*body)->abpairs, i );
+         cxr_lowercase( kv->key );
+      }
+      
+      const char *basetexture = kv_get( *body, "$basetexture", NULL ),
+                 *bumpmap = kv_get( *body, "$bumpmap", NULL );
+      
+      /* TODO: other shader parameters */
+      material->basetexture = valve_texture_path( basetexture );
+      material->bumpmap = valve_texture_path( bumpmap );
+      
+      vdf_free_r( vmt_root );
+      free(vmt);
+
+      return material;
+   }
+
+   return NULL;
+}
+
+CXR_API void valve_free_material( valve_material *material )
+{
+   free( material->basetexture );
+   free( material->bumpmap );
 }
 
 #endif /* CXR_VALVE_BIN_H */
index 3903b9636e0a291c283fb72ec7bc690c2e92d3c9..7d82c1e7f7caf758fb4e2d617dbae35daddff403 100644 (file)
@@ -7,12 +7,30 @@ int main(int arc, const char *argv[])
       "/home/harry/.steam/steam/steamapps/common/"
       "Counter-Strike Global Offensive/csgo/gameinfo.txt" ) )
    {
-      cxr_tri_mesh *mesh_test = cxr_load_mdl( "models/pigeon.mdl" );
-
-      if( mesh_test )
+      valve_model *model_test = valve_load_model( "models/pigeon.mdl" );
+      if( model_test )
       {
          cxr_log( "Mesh loaded\n" );
-         cxr_free_tri_mesh( mesh_test );
+         
+         for( int i=0; i<model_test->material_count; i++ )
+         {
+            cxr_log( " material: %s\n", model_test->materials[i] );
+            
+            valve_material *material = 
+               valve_load_material( model_test->materials[i] );
+            
+            if( material )
+            {
+               cxr_log( "  $basetexture: %s\n"
+                        "  $bumpmap: %s\n",
+                        material->basetexture,
+                        material->bumpmap );
+
+               valve_free_material( material );
+            }
+         }
+
+         valve_free_model( model_test );
       }
       else
          cxr_log( "Mesh failed to load\n" );