Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. #include "stdafx.h"
  3. #include "physdll.h"
  4. #include "vphysics/constraints.h"
  5. #include "tier0/icommandline.h"
  6. #include "filesystem_tools.h"
  7. #include "simplify.h"
  8. #include "keyvalues.h"
  9. #include "studio.h"
  10. IPhysicsCollision *physcollision = NULL;
  11. IPhysicsSurfaceProps *physprops = NULL;
  12. int g_TotalOut = 0;
  13. int g_TotalCompress = 0;
  14. bool g_bRecursive = false;
  15. bool g_bQuiet = false;
  16. KeyValues *g_pModelConfig = NULL;
  17. void InitFilesystem( const char *pPath )
  18. {
  19. CmdLib_InitFileSystem( pPath );
  20. // This bit of hackery allows us to access files on the harddrive
  21. g_pFullFileSystem->AddSearchPath( "", "LOCAL", PATH_ADD_TO_HEAD );
  22. }
  23. static bool LoadSurfaceProps( const char *pMaterialFilename )
  24. {
  25. if ( !physprops )
  26. return false;
  27. FileHandle_t fp = g_pFileSystem->Open( pMaterialFilename, "rb", TOOLS_READ_PATH_ID );
  28. if ( fp == FILESYSTEM_INVALID_HANDLE )
  29. return false;
  30. int len = g_pFileSystem->Size( fp );
  31. char *pText = new char[len+1];
  32. g_pFileSystem->Read( pText, len, fp );
  33. g_pFileSystem->Close( fp );
  34. pText[len]=0;
  35. physprops->ParseSurfaceData( pMaterialFilename, pText );
  36. delete[] pText;
  37. return true;
  38. }
  39. void LoadSurfacePropsAll()
  40. {
  41. // already loaded
  42. if ( physprops->SurfacePropCount() )
  43. return;
  44. const char *SURFACEPROP_MANIFEST_FILE = "scripts/surfaceproperties_manifest.txt";
  45. KeyValues *manifest = new KeyValues( SURFACEPROP_MANIFEST_FILE );
  46. if ( manifest->LoadFromFile( g_pFileSystem, SURFACEPROP_MANIFEST_FILE, "GAME" ) )
  47. {
  48. for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
  49. {
  50. if ( !Q_stricmp( sub->GetName(), "file" ) )
  51. {
  52. // Add
  53. LoadSurfaceProps( sub->GetString() );
  54. continue;
  55. }
  56. }
  57. }
  58. manifest->deleteThis();
  59. }
  60. void InitVPhysics()
  61. {
  62. CreateInterfaceFn physicsFactory = GetPhysicsFactory();
  63. physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
  64. physprops = (IPhysicsSurfaceProps *)physicsFactory( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, NULL );
  65. LoadSurfacePropsAll();
  66. }
  67. struct phyfile_t
  68. {
  69. phyheader_t header;
  70. vcollide_t collide;
  71. int fileSize;
  72. };
  73. void LoadPHYFile(phyfile_t *pOut, const char *name)
  74. {
  75. memset( pOut, 0, sizeof(*pOut) );
  76. FileHandle_t file = g_pFullFileSystem->Open( name, "rb" );
  77. if ( !file )
  78. return;
  79. g_pFullFileSystem->Read( &pOut->header, sizeof(pOut->header), file );
  80. if ( pOut->header.size != sizeof(pOut->header) || pOut->header.solidCount <= 0 )
  81. return;
  82. pOut->fileSize = g_pFullFileSystem->Size( file );
  83. char *buf = (char *)_alloca( pOut->fileSize );
  84. g_pFullFileSystem->Read( buf, pOut->fileSize, file );
  85. g_pFullFileSystem->Close( file );
  86. physcollision->VCollideLoad( &pOut->collide, pOut->header.solidCount, (const char *)buf, pOut->fileSize );
  87. }
  88. void OverrideDefaultsForModel( const char *keyname, simplifyparams_t &params )
  89. {
  90. KeyValues *pKeys = g_pModelConfig;
  91. while ( pKeys )
  92. {
  93. if ( !Q_stricmp( pKeys->GetName(), keyname ) )
  94. {
  95. for ( KeyValues *pData = pKeys->GetFirstSubKey(); pData; pData = pData->GetNextKey() )
  96. {
  97. if ( !Q_stricmp( pData->GetName(), "tolerance" ) )
  98. {
  99. params.tolerance = pData->GetFloat();
  100. if (!g_bQuiet)
  101. {
  102. Msg("%s: tolerance set to %.2f\n", keyname, params.tolerance );
  103. }
  104. }
  105. else if ( !Q_stricmp( pData->GetName(), "addAABB" ) )
  106. {
  107. params.addAABBToSimplifiedHull = pData->GetInt() ? true : false;
  108. if (!g_bQuiet)
  109. {
  110. Msg("%s: AABB %s\n", keyname, params.addAABBToSimplifiedHull ? "on" : "off" );
  111. }
  112. }
  113. else if ( !Q_stricmp( pData->GetName(), "singleconvex" ) )
  114. {
  115. params.forceSingleConvex = pData->GetInt() ? true : false;
  116. if (!g_bQuiet)
  117. {
  118. Msg("%s: Forced to single convex\n", keyname );
  119. }
  120. }
  121. else if ( !Q_stricmp( pData->GetName(), "mergeconvex" ) )
  122. {
  123. params.mergeConvexTolerance = pData->GetFloat();
  124. params.mergeConvexElements = params.mergeConvexTolerance > 0 ? true : false;
  125. if (!g_bQuiet)
  126. {
  127. Msg("%s: Merge convex %.2f\n", keyname, params.mergeConvexTolerance );
  128. }
  129. }
  130. }
  131. return;
  132. }
  133. pKeys = pKeys->GetNextKey();
  134. }
  135. }
  136. bool HasMultipleBones( const char *pFilename )
  137. {
  138. char outName[1024];
  139. studiohdr_t hdr;
  140. Q_strncpy( outName, pFilename, sizeof(outName) );
  141. Q_SetExtension( outName, ".mdl", sizeof(outName) );
  142. FileHandle_t fp = g_pFileSystem->Open( outName, "rb", TOOLS_READ_PATH_ID );
  143. if ( fp == FILESYSTEM_INVALID_HANDLE )
  144. return false;
  145. g_pFileSystem->Read( &hdr, sizeof(hdr), fp );
  146. g_pFileSystem->Close( fp );
  147. if ( hdr.numbones > 1 )
  148. return true;
  149. return false;
  150. }
  151. void WritePHXFile( const char *pName, const phyfile_t &file )
  152. {
  153. if ( file.header.size != sizeof(file.header) || file.collide.solidCount <= 0 )
  154. return;
  155. CUtlBuffer out;
  156. char outName[1024];
  157. Q_snprintf( outName, sizeof(outName), "%s", pName );
  158. Q_SetExtension( outName, ".phx", sizeof(outName) );
  159. simplifyparams_t params;
  160. params.Defaults();
  161. params.tolerance = (file.collide.solidCount > 1) ? 4.0f : 2.0f;
  162. // single solids constraint to AABB for placement help
  163. params.addAABBToSimplifiedHull = (file.collide.solidCount == 1) ? true : false;
  164. params.mergeConvexElements = true;
  165. params.mergeConvexTolerance = 0.025f;
  166. Q_FixSlashes(outName);
  167. Q_strlower(outName);
  168. char *pSearch = Q_strstr( outName,"models\\" );
  169. if ( pSearch )
  170. {
  171. char keyname[1024];
  172. pSearch += strlen("models\\");
  173. Q_StripExtension( pSearch, keyname, sizeof(keyname) );
  174. OverrideDefaultsForModel( keyname, params );
  175. }
  176. out.Put( &file.header, sizeof(file.header) );
  177. int outSize = 0;
  178. bool bStoreSolidNames = file.collide.solidCount > 1 ? true : false;
  179. bStoreSolidNames = bStoreSolidNames || HasMultipleBones(outName);
  180. vcollide_t *pNewCollide = ConvertVCollideToPHX( &file.collide, params, &outSize, false, bStoreSolidNames);
  181. g_TotalOut += file.fileSize;
  182. for ( int i = 0; i < pNewCollide->solidCount; i++ )
  183. {
  184. int collideSize = physcollision->CollideSize( pNewCollide->solids[i] );
  185. out.PutInt( collideSize );
  186. char *pMem = new char[collideSize];
  187. physcollision->CollideWrite( pMem, pNewCollide->solids[i] );
  188. out.Put( pMem, collideSize );
  189. delete[] pMem;
  190. }
  191. if (!g_bQuiet)
  192. {
  193. Msg("%s Compressed %d (%d text) to %d (%d text)\n", outName, file.fileSize, file.collide.descSize, out.TellPut(), pNewCollide->descSize );
  194. }
  195. out.Put( pNewCollide->pKeyValues, pNewCollide->descSize );
  196. g_TotalCompress += out.TellPut();
  197. #if 0
  198. //Msg("OLD:\n-----------------------------------\n%s\n", file.collide.pKeyValues );
  199. CPackedPhysicsDescription *pPacked = physcollision->CreatePackedDesc( pNewCollide->pKeyValues, pNewCollide->descSize );
  200. Msg("NEW:\n-----------------------------------\n" );
  201. for ( int i = 0; i < pPacked->m_solidCount; i++ )
  202. {
  203. solid_t solid;
  204. pPacked->GetSolid( &solid, i );
  205. Msg("index %d\n", solid.index );
  206. Msg("name %s\n", solid.name );
  207. Msg("mass %.2f\n", solid.params.mass );
  208. Msg("surfaceprop %s\n", solid.surfaceprop);
  209. Msg("damping %.2f\n", solid.params.damping );
  210. Msg("rotdamping %.2f\n", solid.params.rotdamping );
  211. Msg("drag %.2f\n", solid.params.dragCoefficient );
  212. Msg("inertia %.2f\n", solid.params.inertia );
  213. Msg("volume %.2f\n", solid.params.volume );
  214. }
  215. #endif
  216. DestroyPHX( pNewCollide );
  217. if ( !g_pFullFileSystem->WriteFile( outName, NULL, out ) )
  218. Warning("Can't write file: %s\n", outName );
  219. }
  220. void UnloadPHYFile( phyfile_t *pFile )
  221. {
  222. physcollision->VCollideUnload( &pFile->collide );
  223. pFile->header.size = 0;
  224. }
  225. void MakeFilename( char *pDest, int destSize, const char *pPathname, const char *pFilenameExt )
  226. {
  227. Q_strncpy(pDest, pPathname, destSize);
  228. Q_AppendSlash(pDest, destSize);
  229. Q_strncat(pDest, pFilenameExt, destSize);
  230. }
  231. void MakeDirname( char *pDest, int destSize, const char *pPathname, const char *pSubdir )
  232. {
  233. MakeFilename(pDest, destSize , pPathname, pSubdir);
  234. }
  235. int main( int argc, char *argv[] )
  236. {
  237. if ( argc < 2 )
  238. {
  239. Msg("Usage:\nmakephx [options] <FILESPEC>\ne.g. makephx [-r] *.phy\n");
  240. return 0;
  241. }
  242. CommandLine()->CreateCmdLine( argc, argv );
  243. g_bRecursive = CommandLine()->FindParm("-r") > 0 ? true : false;
  244. g_bQuiet = CommandLine()->FindParm("-quiet") > 0 ? true : false;
  245. InitFilesystem( "*.*" );
  246. InitVPhysics();
  247. // disable automatic packing, we want to do this ourselves.
  248. physcollision->SetPackOnLoad( false );
  249. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
  250. InstallSpewFunction();
  251. g_pModelConfig = new KeyValues("config");
  252. g_pModelConfig->LoadFromFile( g_pFullFileSystem, "phx.cfg", "GAME" );
  253. g_TotalOut = 0;
  254. g_TotalCompress = 0;
  255. FileFindHandle_t handle;
  256. char fullpath[1024], currentFile[1024], dirName[1024], nameext[256];
  257. strcpy( fullpath, argv[argc-1] );
  258. strcpy( fullpath, ExpandPath( fullpath ) );
  259. strcpy( fullpath, ExpandArg( fullpath ) );
  260. Q_strncpy(dirName, fullpath, sizeof(dirName));
  261. Q_StripFilename(dirName);
  262. Q_strncpy(nameext, fullpath + strlen(dirName)+1, sizeof(nameext));
  263. CUtlVector< const char * > directoryList;
  264. directoryList.AddToTail( strdup(dirName) );
  265. int current = 0;
  266. int count = 0;
  267. do
  268. {
  269. if ( g_bRecursive )
  270. {
  271. MakeFilename( currentFile, sizeof(currentFile), directoryList[current], "*.*" );
  272. const char *pFilename = g_pFullFileSystem->FindFirst( currentFile, &handle );
  273. while ( pFilename )
  274. {
  275. if ( pFilename[0] != '.' && g_pFullFileSystem->FindIsDirectory( handle ) )
  276. {
  277. MakeDirname( currentFile, sizeof(currentFile), directoryList[current], pFilename );
  278. directoryList.AddToTail(strdup(currentFile));
  279. }
  280. pFilename = g_pFullFileSystem->FindNext( handle );
  281. }
  282. g_pFullFileSystem->FindClose( handle );
  283. }
  284. MakeFilename(currentFile, sizeof(currentFile), directoryList[current], nameext);
  285. const char *pFilename = g_pFullFileSystem->FindFirst( currentFile, &handle );
  286. while ( pFilename )
  287. {
  288. phyfile_t phy;
  289. MakeFilename(currentFile, sizeof(currentFile), directoryList[current], pFilename);
  290. LoadPHYFile( &phy, currentFile );
  291. if ( phy.collide.isPacked || phy.collide.solidCount < 1 )
  292. {
  293. Msg("%s is not a valid PHY file\n", currentFile );
  294. }
  295. else
  296. {
  297. WritePHXFile( currentFile, phy );
  298. count++;
  299. }
  300. UnloadPHYFile( &phy );
  301. pFilename = g_pFullFileSystem->FindNext( handle );
  302. }
  303. g_pFullFileSystem->FindClose( handle );
  304. current++;
  305. } while( current < directoryList.Count() );
  306. if ( count )
  307. {
  308. if (!g_bQuiet)
  309. {
  310. Msg("\n------\nTotal %s, %s\nSaved %s\n", Q_pretifymem( g_TotalOut ), Q_pretifymem( g_TotalCompress ), Q_pretifymem( g_TotalOut - g_TotalCompress ) );
  311. Msg("%.2f%% savings\n", ((float)(g_TotalOut-g_TotalCompress) / (float)g_TotalOut) * 100.0f );
  312. }
  313. }
  314. else
  315. {
  316. Msg("No files found in %s!\n", directoryList[current] );
  317. }
  318. return 0;
  319. }