summaryrefslogtreecommitdiff
path: root/utils/xbox/makephx
diff options
context:
space:
mode:
Diffstat (limited to 'utils/xbox/makephx')
-rw-r--r--utils/xbox/makephx/makephx.cpp359
-rw-r--r--utils/xbox/makephx/makephx.vcproj300
-rw-r--r--utils/xbox/makephx/phx.cpp215
-rw-r--r--utils/xbox/makephx/phxfile.cpp42
-rw-r--r--utils/xbox/makephx/phxfile.h18
-rw-r--r--utils/xbox/makephx/simplify.cpp453
-rw-r--r--utils/xbox/makephx/simplify.h38
-rw-r--r--utils/xbox/makephx/stdafx.cpp9
-rw-r--r--utils/xbox/makephx/stdafx.h20
-rw-r--r--utils/xbox/makephx/util.cpp41
-rw-r--r--utils/xbox/makephx/util.h16
11 files changed, 1511 insertions, 0 deletions
diff --git a/utils/xbox/makephx/makephx.cpp b/utils/xbox/makephx/makephx.cpp
new file mode 100644
index 0000000..8473005
--- /dev/null
+++ b/utils/xbox/makephx/makephx.cpp
@@ -0,0 +1,359 @@
+//========= 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 &params )
+{
+ 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;
+}
diff --git a/utils/xbox/makephx/makephx.vcproj b/utils/xbox/makephx/makephx.vcproj
new file mode 100644
index 0000000..90df4a1
--- /dev/null
+++ b/utils/xbox/makephx/makephx.vcproj
@@ -0,0 +1,300 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="makephx"
+ ProjectGUID="{FD9038D2-A941-4B5E-8E35-4205E650F120}"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="..\..\..\..\game\bin\"
+ IntermediateDirectory="Debug"
+ ConfigurationType="1"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\public;..\..\common;..\..\..\public\tier1"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="5"
+ UsePrecompiledHeader="3"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/makephx.exe"
+ LinkIncremental="2"
+ IgnoreDefaultLibraryNames="LIBC,LIBCD"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/makephx.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"
+ Description="Write enable target file"
+ CommandLine="attrib -r ..\..\..\..\game\bin\makephx.exe"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory="Release"
+ ConfigurationType="1"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\..\public;..\..\common;..\..\..\public\tier1"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="4"
+ UsePrecompiledHeader="3"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="if exist ..\..\..\..\game\bin\&quot;$(TargetName)&quot;.exe attrib -r ..\..\..\..\game\bin\&quot;$(TargetName)&quot;.exe
+if exist &quot;$(TargetPath)&quot; copy &quot;$(TargetPath)&quot; ..\..\..\..\game\bin"
+ Outputs="..\..\..\..\game\bin\$(TargetName).exe"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/makephx.exe"
+ LinkIncremental="1"
+ IgnoreDefaultLibraryNames="LIBC,LIBCD"
+ GenerateDebugInformation="TRUE"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"
+ Description="Write enable target file"
+ CommandLine="attrib -r ..\..\..\..\game\bin\makephx.exe"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+ <File
+ RelativePath=".\makephx.cpp">
+ </File>
+ <File
+ RelativePath=".\phxfile.cpp">
+ </File>
+ <File
+ RelativePath=".\simplify.cpp">
+ </File>
+ <File
+ RelativePath=".\stdafx.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"/>
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+ <File
+ RelativePath="..\..\..\public\vphysics\constraints.h">
+ </File>
+ <File
+ RelativePath="..\..\..\public\filesystem.h">
+ </File>
+ <File
+ RelativePath="..\..\..\public\filesystem_helpers.h">
+ </File>
+ <File
+ RelativePath="..\..\..\public\filesystem_init.h">
+ </File>
+ <File
+ RelativePath="..\..\..\public\vstdlib\ICommandLine.h">
+ </File>
+ <File
+ RelativePath="..\..\..\public\tier1\KeyValues.h">
+ </File>
+ <File
+ RelativePath="..\..\..\public\mathlib\mathlib.h">
+ </File>
+ <File
+ RelativePath=".\phxfile.h">
+ </File>
+ <File
+ RelativePath=".\stdafx.h">
+ </File>
+ <File
+ RelativePath="..\..\..\public\vstdlib\strtools.h">
+ </File>
+ <File
+ RelativePath="..\..\..\public\vcollide.h">
+ </File>
+ <File
+ RelativePath="..\..\..\public\vcollide_parse.h">
+ </File>
+ <File
+ RelativePath="..\..\..\public\vphysics_interface.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
+ </Filter>
+ <Filter
+ Name="common"
+ Filter="">
+ <File
+ RelativePath="..\..\common\cmdlib.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\..\..\public\filesystem_helpers.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\..\..\public\filesystem_init.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\..\common\filesystem_tools.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\..\common\physdll.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <File
+ RelativePath="..\..\..\lib\public\mathlib.lib">
+ </File>
+ <File
+ RelativePath=".\ReadMe.txt">
+ </File>
+ <File
+ RelativePath="..\..\..\lib\public\tier0.lib">
+ </File>
+ <File
+ RelativePath="..\..\..\lib\public\tier1.lib">
+ </File>
+ <File
+ RelativePath="..\..\..\lib\public\tier2.lib">
+ </File>
+ <File
+ RelativePath="..\..\..\lib\public\vstdlib.lib">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/utils/xbox/makephx/phx.cpp b/utils/xbox/makephx/phx.cpp
new file mode 100644
index 0000000..5a292b2
--- /dev/null
+++ b/utils/xbox/makephx/phx.cpp
@@ -0,0 +1,215 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "cmdlib.h"
+#include "mathlib/mathlib.h"
+#include "tier1/strtools.h"
+#include "physdll.h"
+#include "phyfile.h"
+#include "phxfile.h"
+#include "utlvector.h"
+#include "utlbuffer.h"
+#include "vphysics_interface.h"
+#include "vcollide_parse.h"
+#include "vphysics/constraints.h"
+#include "tier0/icommandline.h"
+#include "filesystem_tools.h"
+#include "simplify.h"
+#include "mathlib/compressed_vector.h"
+#include "keyvalues.h"
+
+IPhysicsCollision *physcollision = NULL;
+IPhysicsSurfaceProps *physprops = NULL;
+
+int g_TotalOut = 0;
+int g_TotalCompress = 0;
+
+void InitFilesystem( const char *pPath )
+{
+ CmdLib_InitFileSystem( pPath );
+ //FileSystem_Init( ".", 0, FS_INIT_COMPATIBILITY_MODE );
+ // 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;
+
+ // already loaded
+ if ( physprops->SurfacePropCount() )
+ 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()
+{
+ 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 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) );
+
+ out.Put( &file.header, sizeof(file.header) );
+ int outSize = 0;
+ float tolerance = (file.collide.solidCount > 1) ? 3.0f : 7.0f;
+
+ vcollide_t *pNewCollide = ConvertVCollideToPHX( &file.collide, tolerance, &outSize, false );
+ 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;
+ }
+
+ 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();
+
+ 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 );
+ }
+
+ if ( !g_pFullFileSystem->WriteFile( outName, NULL, out ) )
+ Error("Error writing %s\n", outName );
+}
+
+void UnloadPHYFile( phyfile_t *pFile )
+{
+ physcollision->VCollideUnload( &pFile->collide );
+ pFile->header.size = 0;
+}
+
+int main( int argc, char *argv[] )
+{
+ CommandLine()->CreateCmdLine( argc, argv );
+ InstallSpewFunction();
+ InitFilesystem( argv[argc-1] );
+ InitVPhysics();
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
+
+ g_TotalOut = 0;
+ g_TotalCompress = 0;
+ FileFindHandle_t handle;
+ char fullpath[1024], filebase[1024];
+ strcpy( fullpath, argv[argc-1] );
+ strcpy( fullpath, ExpandPath( fullpath ) );
+ strcpy( fullpath, ExpandArg( fullpath ) );
+ Q_FileBase(fullpath, filebase, sizeof(filebase));
+
+ const char *pFilename = g_pFullFileSystem->FindFirst( argv[argc-1], &handle );
+ while ( pFilename )
+ {
+#if 0
+ if ( g_pFullFileSystem->FindIsDirectory( handle ) )
+ {
+ }
+#endif
+ g_pFullFileSystem->RelativePathToFullPath( pFilename, NULL, filebase, sizeof( filebase ) );
+
+ phyfile_t phy;
+ strcpy( filebase, ExpandPath( filebase ) );
+ strcpy( filebase, ExpandArg( filebase ) );
+ LoadPHYFile( &phy, filebase );
+ WritePHXFile( filebase, phy );
+ UnloadPHYFile( &phy );
+ pFilename = g_pFullFileSystem->FindNext( handle );
+ }
+ g_pFullFileSystem->FindClose( handle );
+ Msg("Total %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 );
+
+ return 0;
+}
diff --git a/utils/xbox/makephx/phxfile.cpp b/utils/xbox/makephx/phxfile.cpp
new file mode 100644
index 0000000..304e87e
--- /dev/null
+++ b/utils/xbox/makephx/phxfile.cpp
@@ -0,0 +1,42 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "stdafx.h"
+#include "simplify.h"
+
+extern IPhysicsCollision *physcollision;
+
+vcollide_t *ConvertVCollideToPHX( const vcollide_t *pCollideIn, const simplifyparams_t &params, int *pSize, bool bStoreSurfaceprops, bool bStoreSolidNames )
+{
+ Assert( !pCollideIn->isPacked );
+ vcollide_t *pCollideOut = new vcollide_t;
+ pCollideOut->solids = new CPhysCollide *[pCollideIn->solidCount];
+ pCollideOut->solidCount = pCollideIn->solidCount;
+ *pSize = 0;
+
+ for ( int i = 0; i < pCollideIn->solidCount; i++ )
+ {
+ Assert( pCollideIn->solids[i] );
+ pCollideOut->solids[i] = SimplifyCollide( pCollideIn->solids[i], i, params );
+ Assert( pCollideOut->solids[i] );
+ int collideSize = physcollision->CollideSize( pCollideOut->solids[i] );
+ *pSize += collideSize;
+ }
+ int packedTextSize;
+ pCollideOut->pKeyValues = (char *)physcollision->PackVCollideText( pCollideIn->pKeyValues, &packedTextSize, bStoreSolidNames, bStoreSurfaceprops );
+ pCollideOut->isPacked = true;
+ pCollideOut->descSize = packedTextSize;
+ *pSize += packedTextSize;
+
+ return pCollideOut;
+}
+
+
+void DestroyPHX( vcollide_t *pCollide )
+{
+ for ( int i = 0; i < pCollide->solidCount; i++ )
+ {
+ physcollision->DestroyCollide( pCollide->solids[i] );
+ }
+ delete[] pCollide->solids;
+ physcollision->DestroyVCollideText( pCollide->pKeyValues );
+ delete pCollide;
+}
diff --git a/utils/xbox/makephx/phxfile.h b/utils/xbox/makephx/phxfile.h
new file mode 100644
index 0000000..cb4f2dc
--- /dev/null
+++ b/utils/xbox/makephx/phxfile.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef PHXFILE_H
+#define PHXFILE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "simplify.h"
+// convert a vcollide to packed/simplified format
+vcollide_t *ConvertVCollideToPHX( const vcollide_t *pCollideIn, const simplifyparams_t &params, int *pSize, bool bStoreSurfaceprops, bool bStoreSolidNames );
+void DestroyPHX( vcollide_t *pCollide );
+
+#endif // PHXFILE_H
diff --git a/utils/xbox/makephx/simplify.cpp b/utils/xbox/makephx/simplify.cpp
new file mode 100644
index 0000000..897fccf
--- /dev/null
+++ b/utils/xbox/makephx/simplify.cpp
@@ -0,0 +1,453 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "stdafx.h"
+#include "simplify.h"
+extern IPhysicsCollision *physcollision;
+
+extern bool g_bQuiet;
+
+const float DIST_EPSILON = 1.0f / 32.0f;
+// this is the list of candidate planes that will be added one by one to the convex hull
+// until none of the surface lies outside the tolerance
+struct planetest_t
+{
+ Vector normal;
+ float dist;
+ int inUse;
+ float bestDist;
+ void Init( int axis, float sign, float _dist, bool _inUse = false )
+ {
+ memset( this, 0, sizeof(*this) );
+ normal[axis] = sign;
+ dist = sign*_dist;
+ inUse = _inUse;
+ bestDist = -1;
+ }
+ void Init( const Vector &a, const Vector &b, const Vector &c, bool _inUse = false )
+ {
+ Vector e0 = b-a;
+ Vector e1 = c-a;
+ normal = CrossProduct( e1, e0 );
+ VectorNormalize( normal );
+ dist = DotProduct( normal, a );
+ inUse = _inUse;
+ bestDist = -1;
+ }
+};
+
+CPhysConvex *ConvertPlaneListToConvex( CUtlVector<planetest_t> &list )
+{
+ float temp[4 * 2048];
+ struct listplane_t
+ {
+ float plane[4];
+ };
+
+ int planeCount = 0;
+ listplane_t *pList = (listplane_t *)temp;
+ for ( int i = 0; i < list.Count(); i++ )
+ {
+ if ( list[i].inUse )
+ {
+ list[i].normal.CopyToArray( pList[planeCount].plane );
+ pList[planeCount].plane[3] = list[i].dist;
+ planeCount++;
+ }
+ }
+
+ return physcollision->ConvexFromPlanes( temp, planeCount, 0.25f );
+}
+
+Vector BoxSupport( const Vector &dir, const Vector &mins, const Vector &maxs )
+{
+ Vector out;
+ for ( int i = 0; i < 3; i++ )
+ {
+ out[i] = (dir[i] >= 0) ? maxs[i] : mins[i];
+ }
+ return out;
+}
+
+struct convexoptimize_t
+{
+ CUtlVector<planetest_t> list;
+ float targetTolerance;
+
+ void InitPlanes( CPhysCollide *pCollide, bool addAABBToSimplifiedHull )
+ {
+ Vector mins, maxs;
+ physcollision->CollideGetAABB( &mins, &maxs, pCollide, vec3_origin, vec3_angle );
+ if ( !addAABBToSimplifiedHull )
+ {
+ mins -= Vector(targetTolerance,targetTolerance,targetTolerance);
+ maxs += Vector(targetTolerance,targetTolerance,targetTolerance);
+ }
+ int i;
+ for ( i = 0; i < 3; i++ )
+ {
+ planetest_t &elem = list[list.AddToTail()];
+ elem.Init( i, 1.0f, maxs[i], true );
+ planetest_t &elem2 = list[list.AddToTail()];
+ elem2.Init( i, -1.0f, mins[i], true );
+ }
+ ICollisionQuery *pQuery = physcollision->CreateQueryModel( pCollide );
+ Vector triVerts[3];
+ for ( i = 0; i < pQuery->TriangleCount(0); i++ )
+ {
+ pQuery->GetTriangleVerts( 0, i, triVerts );
+ planetest_t &elem = list[list.AddToTail()];
+ elem.Init( triVerts[0], triVerts[1], triVerts[2], false );
+ elem.bestDist = DotProduct( elem.normal, BoxSupport(elem.normal, mins, maxs) ) - elem.dist;
+ }
+ physcollision->DestroyQueryModel( pQuery );
+ }
+
+ CPhysConvex *ConvertToConvex()
+ {
+ return ::ConvertPlaneListToConvex( list );
+ }
+
+ int FindBestPlane( float dist )
+ {
+ int best = -1;
+ for ( int i = 6; i < list.Count(); i++ )
+ {
+ if ( list[i].inUse )
+ continue;
+ if ( dist >= list[i].bestDist )
+ continue;
+ dist = list[i].bestDist;
+ best = i;
+ }
+ return best;
+ }
+
+ bool AddBestPlane()
+ {
+ convertconvexparams_t params;
+ params.Defaults();
+ CPhysConvex *pConvex = ConvertPlaneListToConvex( list );
+ CPhysCollide *pCurrentCollide = physcollision->ConvertConvexToCollideParams( &pConvex, 1, params );
+ int bestIndex = -1;
+ float bestDist = 0;
+ while ( true )
+ {
+ if ( bestIndex >= 0 )
+ {
+ list[bestIndex].inUse = true;
+ }
+ int test = FindBestPlane( bestDist );
+ if ( test < 0 )
+ break;
+ if ( bestIndex >= 0 )
+ {
+ list[bestIndex].inUse = false;
+ }
+ Vector dir = list[test].normal;
+ Vector point = physcollision->CollideGetExtent( pCurrentCollide, vec3_origin, vec3_angle, dir );
+ float before = DotProduct( dir, point );
+ list[test].inUse = true;
+ pConvex = ConvertToConvex();
+ list[test].inUse = false;
+ CPhysCollide *pCollide = physcollision->ConvertConvexToCollideParams( &pConvex, 1, params );
+ Vector p2 = physcollision->CollideGetExtent( pCollide, vec3_origin, vec3_angle, dir );
+ physcollision->DestroyCollide( pCollide );
+ float after = DotProduct( dir, p2 );
+ list[test].bestDist = fabs(before-after);
+ if ( list[test].bestDist > bestDist )
+ {
+ bestDist = list[test].bestDist;
+ bestIndex = test;
+ }
+ }
+ physcollision->DestroyCollide( pCurrentCollide );
+
+ if ( bestIndex >= 0 && bestDist >= targetTolerance )
+ {
+ list[bestIndex].inUse = true;
+ return true;
+ }
+
+ return false;
+ }
+};
+
+CPhysConvex *SimplifyConvexFromVerts( Vector **verts, int vertCount, bool addAABBToSimplifiedHull, float tolerance, int index )
+{
+ CPhysConvex *pConvex = physcollision->ConvexFromVerts( verts, vertCount );
+ float targetVolume = physcollision->ConvexVolume( pConvex );
+ // can't simplify this polyhedron
+ if ( vertCount <= 8 )
+ return pConvex;
+
+ convexoptimize_t opt;
+ memset( &opt, 0, sizeof(opt));
+ opt.targetTolerance = tolerance;
+ convertconvexparams_t params;
+ params.Defaults();
+ CPhysCollide *pRef = physcollision->ConvertConvexToCollideParams( &pConvex, 1, params );
+ opt.InitPlanes( pRef, addAABBToSimplifiedHull );
+ physcollision->DestroyCollide( pRef );
+
+ // Simplify until you hit the tolerance
+ int i;
+ for ( i = 0; i < vertCount; i++ )
+ {
+ if ( !opt.AddBestPlane() )
+ break;
+ }
+
+ // Create the output shape
+ pConvex = opt.ConvertToConvex();
+ float currentVolume = physcollision->ConvexVolume( pConvex );
+ //Msg("%d iterations, for convex %d\n", i, index );
+
+ return pConvex;
+}
+
+inline int AddVert( Vector **ppVerts, int vertCount, const Vector &newVert )
+{
+ for ( int i = 0; i < vertCount; i++ )
+ {
+ if ( fabs(ppVerts[i]->x - newVert.x) < DIST_EPSILON &&
+ fabs(ppVerts[i]->y - newVert.y) < DIST_EPSILON &&
+ fabs(ppVerts[i]->z - newVert.z) < DIST_EPSILON )
+ return vertCount;
+ }
+ *ppVerts[vertCount] = newVert;
+ return vertCount+1;
+}
+
+void BuildSingleConvex( CPhysConvex **convexListOut, ICollisionQuery *pQuery, Vector **ppVerts, const simplifyparams_t &params )
+{
+ int vertCount = 0;
+ for ( int i = 0; i < pQuery->ConvexCount(); i++ )
+ {
+ Vector v[3];
+ for ( int j = 0; j < pQuery->TriangleCount(i); j++ )
+ {
+ pQuery->GetTriangleVerts( i, j, v );
+ vertCount = AddVert( ppVerts, vertCount, v[0] );
+ vertCount = AddVert( ppVerts, vertCount, v[1] );
+ vertCount = AddVert( ppVerts, vertCount, v[2] );
+ }
+ }
+ convexListOut[0] = SimplifyConvexFromVerts( ppVerts, vertCount, params.addAABBToSimplifiedHull, params.tolerance, 0 );
+ physcollision->SetConvexGameData( convexListOut[0], pQuery->GetGameData( 0 ) );
+}
+
+void SimplifyConvexElements( CPhysConvex **convexListOut, ICollisionQuery *pQuery, Vector **ppVerts, const simplifyparams_t &params )
+{
+ for ( int i = 0; i < pQuery->ConvexCount(); i++ )
+ {
+ int vertCount = 0;
+ Vector v[3];
+ for ( int j = 0; j < pQuery->TriangleCount(i); j++ )
+ {
+ pQuery->GetTriangleVerts( i, j, v );
+ vertCount = AddVert( ppVerts, vertCount, v[0] );
+ vertCount = AddVert( ppVerts, vertCount, v[1] );
+ vertCount = AddVert( ppVerts, vertCount, v[2] );
+ }
+ convexListOut[i] = SimplifyConvexFromVerts( ppVerts, vertCount, params.addAABBToSimplifiedHull, params.tolerance, i );
+ physcollision->SetConvexGameData( convexListOut[i], pQuery->GetGameData( i ) );
+ }
+}
+
+struct mergeconvex_t
+{
+ byte mergeCount;
+ byte list[255];
+};
+
+void MergeElems( CUtlVector<mergeconvex_t> &elems, int index0, int index1 )
+{
+ Assert( index0 < index1 );
+ for (int i = 0; i < elems[index1].mergeCount; i++)
+ {
+ elems[index0].list[i+elems[index0].mergeCount] = elems[index1].list[i];
+ }
+ elems[index0].mergeCount += elems[index1].mergeCount;
+ elems.FastRemove(index1);
+}
+
+int VertsForElem( ICollisionQuery *pQuery, Vector **ppVerts, const mergeconvex_t &elems0, int vertCount )
+{
+ for ( int i = 0; i < elems0.mergeCount; i++ )
+ {
+ int convexId = elems0.list[i];
+ Vector v[3];
+ for ( int j = 0; j < pQuery->TriangleCount(convexId); j++ )
+ {
+ pQuery->GetTriangleVerts( convexId, j, v );
+ vertCount = AddVert( ppVerts, vertCount, v[0] );
+ vertCount = AddVert( ppVerts, vertCount, v[1] );
+ vertCount = AddVert( ppVerts, vertCount, v[2] );
+ }
+ }
+ return vertCount;
+}
+
+void PlanesForElem( ICollisionQuery *pQuery, CUtlVector<float> &planes, const mergeconvex_t &elem0 )
+{
+ for ( int i = 0; i < elem0.mergeCount; i++ )
+ {
+ int convexId = elem0.list[i];
+ Vector v[3];
+ for ( int j = 0; j < pQuery->TriangleCount(convexId); j++ )
+ {
+ pQuery->GetTriangleVerts( convexId, j, v );
+ Vector e0 = v[1]-v[0];
+ Vector e1 = v[2]-v[0];
+ Vector normal = CrossProduct( e1, e0 );
+ VectorNormalize( normal );
+ float dist = DotProduct( normal, v[0] );
+ planes.AddToTail( normal.x );
+ planes.AddToTail( normal.y );
+ planes.AddToTail( normal.z );
+ planes.AddToTail( dist );
+ }
+ }
+}
+
+float ConvexVolumeFromPlanes( CUtlVector<float> &planes )
+{
+ CPhysConvex *pConvex = planes.Count() ? physcollision->ConvexFromPlanes( planes.Base(), planes.Count()/4, DIST_EPSILON ) : NULL;
+ float volume = 0;
+ if ( pConvex )
+ {
+ volume = physcollision->ConvexVolume(pConvex);
+ physcollision->ConvexFree(pConvex);
+ }
+ return volume;
+}
+
+float MergedDeltaVolume( ICollisionQuery *pQuery, Vector **ppVerts, const mergeconvex_t &elem0, const mergeconvex_t &elem1 )
+{
+ // build vert list
+ int vertCount = VertsForElem( pQuery, ppVerts, elem0, 0 );
+ // merge in next element
+ vertCount = VertsForElem( pQuery, ppVerts, elem1, vertCount);
+ CPhysConvex *pConvex = physcollision->ConvexFromVerts( ppVerts, vertCount );
+ float finalVolume = physcollision->ConvexVolume(pConvex);
+ physcollision->ConvexFree(pConvex);
+
+ CUtlVector<float> planes;
+ PlanesForElem( pQuery, planes, elem0 );
+ float vol0 = ConvexVolumeFromPlanes( planes );
+ planes.RemoveAll();
+ PlanesForElem( pQuery, planes, elem1 );
+ float vol1 = ConvexVolumeFromPlanes( planes );
+ PlanesForElem( pQuery, planes, elem0 );
+
+ float volInt = ConvexVolumeFromPlanes( planes );
+
+ return finalVolume - (vol0+vol1-volInt);
+}
+
+int MergeAndSimplifyConvexElements( CPhysConvex **convexListOut, const CPhysCollide *pCollideIn, ICollisionQuery *pQuery, Vector **ppVerts, const simplifyparams_t &params )
+{
+ Assert( pQuery->ConvexCount() < 256 );
+ if ( pQuery->ConvexCount() > 256 )
+ {
+ SimplifyConvexElements(convexListOut, pQuery, ppVerts, params);
+ return pQuery->ConvexCount();
+ }
+
+ CUtlVector<mergeconvex_t> elems;
+ int i;
+ elems.EnsureCount(pQuery->ConvexCount());
+ float totalVolume = physcollision->CollideVolume( (CPhysCollide *)pCollideIn );
+ for ( i = 0; i < pQuery->ConvexCount(); i++ )
+ {
+ elems[i].mergeCount = 1;
+ elems[i].list[0] = i;
+ }
+loop:
+ for ( i = 0; i < elems.Count(); i++ )
+ {
+ for ( int j = i+1; j < elems.Count(); j++ )
+ {
+ float volume = fabs(MergedDeltaVolume( pQuery, ppVerts, elems[i], elems[j] ));
+ volume /= totalVolume;
+ if ( volume < params.mergeConvexTolerance )
+ {
+ MergeElems( elems, i, j );
+ goto loop;
+ }
+ }
+ }
+
+ for ( i = 0; i < elems.Count(); i++ )
+ {
+ int vertCount = VertsForElem( pQuery, ppVerts, elems[i], 0 );
+ convexListOut[i] = SimplifyConvexFromVerts( ppVerts, vertCount, params.addAABBToSimplifiedHull, params.tolerance, i );
+ physcollision->SetConvexGameData( convexListOut[i], pQuery->GetGameData( elems[i].list[0] ) );
+ }
+ return elems.Count();
+}
+
+CPhysCollide *SimplifyCollide( CPhysCollide *pCollideIn, int indexIn, const simplifyparams_t &params )
+{
+ int sizeIn = physcollision->CollideSize( pCollideIn );
+ ICollisionQuery *pQuery = physcollision->CreateQueryModel( pCollideIn );
+ int maxVertCount = 0;
+ int i;
+ for ( i = pQuery->ConvexCount(); --i >= 0; )
+ {
+ int vertCount = pQuery->TriangleCount(i)*3;
+ maxVertCount += vertCount;
+ }
+
+ Vector **ppVerts = new Vector *[maxVertCount];
+ Vector *verts = new Vector[maxVertCount];
+ for ( i = 0; i < maxVertCount; i++ )
+ {
+ ppVerts[i] = &verts[i];
+ }
+
+ int outputConvexCount = params.forceSingleConvex ? 1 : pQuery->ConvexCount();
+ CPhysConvex **convexList = new CPhysConvex *[outputConvexCount];
+ if ( params.forceSingleConvex )
+ {
+ BuildSingleConvex( convexList, pQuery, ppVerts, params );
+ }
+ else if ( params.mergeConvexElements && pQuery->ConvexCount() > 1 )
+ {
+ outputConvexCount = MergeAndSimplifyConvexElements( convexList, pCollideIn, pQuery, ppVerts, params );
+ if ( !g_bQuiet && pQuery->ConvexCount() != outputConvexCount)
+ {
+ Msg("Simplified %d to %d elements\n", pQuery->ConvexCount(), outputConvexCount );
+ }
+ }
+ else
+ {
+ SimplifyConvexElements( convexList, pQuery, ppVerts, params );
+ }
+ convertconvexparams_t params;
+ params.Defaults();
+ params.buildOuterConvexHull = true;
+ params.buildDragAxisAreas = false;
+
+ CPhysCollide *pCollideOut = physcollision->ConvertConvexToCollideParams( convexList, outputConvexCount, params );
+
+ // copy the drag axis areas from the source
+ Vector dragAxisAreas = physcollision->CollideGetOrthographicAreas( pCollideIn );
+ physcollision->CollideSetOrthographicAreas( pCollideOut, dragAxisAreas );
+
+ physcollision->DestroyQueryModel( pQuery );
+ delete[] convexList;
+ delete[] verts;
+ delete[] ppVerts;
+
+ if ( physcollision->CollideSize(pCollideOut) >= sizeIn )
+ {
+ // make a copy of the input collide
+ physcollision->DestroyCollide(pCollideOut);
+ char *pBuf = new char[sizeIn];
+ physcollision->CollideWrite( pBuf, pCollideIn );
+ pCollideOut = physcollision->UnserializeCollide( pBuf, sizeIn, indexIn );
+ delete[] pBuf;
+ }
+ return pCollideOut;
+}
+
diff --git a/utils/xbox/makephx/simplify.h b/utils/xbox/makephx/simplify.h
new file mode 100644
index 0000000..acf2625
--- /dev/null
+++ b/utils/xbox/makephx/simplify.h
@@ -0,0 +1,38 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef SIMPLIFY_H
+#define SIMPLIFY_H
+#pragma once
+
+struct simplifyparams_t
+{
+ float tolerance;
+ bool addAABBToSimplifiedHull;
+ bool forceSingleConvex;
+ bool mergeConvexElements;
+ float mergeConvexTolerance;
+
+ void Defaults()
+ {
+ tolerance = 1.0f;
+ addAABBToSimplifiedHull = false;
+ forceSingleConvex = false;
+ mergeConvexElements = false;
+ mergeConvexTolerance = 0.f;
+ }
+};
+
+extern CPhysCollide *SimplifyCollide( CPhysCollide *pCollideIn, int index, const simplifyparams_t &params );
+
+#endif // SIMPLIFY_H
diff --git a/utils/xbox/makephx/stdafx.cpp b/utils/xbox/makephx/stdafx.cpp
new file mode 100644
index 0000000..c4c1f62
--- /dev/null
+++ b/utils/xbox/makephx/stdafx.cpp
@@ -0,0 +1,9 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// stdafx.cpp : source file that includes just the standard includes
+// makephx.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/utils/xbox/makephx/stdafx.h b/utils/xbox/makephx/stdafx.h
new file mode 100644
index 0000000..db4423e
--- /dev/null
+++ b/utils/xbox/makephx/stdafx.h
@@ -0,0 +1,20 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+
+#include "cmdlib.h"
+#include "mathlib/mathlib.h"
+#include "vphysics_interface.h"
+#include "tier1/strtools.h"
+#include "phyfile.h"
+#include "phxfile.h"
+#include "utlvector.h"
+#include "utlbuffer.h"
+#include "vcollide_parse.h"
+
+// TODO: reference additional headers your program requires here
diff --git a/utils/xbox/makephx/util.cpp b/utils/xbox/makephx/util.cpp
new file mode 100644
index 0000000..cb5e879
--- /dev/null
+++ b/utils/xbox/makephx/util.cpp
@@ -0,0 +1,41 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "mathlib/mathlib.h"
+#include "util.h"
+#include "tier1/strtools.h"
+
+void UTIL_StringToFloatArray( float *pVector, int count, const char *pString )
+{
+ char *pstr, *pfront, tempString[128];
+ int j;
+
+ Q_strncpy( tempString, pString, sizeof(tempString) );
+ pstr = pfront = tempString;
+
+ for ( j = 0; j < count; j++ ) // lifted from pr_edict.c
+ {
+ pVector[j] = atof( pfront );
+
+ // skip any leading whitespace
+ while ( *pstr && *pstr <= ' ' )
+ pstr++;
+
+ // skip to next whitespace
+ while ( *pstr && *pstr > ' ' )
+ pstr++;
+
+ if (!*pstr)
+ break;
+
+ pstr++;
+ pfront = pstr;
+ }
+ for ( j++; j < count; j++ )
+ {
+ pVector[j] = 0;
+ }
+}
+
+void UTIL_StringToVector( float *pVector, const char *pString )
+{
+ UTIL_StringToFloatArray( pVector, 3, pString );
+}
diff --git a/utils/xbox/makephx/util.h b/utils/xbox/makephx/util.h
new file mode 100644
index 0000000..8d389a3
--- /dev/null
+++ b/utils/xbox/makephx/util.h
@@ -0,0 +1,16 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef UTIL_H
+#define UTIL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+extern void UTIL_StringToFloatArray( float *pVector, int count, const char *pString );
+extern void UTIL_StringToVector( float *pVector, const char *pString );
+
+#endif // UTIL_H