cxr_jobs_inf = []
cxr_error_inf = None
+cxr_asset_lib = \
+{
+ "models": {},
+ "materials": {},
+ "textures": {}
+}
+
# Shaders
cxr_view_shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
vec3 worldPos = pWorldPos.xyz;
gl_Position = viewProjectionMatrix * pWorldPos;
- lNormal = aNormal; //mat3(transpose(inverse(modelMatrix))) * aNormal;
+ lNormal = normalize(mat3(transpose(inverse(modelMatrix))) * aNormal);
lPos = worldPos;
}
""","""
("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):
# 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):
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
cxr_batch_lines()
cxr_view_mesh = None
+ cxr_asset_lib['models'] = {}
+ cxr_asset_lib['materials'] = {}
+ cxr_asset_lib['textures'] = {}
+
scene_redraw()
# libnbvtf
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'}
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;
}
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
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 );
*(fn-1) = 0x00;
char *dir = wbuf;
- char *pCur = ((char *)self) + sizeof( VPKHeader );
+ char *pCur = fs->directory_tree;
while( 1 )
{
if( vdf_line_control( ctx ) )
{
/* Unexpected end of line */
+ cxr_log( "vdf: unexpected EOL\n" );
return;
}
if( ctx->st.tokens[0] || !ctx->st.expect_decl )
{
/* Unexpected token '{' */
+ cxr_log( "vdf: Unexpected token '{'\n" );
ctx->errors ++;
}
if( !ctx->st.pnode->parent )
{
/* Unexpected token '}' */
+ cxr_log( "vdf: Unexpected token '}'\n" );
ctx->errors ++;
}
else
if( ctx->st.expect_decl )
{
/* Unexpected token 'name' */
+ cxr_log( "vdf: unexpected token 'name'\n" );
ctx->errors ++;
}
}
}
}
-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;
}
/*
/* 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 );
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 )
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;
}
/*
}
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 )
{
VTXMeshHeader_t* pVtxMesh = pMeshVTX( pVtxLOD, nMesh );
mstudiomesh_t* pMesh = studiomodel_pMesh( pStudioModel, nMesh );
+ (*meshes)++;
+
for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
{
/* groups */
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 */