You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
359 lines
10 KiB
359 lines
10 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
#include "stdafx.h" |
|
#include "physdll.h" |
|
#include "vphysics/constraints.h" |
|
#include "tier0/icommandline.h" |
|
#include "filesystem_tools.h" |
|
#include "simplify.h" |
|
#include "keyvalues.h" |
|
#include "studio.h" |
|
|
|
IPhysicsCollision *physcollision = NULL; |
|
IPhysicsSurfaceProps *physprops = NULL; |
|
|
|
int g_TotalOut = 0; |
|
int g_TotalCompress = 0; |
|
bool g_bRecursive = false; |
|
bool g_bQuiet = false; |
|
|
|
KeyValues *g_pModelConfig = NULL; |
|
|
|
void InitFilesystem( const char *pPath ) |
|
{ |
|
CmdLib_InitFileSystem( pPath ); |
|
// This bit of hackery allows us to access files on the harddrive |
|
g_pFullFileSystem->AddSearchPath( "", "LOCAL", PATH_ADD_TO_HEAD ); |
|
} |
|
|
|
static bool LoadSurfaceProps( const char *pMaterialFilename ) |
|
{ |
|
if ( !physprops ) |
|
return false; |
|
|
|
FileHandle_t fp = g_pFileSystem->Open( pMaterialFilename, "rb", TOOLS_READ_PATH_ID ); |
|
if ( fp == FILESYSTEM_INVALID_HANDLE ) |
|
return false; |
|
|
|
int len = g_pFileSystem->Size( fp ); |
|
char *pText = new char[len+1]; |
|
g_pFileSystem->Read( pText, len, fp ); |
|
g_pFileSystem->Close( fp ); |
|
|
|
pText[len]=0; |
|
|
|
physprops->ParseSurfaceData( pMaterialFilename, pText ); |
|
|
|
delete[] pText; |
|
|
|
return true; |
|
} |
|
|
|
void LoadSurfacePropsAll() |
|
{ |
|
// already loaded |
|
if ( physprops->SurfacePropCount() ) |
|
return; |
|
|
|
const char *SURFACEPROP_MANIFEST_FILE = "scripts/surfaceproperties_manifest.txt"; |
|
KeyValues *manifest = new KeyValues( SURFACEPROP_MANIFEST_FILE ); |
|
if ( manifest->LoadFromFile( g_pFileSystem, SURFACEPROP_MANIFEST_FILE, "GAME" ) ) |
|
{ |
|
for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() ) |
|
{ |
|
if ( !Q_stricmp( sub->GetName(), "file" ) ) |
|
{ |
|
// Add |
|
LoadSurfaceProps( sub->GetString() ); |
|
continue; |
|
} |
|
} |
|
} |
|
|
|
manifest->deleteThis(); |
|
} |
|
void InitVPhysics() |
|
{ |
|
CreateInterfaceFn physicsFactory = GetPhysicsFactory(); |
|
physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL ); |
|
physprops = (IPhysicsSurfaceProps *)physicsFactory( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, NULL ); |
|
LoadSurfacePropsAll(); |
|
} |
|
|
|
struct phyfile_t |
|
{ |
|
phyheader_t header; |
|
vcollide_t collide; |
|
int fileSize; |
|
}; |
|
|
|
void LoadPHYFile(phyfile_t *pOut, const char *name) |
|
{ |
|
memset( pOut, 0, sizeof(*pOut) ); |
|
FileHandle_t file = g_pFullFileSystem->Open( name, "rb" ); |
|
if ( !file ) |
|
return; |
|
|
|
g_pFullFileSystem->Read( &pOut->header, sizeof(pOut->header), file ); |
|
if ( pOut->header.size != sizeof(pOut->header) || pOut->header.solidCount <= 0 ) |
|
return; |
|
|
|
pOut->fileSize = g_pFullFileSystem->Size( file ); |
|
|
|
char *buf = (char *)_alloca( pOut->fileSize ); |
|
g_pFullFileSystem->Read( buf, pOut->fileSize, file ); |
|
g_pFullFileSystem->Close( file ); |
|
|
|
physcollision->VCollideLoad( &pOut->collide, pOut->header.solidCount, (const char *)buf, pOut->fileSize ); |
|
} |
|
|
|
|
|
void OverrideDefaultsForModel( const char *keyname, simplifyparams_t ¶ms ) |
|
{ |
|
KeyValues *pKeys = g_pModelConfig; |
|
while ( pKeys ) |
|
{ |
|
if ( !Q_stricmp( pKeys->GetName(), keyname ) ) |
|
{ |
|
for ( KeyValues *pData = pKeys->GetFirstSubKey(); pData; pData = pData->GetNextKey() ) |
|
{ |
|
if ( !Q_stricmp( pData->GetName(), "tolerance" ) ) |
|
{ |
|
params.tolerance = pData->GetFloat(); |
|
if (!g_bQuiet) |
|
{ |
|
Msg("%s: tolerance set to %.2f\n", keyname, params.tolerance ); |
|
} |
|
} |
|
else if ( !Q_stricmp( pData->GetName(), "addAABB" ) ) |
|
{ |
|
params.addAABBToSimplifiedHull = pData->GetInt() ? true : false; |
|
if (!g_bQuiet) |
|
{ |
|
Msg("%s: AABB %s\n", keyname, params.addAABBToSimplifiedHull ? "on" : "off" ); |
|
} |
|
} |
|
else if ( !Q_stricmp( pData->GetName(), "singleconvex" ) ) |
|
{ |
|
params.forceSingleConvex = pData->GetInt() ? true : false; |
|
if (!g_bQuiet) |
|
{ |
|
Msg("%s: Forced to single convex\n", keyname ); |
|
} |
|
} |
|
else if ( !Q_stricmp( pData->GetName(), "mergeconvex" ) ) |
|
{ |
|
params.mergeConvexTolerance = pData->GetFloat(); |
|
params.mergeConvexElements = params.mergeConvexTolerance > 0 ? true : false; |
|
if (!g_bQuiet) |
|
{ |
|
Msg("%s: Merge convex %.2f\n", keyname, params.mergeConvexTolerance ); |
|
} |
|
} |
|
} |
|
return; |
|
} |
|
pKeys = pKeys->GetNextKey(); |
|
} |
|
} |
|
|
|
bool HasMultipleBones( const char *pFilename ) |
|
{ |
|
char outName[1024]; |
|
studiohdr_t hdr; |
|
Q_strncpy( outName, pFilename, sizeof(outName) ); |
|
Q_SetExtension( outName, ".mdl", sizeof(outName) ); |
|
FileHandle_t fp = g_pFileSystem->Open( outName, "rb", TOOLS_READ_PATH_ID ); |
|
if ( fp == FILESYSTEM_INVALID_HANDLE ) |
|
return false; |
|
|
|
g_pFileSystem->Read( &hdr, sizeof(hdr), fp ); |
|
g_pFileSystem->Close( fp ); |
|
if ( hdr.numbones > 1 ) |
|
return true; |
|
return false; |
|
} |
|
|
|
void WritePHXFile( const char *pName, const phyfile_t &file ) |
|
{ |
|
if ( file.header.size != sizeof(file.header) || file.collide.solidCount <= 0 ) |
|
return; |
|
|
|
CUtlBuffer out; |
|
|
|
char outName[1024]; |
|
Q_snprintf( outName, sizeof(outName), "%s", pName ); |
|
Q_SetExtension( outName, ".phx", sizeof(outName) ); |
|
|
|
simplifyparams_t params; |
|
params.Defaults(); |
|
params.tolerance = (file.collide.solidCount > 1) ? 4.0f : 2.0f; |
|
// single solids constraint to AABB for placement help |
|
params.addAABBToSimplifiedHull = (file.collide.solidCount == 1) ? true : false; |
|
params.mergeConvexElements = true; |
|
params.mergeConvexTolerance = 0.025f; |
|
Q_FixSlashes(outName); |
|
Q_strlower(outName); |
|
char *pSearch = Q_strstr( outName,"models\\" ); |
|
if ( pSearch ) |
|
{ |
|
char keyname[1024]; |
|
pSearch += strlen("models\\"); |
|
Q_StripExtension( pSearch, keyname, sizeof(keyname) ); |
|
OverrideDefaultsForModel( keyname, params ); |
|
} |
|
out.Put( &file.header, sizeof(file.header) ); |
|
int outSize = 0; |
|
bool bStoreSolidNames = file.collide.solidCount > 1 ? true : false; |
|
bStoreSolidNames = bStoreSolidNames || HasMultipleBones(outName); |
|
|
|
vcollide_t *pNewCollide = ConvertVCollideToPHX( &file.collide, params, &outSize, false, bStoreSolidNames); |
|
g_TotalOut += file.fileSize; |
|
for ( int i = 0; i < pNewCollide->solidCount; i++ ) |
|
{ |
|
int collideSize = physcollision->CollideSize( pNewCollide->solids[i] ); |
|
out.PutInt( collideSize ); |
|
char *pMem = new char[collideSize]; |
|
physcollision->CollideWrite( pMem, pNewCollide->solids[i] ); |
|
out.Put( pMem, collideSize ); |
|
delete[] pMem; |
|
} |
|
|
|
if (!g_bQuiet) |
|
{ |
|
Msg("%s Compressed %d (%d text) to %d (%d text)\n", outName, file.fileSize, file.collide.descSize, out.TellPut(), pNewCollide->descSize ); |
|
} |
|
out.Put( pNewCollide->pKeyValues, pNewCollide->descSize ); |
|
g_TotalCompress += out.TellPut(); |
|
|
|
#if 0 |
|
//Msg("OLD:\n-----------------------------------\n%s\n", file.collide.pKeyValues ); |
|
CPackedPhysicsDescription *pPacked = physcollision->CreatePackedDesc( pNewCollide->pKeyValues, pNewCollide->descSize ); |
|
Msg("NEW:\n-----------------------------------\n" ); |
|
for ( int i = 0; i < pPacked->m_solidCount; i++ ) |
|
{ |
|
solid_t solid; |
|
pPacked->GetSolid( &solid, i ); |
|
Msg("index %d\n", solid.index ); |
|
Msg("name %s\n", solid.name ); |
|
Msg("mass %.2f\n", solid.params.mass ); |
|
Msg("surfaceprop %s\n", solid.surfaceprop); |
|
Msg("damping %.2f\n", solid.params.damping ); |
|
Msg("rotdamping %.2f\n", solid.params.rotdamping ); |
|
Msg("drag %.2f\n", solid.params.dragCoefficient ); |
|
Msg("inertia %.2f\n", solid.params.inertia ); |
|
Msg("volume %.2f\n", solid.params.volume ); |
|
} |
|
#endif |
|
DestroyPHX( pNewCollide ); |
|
if ( !g_pFullFileSystem->WriteFile( outName, NULL, out ) ) |
|
Warning("Can't write file: %s\n", outName ); |
|
} |
|
|
|
void UnloadPHYFile( phyfile_t *pFile ) |
|
{ |
|
physcollision->VCollideUnload( &pFile->collide ); |
|
pFile->header.size = 0; |
|
} |
|
|
|
void MakeFilename( char *pDest, int destSize, const char *pPathname, const char *pFilenameExt ) |
|
{ |
|
Q_strncpy(pDest, pPathname, destSize); |
|
Q_AppendSlash(pDest, destSize); |
|
Q_strncat(pDest, pFilenameExt, destSize); |
|
} |
|
|
|
void MakeDirname( char *pDest, int destSize, const char *pPathname, const char *pSubdir ) |
|
{ |
|
MakeFilename(pDest, destSize , pPathname, pSubdir); |
|
} |
|
|
|
int main( int argc, char *argv[] ) |
|
{ |
|
if ( argc < 2 ) |
|
{ |
|
Msg("Usage:\nmakephx [options] <FILESPEC>\ne.g. makephx [-r] *.phy\n"); |
|
return 0; |
|
} |
|
|
|
CommandLine()->CreateCmdLine( argc, argv ); |
|
g_bRecursive = CommandLine()->FindParm("-r") > 0 ? true : false; |
|
g_bQuiet = CommandLine()->FindParm("-quiet") > 0 ? true : false; |
|
InitFilesystem( "*.*" ); |
|
InitVPhysics(); |
|
// disable automatic packing, we want to do this ourselves. |
|
physcollision->SetPackOnLoad( false ); |
|
MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false ); |
|
InstallSpewFunction(); |
|
|
|
g_pModelConfig = new KeyValues("config"); |
|
g_pModelConfig->LoadFromFile( g_pFullFileSystem, "phx.cfg", "GAME" ); |
|
g_TotalOut = 0; |
|
g_TotalCompress = 0; |
|
FileFindHandle_t handle; |
|
char fullpath[1024], currentFile[1024], dirName[1024], nameext[256]; |
|
strcpy( fullpath, argv[argc-1] ); |
|
strcpy( fullpath, ExpandPath( fullpath ) ); |
|
strcpy( fullpath, ExpandArg( fullpath ) ); |
|
Q_strncpy(dirName, fullpath, sizeof(dirName)); |
|
Q_StripFilename(dirName); |
|
Q_strncpy(nameext, fullpath + strlen(dirName)+1, sizeof(nameext)); |
|
CUtlVector< const char * > directoryList; |
|
directoryList.AddToTail( strdup(dirName) ); |
|
int current = 0; |
|
int count = 0; |
|
do |
|
{ |
|
if ( g_bRecursive ) |
|
{ |
|
MakeFilename( currentFile, sizeof(currentFile), directoryList[current], "*.*" ); |
|
const char *pFilename = g_pFullFileSystem->FindFirst( currentFile, &handle ); |
|
while ( pFilename ) |
|
{ |
|
if ( pFilename[0] != '.' && g_pFullFileSystem->FindIsDirectory( handle ) ) |
|
{ |
|
MakeDirname( currentFile, sizeof(currentFile), directoryList[current], pFilename ); |
|
directoryList.AddToTail(strdup(currentFile)); |
|
} |
|
pFilename = g_pFullFileSystem->FindNext( handle ); |
|
} |
|
g_pFullFileSystem->FindClose( handle ); |
|
} |
|
|
|
MakeFilename(currentFile, sizeof(currentFile), directoryList[current], nameext); |
|
const char *pFilename = g_pFullFileSystem->FindFirst( currentFile, &handle ); |
|
while ( pFilename ) |
|
{ |
|
phyfile_t phy; |
|
MakeFilename(currentFile, sizeof(currentFile), directoryList[current], pFilename); |
|
LoadPHYFile( &phy, currentFile ); |
|
if ( phy.collide.isPacked || phy.collide.solidCount < 1 ) |
|
{ |
|
Msg("%s is not a valid PHY file\n", currentFile ); |
|
} |
|
else |
|
{ |
|
WritePHXFile( currentFile, phy ); |
|
count++; |
|
} |
|
UnloadPHYFile( &phy ); |
|
pFilename = g_pFullFileSystem->FindNext( handle ); |
|
} |
|
g_pFullFileSystem->FindClose( handle ); |
|
current++; |
|
} while( current < directoryList.Count() ); |
|
|
|
if ( count ) |
|
{ |
|
if (!g_bQuiet) |
|
{ |
|
Msg("\n------\nTotal %s, %s\nSaved %s\n", Q_pretifymem( g_TotalOut ), Q_pretifymem( g_TotalCompress ), Q_pretifymem( g_TotalOut - g_TotalCompress ) ); |
|
Msg("%.2f%% savings\n", ((float)(g_TotalOut-g_TotalCompress) / (float)g_TotalOut) * 100.0f ); |
|
} |
|
} |
|
else |
|
{ |
|
Msg("No files found in %s!\n", directoryList[current] ); |
|
} |
|
|
|
return 0; |
|
}
|
|
|