Counter Strike : Global Offensive Source Code
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.

4264 lines
124 KiB

  1. //====== Copyright (c) 1996-2007, Valve Corporation, All rights reserved. =======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. #include <malloc.h>
  10. #include <string.h>
  11. #include "tier1/strtools.h"
  12. #include <sys/stat.h>
  13. #include "bitmap/bitmap.h"
  14. #include "bitmap/TGALoader.h"
  15. #include "bitmap/psd.h"
  16. #include "bitmap/floatbitmap.h"
  17. #include "bitmap/imageformat.h"
  18. #include "mathlib/mathlib.h"
  19. #ifdef PLATFORM_POSIX
  20. #include <sys/stat.h>
  21. #define _stat stat
  22. #endif
  23. #ifdef PLATFORM_WINDOWS
  24. #include "conio.h"
  25. #include <direct.h>
  26. #include <io.h>
  27. #endif
  28. #include "vtf/vtf.h"
  29. #include "UtlBuffer.h"
  30. #include "tier0/dbg.h"
  31. #include "cmdlib.h"
  32. #include "tier0/icommandline.h"
  33. #ifdef PLATFORM_WINDOWS
  34. #include "windows.h"
  35. #endif
  36. #include "ilaunchabledll.h"
  37. #include "ivtex.h"
  38. #include "appframework/IAppSystemGroup.h"
  39. #include "datamodel/dmelement.h"
  40. #include "materialobjects/dmetexture.h"
  41. #include "tier2/tier2dm.h"
  42. #include "tier2/p4helpers.h"
  43. #include "p4lib/ip4.h"
  44. #include "datamodel/dmelementfactoryhelper.h"
  45. #include "resourcesystem/iresourcecompiler.h"
  46. #include "rendersystem/schema/texture.g.h"
  47. #include "materialobjects/dmeprecompiledtexture.h"
  48. #include "vstdlib/jobthread.h"
  49. #include "tier1/checksum_crc.h"
  50. #include "tier1/keyvalues.h"
  51. #define FF_PROCESS 1
  52. #define FF_TRYAGAIN 2
  53. #define FF_DONTPROCESS 3
  54. #define LOWRESIMAGE_DIM 16
  55. #ifdef PLATFORM_POSIX
  56. #define LOWRES_IMAGE_FORMAT IMAGE_FORMAT_RGBA8888
  57. #else
  58. #define LOWRES_IMAGE_FORMAT IMAGE_FORMAT_DXT1
  59. #endif
  60. //#define DEBUG_NO_COMPRESSION
  61. static bool g_NoPause = false;
  62. static bool g_Quiet = false;
  63. static const char *g_ShaderName = NULL;
  64. static bool g_CreateDir = true;
  65. static bool g_UseGameDir = true;
  66. static bool g_bUseStandardError = false;
  67. static bool g_bWarningsAsErrors = false;
  68. static bool g_bUsedAsLaunchableDLL = false;
  69. static bool g_bNoTga = false;
  70. static bool g_bNoPsd = false;
  71. static bool g_bUsePfm = false;
  72. static bool g_bSupportsXBox360 = false;
  73. static char g_ForcedOutputDir[MAX_PATH];
  74. static bool g_bOldCubemapPath = false;
  75. #define MAX_VMT_PARAMS 16
  76. struct VTexVMTParam_t
  77. {
  78. const char *m_szParam;
  79. const char *m_szValue;
  80. };
  81. class SmartIVTFTexture
  82. {
  83. public:
  84. explicit SmartIVTFTexture( IVTFTexture *pVtf ) : m_p( pVtf ) {}
  85. ~SmartIVTFTexture() { if ( m_p ) DestroyVTFTexture( m_p ); }
  86. private:
  87. SmartIVTFTexture( SmartIVTFTexture const &x );
  88. SmartIVTFTexture & operator = ( SmartIVTFTexture const &x );
  89. private:
  90. SmartIVTFTexture & operator = ( IVTFTexture *pVtf ) { m_p = pVtf; }
  91. operator IVTFTexture * () const { return m_p; }
  92. public:
  93. IVTFTexture * Assign( IVTFTexture *pVtfNew ) { IVTFTexture *pOld = m_p; m_p = pVtfNew; return pOld; }
  94. IVTFTexture * Get() const { return m_p; }
  95. IVTFTexture * operator->() const { return m_p; }
  96. protected:
  97. IVTFTexture *m_p;
  98. };
  99. struct OutputTexture_t
  100. {
  101. IVTFTexture * pTexture;
  102. char dstFileName[ MAX_PATH ];
  103. };
  104. static VTexVMTParam_t g_VMTParams[MAX_VMT_PARAMS];
  105. static int g_NumVMTParams = 0;
  106. static BitmapFileType_t g_eMode = BITMAP_FILE_TYPE_PSD;
  107. // NOTE: these must stay in the same order as CubeMapFaceIndex_t.
  108. static const char *g_CubemapFacingNames[7] = { "rt", "lf", "bk", "ft", "up", "dn", "sph" };
  109. static void Pause( void )
  110. {
  111. if( !g_NoPause )
  112. {
  113. printf( "\nHit a key to continue\n" );
  114. #ifdef PLATFORM_WINDOWS
  115. getch();
  116. #endif
  117. }
  118. }
  119. static bool VTexErrorAborts()
  120. {
  121. if ( CommandLine()->FindParm( "-crcvalidate" ) )
  122. return false;
  123. return true;
  124. }
  125. #if defined( _DEBUG ) && defined( _WIN32 )
  126. #define DebuggerOutput2(x, y) (void)( OutputDebugString( x ), OutputDebugString( y ) )
  127. #else
  128. #define DebuggerOutput2(x, y) (void)( (x), (y) )
  129. #endif
  130. static void VTexError( const char *pFormat, ... )
  131. {
  132. char str[4096];
  133. va_list marker;
  134. va_start( marker, pFormat );
  135. Q_vsnprintf( str, sizeof( str ), pFormat, marker );
  136. va_end( marker );
  137. DebuggerOutput2( "[VTEXDBG] ERROR: ", str );
  138. if ( !VTexErrorAborts() )
  139. {
  140. fprintf( stderr, "ERROR: %s", str );
  141. return;
  142. }
  143. if ( g_bUseStandardError )
  144. {
  145. Error( "ERROR: %s", str );
  146. }
  147. else
  148. {
  149. fprintf( stderr, "ERROR: %s", str );
  150. Pause();
  151. exit( 1 );
  152. }
  153. }
  154. static void VTexWarning( const char *pFormat, ... )
  155. {
  156. char str[4096];
  157. va_list marker;
  158. va_start( marker, pFormat );
  159. Q_vsnprintf( str, sizeof( str ), pFormat, marker );
  160. va_end( marker );
  161. DebuggerOutput2( "[VTEXDBG] WARNING: ", str );
  162. if ( g_bWarningsAsErrors )
  163. {
  164. VTexError( "%s", str );
  165. }
  166. else
  167. {
  168. fprintf( stderr, "WARNING: %s", str );
  169. Pause();
  170. }
  171. }
  172. static void VTexWarningNoPause( const char *pFormat, ... )
  173. {
  174. char str[4096];
  175. va_list marker;
  176. va_start( marker, pFormat );
  177. Q_vsnprintf( str, sizeof( str ), pFormat, marker );
  178. va_end( marker );
  179. DebuggerOutput2( "[VTEXDBG] WARNING: ", str );
  180. if ( g_bWarningsAsErrors )
  181. {
  182. VTexError( "%s", str );
  183. }
  184. else
  185. {
  186. fprintf( stderr, "WARNING: %s", str );
  187. }
  188. }
  189. static void VTexMsg( const char *pFormat, ... )
  190. {
  191. char str[4096];
  192. va_list marker;
  193. va_start( marker, pFormat );
  194. Q_vsnprintf( str, sizeof( str ), pFormat, marker );
  195. va_end( marker );
  196. DebuggerOutput2( "[VTEXDBG] MSG: ", str );
  197. fprintf( stdout, "%s", str );
  198. }
  199. static void VTexMsgEx( FILE *fout, const char *pFormat, ... )
  200. {
  201. char str[4096];
  202. va_list marker;
  203. va_start( marker, pFormat );
  204. Q_vsnprintf( str, sizeof( str ), pFormat, marker );
  205. va_end( marker );
  206. DebuggerOutput2( "[VTEXDBG] MSG: ", str );
  207. fprintf( fout, "%s", str );
  208. }
  209. struct VTexConfigInfo_t
  210. {
  211. int m_nStartFrame = -1;
  212. int m_nEndFrame = -1;
  213. unsigned int m_nFlags = 0;
  214. float m_flBumpScale = 1.0;
  215. bool m_bNormalToDuDv = false;
  216. bool m_bNormalToDXT5GA = false;
  217. bool m_bNormalInvertGreen = false;
  218. bool m_bAlphaToLuminance = false;
  219. bool m_bDuDv = false;
  220. float m_flAlphaThreshhold = -1.0f;
  221. float m_flAlphaHiFreqThreshhold = -1.0f;
  222. int m_nVolumeTextureDepth = 1;
  223. float m_pfmscale = 1.0;
  224. bool m_bStripAlphaChannel = false;
  225. bool m_bStripColorChannel = false;
  226. bool m_bIsCubeMap = false;
  227. bool m_bIsSkyBox = false;
  228. bool m_bIsCroppedSkyBox = false;
  229. bool m_bManualMip = false;
  230. bool m_bDisplacementMap = false;
  231. bool m_bDisplacementWrinkleMap = false;
  232. // scaling parameters
  233. int m_nReduceX = 1;
  234. int m_nReduceY = 1;
  235. int m_nMaxDimensionX = -1;
  236. int m_nMaxDimensionX_360 = -1;
  237. int m_nMaxDimensionY = -1;
  238. int m_nMaxDimensionY_360 = -1;
  239. // may restrict the texture to reading only 3 channels
  240. int m_numChannelsMax = 4;
  241. bool m_bAlphaToDistance = false;
  242. float m_flDistanceSpread = 1.0; // how far to stretch out distance range in pixels
  243. CRC32_t m_uiInputHash; // Sources hash
  244. TextureSettingsEx_t m_exSettings0;
  245. VtfProcessingOptions m_vtfProcOptions;
  246. CUtlVector<char *> m_pVolumeTextureFileNames;
  247. enum
  248. {
  249. // CRC of input files:
  250. // txt + tga/pfm
  251. // or
  252. // psd
  253. VTF_INPUTSRC_CRC = MK_VTF_RSRC_ID( 'C','R','C' )
  254. };
  255. char m_SrcName[MAX_PATH];
  256. VTexConfigInfo_t( void )
  257. {
  258. memset( &m_exSettings0, 0, sizeof( m_exSettings0 ) );
  259. memset( &m_vtfProcOptions, 0, sizeof( m_vtfProcOptions ) );
  260. m_vtfProcOptions.cbSize = sizeof( m_vtfProcOptions );
  261. m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_FILTER_NICE;
  262. CRC32_Init( &m_uiInputHash );
  263. }
  264. bool IsSettings0Valid( void ) const
  265. {
  266. TextureSettingsEx_t exSettingsEmpty;
  267. memset( &exSettingsEmpty, 0, sizeof( exSettingsEmpty ) );
  268. Assert( sizeof( m_exSettings0 ) == sizeof( exSettingsEmpty ) );
  269. return !!memcmp( &m_exSettings0, &exSettingsEmpty, sizeof( m_exSettings0 ) );
  270. }
  271. // returns false if unrecognized option
  272. void ParseOptionKey( const char *pKeyName, const char *pKeyValue );
  273. void ParseVolumeOption( const char *pKeyValue );
  274. };
  275. template < typename T >
  276. static inline T& SetFlagValueT( T &field, T const &flag, int bSetFlag )
  277. {
  278. if ( bSetFlag )
  279. field |= flag;
  280. else
  281. field &=~flag;
  282. return field;
  283. }
  284. static inline uint32& SetFlagValue( uint32 &field, uint32 const &flag, int bSetFlag )
  285. {
  286. return SetFlagValueT<uint32>( field, flag, bSetFlag );
  287. }
  288. void VTexConfigInfo_t::ParseVolumeOption( const char *pKeyValue )
  289. {
  290. pKeyValue += strspn( pKeyValue, " \t" );
  291. if ( strchr( pKeyValue, ',' ) == 0 ) // its just a single wor,d not a list of filenames
  292. {
  293. m_nVolumeTextureDepth = atoi( pKeyValue );
  294. }
  295. else
  296. {
  297. V_SplitString( pKeyValue, ",", m_pVolumeTextureFileNames );
  298. m_nVolumeTextureDepth = m_pVolumeTextureFileNames.Count();
  299. printf("depth=%d\n", m_pVolumeTextureFileNames.Count() );
  300. }
  301. // FIXME: Volume textures don't currently support NICE filtering
  302. m_vtfProcOptions.flags0 &= ~VtfProcessingOptions::OPT_FILTER_NICE;
  303. // Volume textures not supported for manual mip painting
  304. m_bManualMip = false;
  305. }
  306. void VTexConfigInfo_t::ParseOptionKey( const char *pKeyName, const char *pKeyValue )
  307. {
  308. int iValue = atoi( pKeyValue ); // To properly have "clamps 0" and not enable the clamping
  309. if ( !stricmp( pKeyName, "skybox" ) )
  310. {
  311. // We're going to treat it like a cubemap until the very end (we have to load and process all cubemap
  312. // faces at once, so we can match their edges with the texture compression and mipmapping).
  313. m_bIsSkyBox = iValue ? true : false;
  314. m_bIsCubeMap = iValue ? true : false;
  315. if ( !g_Quiet && iValue )
  316. Msg( "'skybox' detected. Treating skybox like a cubemap for edge-matching purposes.\n" );
  317. }
  318. else if ( !stricmp( pKeyName, "skyboxcropped" ) )
  319. {
  320. m_bIsCroppedSkyBox = iValue ? true : false;
  321. if ( !g_Quiet && iValue )
  322. Msg( "'skyboxcropped' detected. Will output half-height front/back/left/right images and a 4x4 'down' image.\n" );
  323. }
  324. else if ( !stricmp( pKeyName, "cubemap" ) )
  325. {
  326. m_bIsCubeMap = iValue ? true : false;
  327. if ( !g_Quiet && iValue )
  328. Msg( "'cubemap' detected.\n" );
  329. }
  330. else if( !stricmp( pKeyName, "startframe" ) )
  331. {
  332. m_nStartFrame = atoi( pKeyValue );
  333. }
  334. else if( !stricmp( pKeyName, "endframe" ) )
  335. {
  336. m_nEndFrame = atoi( pKeyValue );
  337. }
  338. else if( !stricmp( pKeyName, "volumetexture" ) )
  339. {
  340. ParseVolumeOption( pKeyValue );
  341. }
  342. else if( !stricmp( pKeyName, "bumpscale" ) )
  343. {
  344. m_flBumpScale = atof( pKeyValue );
  345. }
  346. else if( !stricmp( pKeyName, "pointsample" ) )
  347. {
  348. SetFlagValue( m_nFlags, TEXTUREFLAGS_POINTSAMPLE, iValue );
  349. }
  350. else if( !stricmp( pKeyName, "trilinear" ) )
  351. {
  352. SetFlagValue( m_nFlags, TEXTUREFLAGS_TRILINEAR, iValue );
  353. }
  354. else if( !stricmp( pKeyName, "clamps" ) )
  355. {
  356. SetFlagValue( m_nFlags, TEXTUREFLAGS_CLAMPS, iValue );
  357. }
  358. else if( !stricmp( pKeyName, "clampt" ) )
  359. {
  360. SetFlagValue( m_nFlags, TEXTUREFLAGS_CLAMPT, iValue );
  361. }
  362. else if( !stricmp( pKeyName, "clampu" ) )
  363. {
  364. SetFlagValue( m_nFlags, TEXTUREFLAGS_CLAMPU, iValue );
  365. }
  366. else if( !stricmp( pKeyName, "border" ) )
  367. {
  368. SetFlagValue( m_nFlags, TEXTUREFLAGS_BORDER, iValue );
  369. // Gets applied to s, t and u We currently assume black border color
  370. }
  371. else if( !stricmp( pKeyName, "anisotropic" ) )
  372. {
  373. SetFlagValue( m_nFlags, TEXTUREFLAGS_ANISOTROPIC, iValue );
  374. }
  375. else if( !stricmp( pKeyName, "dxt5" ) )
  376. {
  377. SetFlagValue( m_nFlags, TEXTUREFLAGS_HINT_DXT5, iValue );
  378. }
  379. else if( !stricmp( pKeyName, "nocompress" ) )
  380. {
  381. SetFlagValue( m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_NOCOMPRESS, iValue );
  382. }
  383. else if( !stricmp( pKeyName, "normal" ) )
  384. {
  385. SetFlagValue( m_nFlags, TEXTUREFLAGS_NORMAL, iValue );
  386. // Normal maps not supported for manual mip painting
  387. m_bManualMip = false;
  388. }
  389. else if( !stricmp( pKeyName, "normalga" ) )
  390. {
  391. m_bNormalToDXT5GA = iValue ? true : false;
  392. SetFlagValue( m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_NORMAL_GA, iValue );
  393. }
  394. else if( !stricmp( pKeyName, "invertgreen" ) )
  395. {
  396. m_bNormalInvertGreen = iValue ? true : false;
  397. if ( !g_Quiet && iValue )
  398. Msg( "'invertgreen' detected, assuming this is a normal map authored in Zbrush, Modo, Crazybump, etc.\n" );
  399. }
  400. else if( !stricmp( pKeyName, "ssbump" ) )
  401. {
  402. SetFlagValue( m_nFlags, TEXTUREFLAGS_SSBUMP, iValue );
  403. }
  404. else if( !stricmp( pKeyName, "nomip" ) )
  405. {
  406. SetFlagValue( m_nFlags, TEXTUREFLAGS_NOMIP, iValue );
  407. m_bManualMip = false;
  408. }
  409. else if( !stricmp( pKeyName, "allmips" ) )
  410. {
  411. SetFlagValue( m_nFlags, TEXTUREFLAGS_ALL_MIPS, iValue );
  412. }
  413. else if( !stricmp( pKeyName, "mostmips" ) )
  414. {
  415. SetFlagValue( m_nFlags, TEXTUREFLAGS_MOST_MIPS, iValue );
  416. }
  417. else if( !stricmp( pKeyName, "nonice" ) )
  418. {
  419. SetFlagValue( m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_FILTER_NICE, !iValue );
  420. }
  421. else if( !stricmp( pKeyName, "nolod" ) )
  422. {
  423. SetFlagValue( m_nFlags, TEXTUREFLAGS_NOLOD, iValue );
  424. }
  425. else if( !stricmp( pKeyName, "procedural" ) )
  426. {
  427. SetFlagValue( m_nFlags, TEXTUREFLAGS_PROCEDURAL, iValue );
  428. }
  429. else if( !stricmp( pKeyName, "alphatest" ) )
  430. {
  431. SetFlagValue( m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_MIP_ALPHATEST, iValue );
  432. }
  433. else if( !stricmp( pKeyName, "alphatest_threshhold" ) )
  434. {
  435. m_flAlphaThreshhold = atof( pKeyValue );
  436. }
  437. else if( !stricmp( pKeyName, "alphatest_hifreq_threshhold" ) )
  438. {
  439. m_flAlphaHiFreqThreshhold = atof( pKeyValue );
  440. }
  441. else if( !stricmp( pKeyName, "rendertarget" ) )
  442. {
  443. SetFlagValue( m_nFlags, TEXTUREFLAGS_RENDERTARGET, iValue );
  444. }
  445. else if ( !stricmp( pKeyName, "numchannels" ) )
  446. {
  447. m_numChannelsMax = atoi( pKeyValue );
  448. }
  449. else if ( !stricmp( pKeyName, "nodebug" ) )
  450. {
  451. SetFlagValue( m_nFlags, TEXTUREFLAGS_NODEBUGOVERRIDE, iValue );
  452. }
  453. else if ( !stricmp( pKeyName, "singlecopy" ) )
  454. {
  455. SetFlagValue( m_nFlags, TEXTUREFLAGS_SINGLECOPY, iValue );
  456. }
  457. else if( !stricmp( pKeyName, "oneovermiplevelinalpha" ) )
  458. {
  459. SetFlagValue( m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_SET_ALPHA_ONEOVERMIP, iValue );
  460. }
  461. else if( !stricmp( pKeyName, "premultalphabymiplevelfraction" ) )
  462. {
  463. SetFlagValue( m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_PREMULT_ALPHA_MIPFRACTION, iValue );
  464. }
  465. else if( !stricmp( pKeyName, "premultalphabymiplevelfraction_maxalphamiplevel" ) )
  466. {
  467. m_vtfProcOptions.fullAlphaAtMipLevel = atoi( pKeyValue );
  468. }
  469. else if( !stricmp( pKeyName, "premultalphabymiplevelfraction_minalphaperpixel" ) )
  470. {
  471. m_vtfProcOptions.minAlpha = atoi( pKeyValue );
  472. }
  473. else if( !stricmp( pKeyName, "premultcolorbyoneovermiplevel" ) )
  474. {
  475. SetFlagValue( m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_PREMULT_COLOR_ONEOVERMIP, iValue );
  476. }
  477. else if ( !stricmp( pKeyName, "normaltodudv" ) )
  478. {
  479. m_bNormalToDuDv = iValue ? true : false;
  480. SetFlagValue( m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_NORMAL_DUDV, iValue );
  481. }
  482. else if ( !stricmp( pKeyName, "compute2dgradient" ) )
  483. {
  484. SetFlagValue( m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_COMPUTE_GRADIENT, iValue ? true : false );
  485. }
  486. else if ( !stricmp( pKeyName, "stripalphachannel" ) )
  487. {
  488. m_bStripAlphaChannel = iValue ? true : false;
  489. }
  490. else if ( !stricmp( pKeyName, "stripcolorchannel" ) )
  491. {
  492. m_bStripColorChannel = iValue ? true : false;
  493. }
  494. else if ( !stricmp( pKeyName, "normalalphatodudvluminance" ) )
  495. {
  496. m_bAlphaToLuminance = iValue ? true : false;
  497. }
  498. else if ( !stricmp( pKeyName, "dudv" ) )
  499. {
  500. m_bDuDv = iValue ? true : false;
  501. }
  502. else if( !stricmp( pKeyName, "reduce" ) )
  503. {
  504. m_nReduceX = atoi(pKeyValue);
  505. m_nReduceY = m_nReduceX;
  506. }
  507. else if( !stricmp( pKeyName, "reducex" ) )
  508. {
  509. m_nReduceX = atoi(pKeyValue);
  510. }
  511. else if( !stricmp( pKeyName, "reducey" ) )
  512. {
  513. m_nReduceY = atoi(pKeyValue);
  514. }
  515. else if( !stricmp( pKeyName, "maxwidth" ) )
  516. {
  517. m_nMaxDimensionX = atoi(pKeyValue);
  518. }
  519. else if( !stricmp( pKeyName, "maxwidth_360" ) )
  520. {
  521. m_nMaxDimensionX_360 = atoi(pKeyValue);
  522. }
  523. else if( !stricmp( pKeyName, "maxheight" ) )
  524. {
  525. m_nMaxDimensionY = atoi(pKeyValue);
  526. }
  527. else if( !stricmp( pKeyName, "maxheight_360" ) )
  528. {
  529. m_nMaxDimensionY_360 = atoi(pKeyValue);
  530. }
  531. else if( !stricmp( pKeyName, "alphatodistance" ) )
  532. {
  533. m_bAlphaToDistance = iValue ? true : false;
  534. }
  535. else if( !stricmp( pKeyName, "distancespread" ) )
  536. {
  537. m_flDistanceSpread = atof(pKeyValue);
  538. }
  539. else if( !stricmp( pKeyName, "pfmscale" ) )
  540. {
  541. m_pfmscale=atof(pKeyValue);
  542. VTexMsg( "pfmscale = %.2f\n", m_pfmscale );
  543. }
  544. else if ( !stricmp( pKeyName, "pfm" ) )
  545. {
  546. if ( iValue )
  547. g_eMode = BITMAP_FILE_TYPE_PFM;
  548. }
  549. else if ( !stricmp( pKeyName, "displacementwrinkle" ) )
  550. {
  551. m_bDisplacementWrinkleMap = true;
  552. }
  553. else if ( !stricmp( pKeyName, "specvar" ) )
  554. {
  555. int iDecayChannel = -1;
  556. if ( !stricmp( pKeyValue, "red" ) || !stricmp( pKeyValue, "r" ) )
  557. iDecayChannel = 0;
  558. if ( !stricmp( pKeyValue, "green" ) || !stricmp( pKeyValue, "g" ) )
  559. iDecayChannel = 1;
  560. if ( !stricmp( pKeyValue, "blue" ) || !stricmp( pKeyValue, "b" ) )
  561. iDecayChannel = 2;
  562. if ( !stricmp( pKeyValue, "alpha" ) || !stricmp( pKeyValue, "a" ) )
  563. iDecayChannel = 3;
  564. if ( iDecayChannel >= 0 && iDecayChannel < 4 )
  565. {
  566. m_vtfProcOptions.flags0 |= ( VtfProcessingOptions::OPT_DECAY_R | VtfProcessingOptions::OPT_DECAY_EXP_R ) << iDecayChannel;
  567. m_vtfProcOptions.numNotDecayMips[iDecayChannel] = 0;
  568. m_vtfProcOptions.clrDecayGoal[iDecayChannel] = 0;
  569. m_vtfProcOptions.fDecayExponentBase[iDecayChannel] = 0.75;
  570. m_bManualMip = false;
  571. SetFlagValue( m_nFlags, TEXTUREFLAGS_ALL_MIPS, 1 );
  572. }
  573. }
  574. else if ( stricmp( pKeyName, "manualmip" ) == 0 )
  575. {
  576. if ( ( m_nVolumeTextureDepth == 1 ) && !( m_nFlags & ( TEXTUREFLAGS_NORMAL | TEXTUREFLAGS_NOMIP ) ) )
  577. {
  578. m_bManualMip = true;
  579. }
  580. }
  581. else if ( !stricmp( pKeyName, "mipblend" ) )
  582. {
  583. SetFlagValue( m_nFlags, TEXTUREFLAGS_ALL_MIPS, 1 );
  584. // Possible values
  585. if ( !stricmp( pKeyValue, "detail" ) ) // Skip 2 mips and fade to gray -> (128, 128, 128, -)
  586. {
  587. for( int ch = 0; ch < 3; ++ ch )
  588. {
  589. m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_DECAY_R << ch;
  590. // m_vtfProcOptions.flags0 &= ~(VtfProcessingOptions::OPT_DECAY_EXP_R << ch);
  591. m_vtfProcOptions.numNotDecayMips[ch] = 2;
  592. m_vtfProcOptions.clrDecayGoal[ch] = 128;
  593. }
  594. }
  595. /*
  596. else if ( !stricmp( pKeyValue, "additive" ) ) // Skip 2 mips and fade to black -> (0, 0, 0, -)
  597. {
  598. for( int ch = 0; ch < 3; ++ ch )
  599. {
  600. m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_DECAY_R << ch;
  601. m_vtfProcOptions.flags0 &= ~(VtfProcessingOptions::OPT_DECAY_EXP_R << ch);
  602. m_vtfProcOptions.numDecayMips[ch] = 2;
  603. m_vtfProcOptions.clrDecayGoal[ch] = 0;
  604. }
  605. }
  606. else if ( !stricmp( pKeyValue, "alphablended" ) ) // Skip 2 mips and fade out alpha to 0
  607. {
  608. for( int ch = 3; ch < 4; ++ ch )
  609. {
  610. m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_DECAY_R << ch;
  611. m_vtfProcOptions.flags0 &= ~(VtfProcessingOptions::OPT_DECAY_EXP_R << ch);
  612. m_vtfProcOptions.numDecayMips[ch] = 2;
  613. m_vtfProcOptions.clrDecayGoal[ch] = 0;
  614. }
  615. }
  616. */
  617. else
  618. {
  619. // Parse the given value:
  620. // skip=3:r=255:g=255:b=255:a=255 - linear decay
  621. // r=0e.75 - exponential decay targeting 0 with exponent base 0.75
  622. int nSteps = 0; // default
  623. for ( char const *szParse = pKeyValue; szParse; szParse = strchr( szParse, ':' ), szParse ? ++ szParse : 0 )
  624. {
  625. if ( char const *sz = StringAfterPrefix( szParse, "skip=" ) )
  626. {
  627. szParse = sz;
  628. nSteps = atoi(sz);
  629. }
  630. else if ( StringHasPrefix( szParse, "r=" ) ||
  631. StringHasPrefix( szParse, "g=" ) ||
  632. StringHasPrefix( szParse, "b=" ) ||
  633. StringHasPrefix( szParse, "a=" ) )
  634. {
  635. int ch = 0;
  636. switch ( *szParse )
  637. {
  638. case 'g': case 'G': ch = 1; break;
  639. case 'b': case 'B': ch = 2; break;
  640. case 'a': case 'A': ch = 3; break;
  641. }
  642. szParse += 2;
  643. m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_DECAY_R << ch;
  644. m_vtfProcOptions.flags0 &= ~(VtfProcessingOptions::OPT_DECAY_EXP_R << ch);
  645. m_vtfProcOptions.numNotDecayMips[ch] = nSteps;
  646. m_vtfProcOptions.clrDecayGoal[ch] = atoi( szParse );
  647. while ( V_isdigit( *szParse ) )
  648. ++ szParse;
  649. // Exponential decay
  650. if ( ( *szParse == 'e' || *szParse == 'E' ) && ( szParse[1] == '.' ) )
  651. {
  652. m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_DECAY_EXP_R << ch;
  653. m_vtfProcOptions.fDecayExponentBase[ch] = ( float ) atof( szParse + 1 );
  654. }
  655. }
  656. else
  657. {
  658. VTexWarning( "invalid mipblend setting \"%s\"\n", pKeyValue );
  659. }
  660. }
  661. }
  662. }
  663. else if( !stricmp( pKeyName, "srgb" ) )
  664. {
  665. // Do nothing for now...this will be removed shortly
  666. }
  667. else
  668. {
  669. VTexError("unrecognized option in text file - %s\n", pKeyName );
  670. }
  671. }
  672. //-----------------------------------------------------------------------------
  673. // Returns the extension
  674. //-----------------------------------------------------------------------------
  675. static const char *GetSourceExtension( void )
  676. {
  677. switch ( g_eMode )
  678. {
  679. case BITMAP_FILE_TYPE_PSD:
  680. return ".psd";
  681. case BITMAP_FILE_TYPE_TGA:
  682. return ".tga";
  683. case BITMAP_FILE_TYPE_PFM:
  684. return ".pfm";
  685. default:
  686. return ".tga";
  687. }
  688. }
  689. //-----------------------------------------------------------------------------
  690. // Computes the desired texture format based on flags
  691. //-----------------------------------------------------------------------------
  692. static ImageFormat ComputeDesiredImageFormat( IVTFTexture *pTexture, VTexConfigInfo_t &info )
  693. {
  694. bool bDUDVTarget = info.m_bNormalToDuDv || info.m_bDuDv;
  695. bool bCopyAlphaToLuminance = info.m_bNormalToDuDv && info.m_bAlphaToLuminance;
  696. ImageFormat targetFormat;
  697. int nFlags = pTexture->Flags();
  698. if ( info.m_bStripAlphaChannel )
  699. {
  700. nFlags &= ~( TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA );
  701. }
  702. if ( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_NORMAL_GA )
  703. {
  704. return IMAGE_FORMAT_DXT5;
  705. }
  706. if ( pTexture->Format() == IMAGE_FORMAT_RGB323232F )
  707. {
  708. #ifndef DEBUG_NO_COMPRESSION
  709. if ( g_bUsedAsLaunchableDLL && !( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_NOCOMPRESS ) )
  710. {
  711. return IMAGE_FORMAT_BGRA8888;
  712. }
  713. else
  714. #endif // #ifndef DEBUG_NO_COMPRESSION
  715. {
  716. return IMAGE_FORMAT_RGBA16161616F;
  717. }
  718. }
  719. // Typically used for uncompressed/unquantized displacement maps
  720. if ( ( pTexture->Format() == IMAGE_FORMAT_R32F ) || ( pTexture->Format() == IMAGE_FORMAT_RGBA32323232F ) )
  721. {
  722. return pTexture->Format();
  723. }
  724. if ( bDUDVTarget )
  725. {
  726. if ( bCopyAlphaToLuminance && ( nFlags & ( TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA ) ) )
  727. {
  728. return IMAGE_FORMAT_UVLX8888;
  729. }
  730. return IMAGE_FORMAT_UV88;
  731. }
  732. if ( info.m_bStripColorChannel )
  733. {
  734. return IMAGE_FORMAT_A8;
  735. }
  736. // can't compress textures that are smaller than 4x4
  737. if( (nFlags & TEXTUREFLAGS_PROCEDURAL) ||
  738. (info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_NOCOMPRESS) ||
  739. (pTexture->Width() < 4) || (pTexture->Height() < 4) )
  740. {
  741. if ( nFlags & ( TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA ) )
  742. {
  743. targetFormat = IMAGE_FORMAT_BGRA8888;
  744. }
  745. else
  746. {
  747. targetFormat = IMAGE_FORMAT_BGR888;
  748. }
  749. }
  750. else if( nFlags & TEXTUREFLAGS_HINT_DXT5 )
  751. {
  752. #ifdef DEBUG_NO_COMPRESSION
  753. targetFormat = IMAGE_FORMAT_BGRA8888;
  754. #else
  755. targetFormat = IsPosix() ? IMAGE_FORMAT_BGRA8888 : IMAGE_FORMAT_DXT5; // No DXT compressor on Posix
  756. #endif
  757. }
  758. else if( nFlags & TEXTUREFLAGS_EIGHTBITALPHA )
  759. {
  760. // compressed with alpha blending
  761. #ifdef DEBUG_NO_COMPRESSION
  762. targetFormat = IMAGE_FORMAT_BGRA8888;
  763. #else
  764. targetFormat = IsPosix() ? IMAGE_FORMAT_BGRA8888 : IMAGE_FORMAT_DXT5; // No DXT compressor on Posix
  765. #endif
  766. }
  767. else if ( nFlags & TEXTUREFLAGS_ONEBITALPHA )
  768. {
  769. // garymcthack - fixme IMAGE_FORMAT_DXT1_ONEBITALPHA doesn't work yet.
  770. #ifdef DEBUG_NO_COMPRESSION
  771. targetFormat = IMAGE_FORMAT_BGRA8888;
  772. #else
  773. // targetFormat = IMAGE_FORMAT_DXT1_ONEBITALPHA;
  774. targetFormat = IsPosix() ? IMAGE_FORMAT_BGRA8888 : IMAGE_FORMAT_DXT5; // No DXT compressor on Posix
  775. #endif
  776. }
  777. else
  778. {
  779. #ifdef DEBUG_NO_COMPRESSION
  780. targetFormat = IMAGE_FORMAT_BGR888;
  781. #else
  782. targetFormat = IsPosix() ? IMAGE_FORMAT_BGR888 : IMAGE_FORMAT_DXT1; // No DXT compressor on Posix
  783. #endif
  784. }
  785. return targetFormat;
  786. }
  787. //-----------------------------------------------------------------------------
  788. // Computes the desired texture format based on flags
  789. //-----------------------------------------------------------------------------
  790. static ImageFormat ComputeDesiredImageFormat( CDmePrecompiledTexture *pPrecompiledTexture, CDmeTexture *pTexture )
  791. {
  792. // FIXME: Implement!
  793. // bool bDUDVTarget = info.m_bNormalToDuDv || info.m_bDuDv;
  794. // bool bCopyAlphaToLuminance = info.m_bNormalToDuDv && info.m_bAlphaToLuminance;
  795. ImageFormat targetFormat;
  796. bool bHasAlphaChannel = ImageLoader::IsTransparent( pTexture->Format() );
  797. // int nFlags = pTexture->Flags();
  798. // if ( info.m_bStripAlphaChannel )
  799. // {
  800. // nFlags &= ~( TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA );
  801. // }
  802. // if ( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_NORMAL_GA )
  803. // {
  804. // return IMAGE_FORMAT_DXT5;
  805. // }
  806. if ( pTexture->Format() == IMAGE_FORMAT_RGB323232F )
  807. {
  808. #ifndef DEBUG_NO_COMPRESSION
  809. if ( g_bUsedAsLaunchableDLL && !pPrecompiledTexture->m_bNoCompression )
  810. return IMAGE_FORMAT_BGRA8888;
  811. #endif // #ifndef DEBUG_NO_COMPRESSION
  812. return IMAGE_FORMAT_RGBA16161616F;
  813. }
  814. // Typically used for uncompressed/unquantized displacement maps
  815. if ( ( pTexture->Format() == IMAGE_FORMAT_R32F ) || ( pTexture->Format() == IMAGE_FORMAT_RGBA32323232F ) )
  816. {
  817. return pTexture->Format();
  818. }
  819. // if ( bDUDVTarget )
  820. // {
  821. // if ( bCopyAlphaToLuminance && ( nFlags & ( TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA ) ) )
  822. // {
  823. // return IMAGE_FORMAT_UVLX8888;
  824. // }
  825. // return IMAGE_FORMAT_UV88;
  826. // }
  827. // if ( info.m_bStripColorChannel )
  828. // {
  829. // return IMAGE_FORMAT_A8;
  830. // }
  831. // Leave UV formats uncompressed
  832. if ( pTexture->Format() == IMAGE_FORMAT_UV88 || pTexture->Format() == IMAGE_FORMAT_UVLX8888 || pTexture->Format() == IMAGE_FORMAT_UVWQ8888 )
  833. return pTexture->Format();
  834. // can't compress textures that are smaller than 4x4
  835. if( pPrecompiledTexture->m_bNoCompression || ( pTexture->Width() < 4 ) || ( pTexture->Height() < 4 ) )
  836. {
  837. if ( bHasAlphaChannel )
  838. {
  839. targetFormat = IMAGE_FORMAT_BGRA8888;
  840. }
  841. else
  842. {
  843. targetFormat = IMAGE_FORMAT_BGRX8888;
  844. }
  845. }
  846. else if( pPrecompiledTexture->m_bHintDxt5Compression )
  847. {
  848. #ifdef DEBUG_NO_COMPRESSION
  849. targetFormat = IMAGE_FORMAT_BGRA8888;
  850. #else
  851. targetFormat = IMAGE_FORMAT_DXT5;
  852. #endif
  853. }
  854. else if( bHasAlphaChannel )
  855. {
  856. // compressed with alpha blending
  857. #ifdef DEBUG_NO_COMPRESSION
  858. targetFormat = IMAGE_FORMAT_BGRA8888;
  859. #else
  860. targetFormat = IsPosix() ? IMAGE_FORMAT_BGRA8888 : IMAGE_FORMAT_DXT5; // No DXT compressor on Posix
  861. #endif
  862. }
  863. else
  864. {
  865. #ifdef DEBUG_NO_COMPRESSION
  866. targetFormat = IMAGE_FORMAT_BGRX8888;
  867. #else
  868. targetFormat = IsPosix() ? IMAGE_FORMAT_BGR888 : IMAGE_FORMAT_DXT1; // No DXT compressor on Posix
  869. #endif
  870. }
  871. return targetFormat;
  872. }
  873. //-----------------------------------------------------------------------------
  874. // Computes the low res image size
  875. //-----------------------------------------------------------------------------
  876. void VTFGetLowResImageInfo( int cacheWidth, int cacheHeight, int *lowResImageWidth, int *lowResImageHeight,
  877. ImageFormat *imageFormat )
  878. {
  879. if (cacheWidth > cacheHeight)
  880. {
  881. int factor = cacheWidth / LOWRESIMAGE_DIM;
  882. if (factor > 0)
  883. {
  884. *lowResImageWidth = LOWRESIMAGE_DIM;
  885. *lowResImageHeight = cacheHeight / factor;
  886. }
  887. else
  888. {
  889. *lowResImageWidth = cacheWidth;
  890. *lowResImageHeight = cacheHeight;
  891. }
  892. }
  893. else
  894. {
  895. int factor = cacheHeight / LOWRESIMAGE_DIM;
  896. if (factor > 0)
  897. {
  898. *lowResImageHeight = LOWRESIMAGE_DIM;
  899. *lowResImageWidth = cacheWidth / factor;
  900. }
  901. else
  902. {
  903. *lowResImageWidth = cacheWidth;
  904. *lowResImageHeight = cacheHeight;
  905. }
  906. }
  907. // Can end up with a dimension of zero for high aspect ration images.
  908. if( *lowResImageWidth < 1 )
  909. {
  910. *lowResImageWidth = 1;
  911. }
  912. if( *lowResImageHeight < 1 )
  913. {
  914. *lowResImageHeight = 1;
  915. }
  916. *imageFormat = LOWRES_IMAGE_FORMAT;
  917. }
  918. //-----------------------------------------------------------------------------
  919. // This method creates the low-res image and hooks it into the VTF Texture
  920. //-----------------------------------------------------------------------------
  921. static void CreateLowResImage( IVTFTexture *pVTFTexture )
  922. {
  923. int iWidth, iHeight;
  924. ImageFormat imageFormat;
  925. VTFGetLowResImageInfo( pVTFTexture->Width(), pVTFTexture->Height(), &iWidth, &iHeight, &imageFormat );
  926. // Allocate the low-res image data
  927. pVTFTexture->InitLowResImage( iWidth, iHeight, imageFormat );
  928. // Generate the low-res image bits
  929. if (!pVTFTexture->ConstructLowResImage())
  930. {
  931. VTexError( "Can't convert image from %s to %s in CalcLowResImage\n",
  932. ImageLoader::GetName( pVTFTexture->Format() ), ImageLoader::GetName(imageFormat) );
  933. }
  934. }
  935. //-----------------------------------------------------------------------------
  936. // Computes the source file name
  937. //-----------------------------------------------------------------------------
  938. void MakeSrcFileName( VTexConfigInfo_t &info, const char *pFullNameWithoutExtension, int frameID, int faceID, int mipLevel, int z )
  939. {
  940. bool bAnimated = !( info.m_nStartFrame == -1 || info.m_nEndFrame == -1 );
  941. char normalTempBuf[512];
  942. if( info.m_bNormalToDuDv )
  943. {
  944. char *pNormalString = Q_stristr( ( char * )pFullNameWithoutExtension, "_dudv" );
  945. if( pNormalString )
  946. {
  947. Q_strncpy( normalTempBuf, pFullNameWithoutExtension, sizeof( normalTempBuf ) );
  948. char *pNormalString = Q_stristr( normalTempBuf, "_dudv" );
  949. Q_strcpy( pNormalString, "_normal" );
  950. pFullNameWithoutExtension = normalTempBuf;
  951. }
  952. else
  953. {
  954. Assert( Q_stristr( ( char * )pFullNameWithoutExtension, "_dudv" ) );
  955. }
  956. }
  957. char mipTempBuf[512];
  958. if ( mipLevel > 0 )
  959. {
  960. Q_strncpy( mipTempBuf, pFullNameWithoutExtension, sizeof( mipTempBuf ) );
  961. char right[16];
  962. V_StrRight( mipTempBuf, 5, right, sizeof( right ) );
  963. if ( !V_strstr( right, "_mip0" ) )
  964. {
  965. VTexError( "Invalid texture name (%s%s) for 'manualmip' - the top mip file should end in '_mip0'\n", pFullNameWithoutExtension, GetSourceExtension() );
  966. }
  967. mipTempBuf[ strlen( mipTempBuf ) - 1 ] = 0;
  968. sprintf( right, "%d", mipLevel );
  969. V_strncat( mipTempBuf, right, sizeof( mipTempBuf ) );
  970. pFullNameWithoutExtension = mipTempBuf;
  971. }
  972. if( bAnimated )
  973. {
  974. if ( info.m_bIsCubeMap && g_bOldCubemapPath )
  975. {
  976. Assert( z == -1 );
  977. Q_snprintf( info.m_SrcName, ARRAYSIZE(info.m_SrcName), "%s%s%03d%s", pFullNameWithoutExtension, g_CubemapFacingNames[faceID], frameID + info.m_nStartFrame, GetSourceExtension() );
  978. }
  979. else
  980. {
  981. if ( z == -1 )
  982. {
  983. Q_snprintf( info.m_SrcName, ARRAYSIZE(info.m_SrcName), "%s%03d%s", pFullNameWithoutExtension, frameID + info.m_nStartFrame, GetSourceExtension() );
  984. }
  985. else
  986. {
  987. if ( info.m_pVolumeTextureFileNames.Count() == info.m_nVolumeTextureDepth )
  988. {
  989. Q_snprintf( info.m_SrcName, ARRAYSIZE(info.m_SrcName), "%s%03d%s", pFullNameWithoutExtension, frameID + info.m_nStartFrame, GetSourceExtension() );
  990. }
  991. else
  992. {
  993. Q_snprintf( info.m_SrcName, ARRAYSIZE(info.m_SrcName), "%s%03d_z%03d%s", pFullNameWithoutExtension, z, frameID + info.m_nStartFrame, GetSourceExtension() );
  994. }
  995. }
  996. }
  997. }
  998. else
  999. {
  1000. if ( info.m_bIsCubeMap && g_bOldCubemapPath )
  1001. {
  1002. Assert( z == -1 );
  1003. Q_snprintf( info.m_SrcName, ARRAYSIZE(info.m_SrcName), "%s%s%s", pFullNameWithoutExtension, g_CubemapFacingNames[faceID], GetSourceExtension() );
  1004. }
  1005. else
  1006. {
  1007. if ( z == -1 )
  1008. {
  1009. Q_snprintf( info.m_SrcName, ARRAYSIZE(info.m_SrcName), "%s%s", pFullNameWithoutExtension, GetSourceExtension() );
  1010. }
  1011. else
  1012. {
  1013. if ( info.m_pVolumeTextureFileNames.Count() == info.m_nVolumeTextureDepth )
  1014. {
  1015. Q_snprintf( info.m_SrcName, ARRAYSIZE(info.m_SrcName), info.m_pVolumeTextureFileNames[z] );
  1016. }
  1017. else
  1018. {
  1019. Q_snprintf( info.m_SrcName, ARRAYSIZE(info.m_SrcName), "%s_z%03d%s", pFullNameWithoutExtension, z, GetSourceExtension() );
  1020. }
  1021. }
  1022. }
  1023. }
  1024. }
  1025. //-----------------------------------------------------------------------------
  1026. // Computes the source file name
  1027. //-----------------------------------------------------------------------------
  1028. void MakeSrcFileName( CDmePrecompiledTexture *pTexture, const char *pSrcName,
  1029. int nFrameID, int nFaceID, int nMipLevel, int z, char *pFullPath, size_t nBufLen )
  1030. {
  1031. const char *pExt = Q_GetFileExtension( pSrcName );
  1032. char pFullNameWithoutExtension[MAX_PATH];
  1033. Q_StripExtension( pSrcName, pFullNameWithoutExtension, sizeof(pFullNameWithoutExtension) );
  1034. bool bAnimated = !( pTexture->m_nStartFrame == -1 || pTexture->m_nEndFrame == -1 );
  1035. /*
  1036. char normalTempBuf[512];
  1037. if( info.m_bNormalToDuDv )
  1038. {
  1039. char *pNormalString = Q_stristr( ( char * )pFullNameWithoutExtension, "_dudv" );
  1040. if( pNormalString )
  1041. {
  1042. Q_strncpy( normalTempBuf, pFullNameWithoutExtension, sizeof( normalTempBuf ) );
  1043. char *pNormalString = Q_stristr( normalTempBuf, "_dudv" );
  1044. Q_strcpy( pNormalString, "_normal" );
  1045. pFullNameWithoutExtension = normalTempBuf;
  1046. }
  1047. else
  1048. {
  1049. Assert( Q_stristr( ( char * )pFullNameWithoutExtension, "_dudv" ) );
  1050. }
  1051. }
  1052. */
  1053. if ( nMipLevel > 0 )
  1054. {
  1055. // Replace the mip digit with the actual mip level
  1056. char pRight[16];
  1057. pFullNameWithoutExtension[ Q_strlen( pFullNameWithoutExtension ) - 1 ] = 0;
  1058. Q_snprintf( pRight, sizeof(pRight), "%d", nMipLevel );
  1059. Q_strncat( pFullNameWithoutExtension, pRight, sizeof( pFullNameWithoutExtension ) );
  1060. }
  1061. if( bAnimated )
  1062. {
  1063. if ( z == -1 )
  1064. {
  1065. Q_snprintf( pFullPath, nBufLen, "%s%03d.%s", pFullNameWithoutExtension, nFrameID + pTexture->m_nStartFrame, pExt );
  1066. }
  1067. else
  1068. {
  1069. Q_snprintf( pFullPath, nBufLen, "%s%03d_z%03d.%s", pFullNameWithoutExtension, z, nFrameID + pTexture->m_nStartFrame, pExt );
  1070. }
  1071. }
  1072. else
  1073. {
  1074. if ( z == -1 )
  1075. {
  1076. Q_snprintf( pFullPath, nBufLen, "%s.%s", pFullNameWithoutExtension, pExt );
  1077. }
  1078. else
  1079. {
  1080. Q_snprintf( pFullPath, nBufLen, "%s_z%03d.%s", pFullNameWithoutExtension, z, pExt );
  1081. }
  1082. }
  1083. }
  1084. //-----------------------------------------------------------------------------
  1085. // Computes the output file name
  1086. //-----------------------------------------------------------------------------
  1087. void MakeOutputFileName( char *pDstFileName, int maxLen, const char *pOutputDir, const char *pBaseName, const char *pSuffix,
  1088. const VTexConfigInfo_t &info )
  1089. {
  1090. Q_snprintf( pDstFileName, maxLen, "%s/%s%s%s.vtf",
  1091. pOutputDir, pBaseName, pSuffix,
  1092. ( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_SRGB_PC_TO_360 ) ? ".pwl" : "" );
  1093. }
  1094. //-----------------------------------------------------------------------------
  1095. // Loads a file into a UTLBuffer,
  1096. // also computes the hash of the buffer.
  1097. //-----------------------------------------------------------------------------
  1098. static bool LoadFile( const char *pFileName, CUtlBuffer &buf, bool bFailOnError, CRC32_t *puiHash )
  1099. {
  1100. FILE *fp = fopen( pFileName, "rb" );
  1101. if (!fp)
  1102. {
  1103. if ( bFailOnError )
  1104. VTexError( "Can't open: \"%s\"\n", pFileName );
  1105. return false;
  1106. }
  1107. fseek( fp, 0, SEEK_END );
  1108. int nFileLength = ftell( fp );
  1109. fseek( fp, 0, SEEK_SET );
  1110. buf.EnsureCapacity( nFileLength );
  1111. int nBytesRead = fread( buf.Base(), 1, nFileLength, fp );
  1112. fclose( fp );
  1113. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
  1114. { CP4AutoAddFile autop4( pFileName ); /* add loaded file to P4 */ }
  1115. // Auto-compute buffer hash if necessary
  1116. if ( puiHash )
  1117. CRC32_ProcessBuffer( puiHash, buf.Base(), nBytesRead );
  1118. return true;
  1119. }
  1120. //-----------------------------------------------------------------------------
  1121. // Extract basic info from an image file
  1122. //-----------------------------------------------------------------------------
  1123. bool ImageGetInfo( BitmapFileType_t nMode, CUtlBuffer &fileBuffer, int &nWidth, int &nHeight, ImageFormat &imageFormat )
  1124. {
  1125. float flSrcGamma;
  1126. switch ( nMode )
  1127. {
  1128. case BITMAP_FILE_TYPE_PSD:
  1129. return PSDGetInfo( fileBuffer, &nWidth, &nHeight, &imageFormat, &flSrcGamma );
  1130. case BITMAP_FILE_TYPE_TGA:
  1131. return TGALoader::GetInfo( fileBuffer, &nWidth, &nHeight, &imageFormat, &flSrcGamma );
  1132. case BITMAP_FILE_TYPE_PFM:
  1133. return PFMGetInfo_AndAdvanceToTextureBits( fileBuffer, nWidth, nHeight, imageFormat );
  1134. default:
  1135. return false;
  1136. }
  1137. }
  1138. //-----------------------------------------------------------------------------
  1139. // For cubemaps, the source image contains all 6 faces, embedded in a 4x3 grid
  1140. //-----------------------------------------------------------------------------
  1141. void AdjustResForCubemap( const VTexConfigInfo_t &info, int &nWidth, int &nHeight )
  1142. {
  1143. if ( info.m_bIsCubeMap && !g_bOldCubemapPath )
  1144. {
  1145. if ( ( nWidth % 4 ) || ( nHeight % 3 ) )
  1146. {
  1147. Error( "TGA is wrong size for cubemap - [%d,%d] after 'reduce' - should be 4x3 grid of squares\n", nWidth, nHeight );
  1148. }
  1149. nWidth /= 4;
  1150. nHeight /= 3;
  1151. }
  1152. }
  1153. //-----------------------------------------------------------------------------
  1154. // Creates a texture the size of the image stored in the buffer
  1155. //-----------------------------------------------------------------------------
  1156. static void InitializeSrcTexture( IVTFTexture *pTexture, const char *pInputFileName,
  1157. CUtlBuffer &fileBuffer, int nDepth, int nFrameCount,
  1158. const VTexConfigInfo_t &info )
  1159. {
  1160. int nWidth, nHeight;
  1161. ImageFormat sourceFormat;
  1162. if ( !ImageGetInfo( g_eMode, fileBuffer, nWidth, nHeight, sourceFormat ) )
  1163. {
  1164. Error( "Cannot read texture %s\n", pInputFileName );
  1165. }
  1166. nWidth /= info.m_nReduceX;
  1167. nHeight /= info.m_nReduceY;
  1168. AdjustResForCubemap( info, nWidth, nHeight );
  1169. // Wrinkle displacement maps hold three channels of data
  1170. ImageFormat dMapFormat = info.m_bDisplacementWrinkleMap ? IMAGE_FORMAT_RGBA32323232F : IMAGE_FORMAT_R32F;
  1171. ImageFormat textureFormat = ( g_eMode == BITMAP_FILE_TYPE_PFM ) ? info.m_bDisplacementMap ? dMapFormat : IMAGE_FORMAT_RGB323232F : IMAGE_FORMAT_RGBA8888;
  1172. if ( !pTexture->Init( nWidth, nHeight, nDepth, textureFormat, info.m_nFlags, nFrameCount ) )
  1173. {
  1174. Error( "Cannot initialize texture %s\n", pInputFileName );
  1175. }
  1176. }
  1177. #define DISTANCE_CODE_ALPHA_INOUT_THRESHOLD 10
  1178. //-----------------------------------------------------------------------------
  1179. // Converts an 8888 image's alpha channel to encode distance-to-silhouette
  1180. //-----------------------------------------------------------------------------
  1181. void ConvertAlphaToDistance( IVTFTexture *pTexture, const VTexConfigInfo_t &info, const Bitmap_t &source, unsigned char *pDest )
  1182. {
  1183. if ( !info.m_bAlphaToDistance )
  1184. return;
  1185. ImageFormatInfo_t fmtInfo = ImageLoader::ImageFormatInfo( pTexture->Format() );
  1186. if ( fmtInfo.m_nNumAlphaBits == 0 )
  1187. {
  1188. VTexWarning( "%s: alpha to distance asked for but no alpha channel.\n", info.m_SrcName );
  1189. }
  1190. else
  1191. {
  1192. float flMaxRad = info.m_flDistanceSpread*2.0*MAX( info.m_nReduceX, info.m_nReduceY );
  1193. int nSearchRad = ceil(flMaxRad);
  1194. bool bWarnEdges = false;
  1195. for ( int x = 0; x < pTexture->Width(); x++ )
  1196. {
  1197. for ( int y = 0; y < pTexture->Height(); y++ )
  1198. {
  1199. // map to original image coords
  1200. int nOrig_x = FLerp( 0, (source.Width() -1), 0, (pTexture->Width() -1), x);
  1201. int nOrig_y = FLerp( 0, (source.Height()-1), 0, (pTexture->Height()-1), y);
  1202. uint8 nOrigAlpha = source.GetBits()[ 3 + 4*( nOrig_x + source.Width()*nOrig_y ) ];
  1203. bool bInOrOut = nOrigAlpha > DISTANCE_CODE_ALPHA_INOUT_THRESHOLD;
  1204. float flClosest_Dist = 1.0e23f;
  1205. for ( int iy = -nSearchRad; iy <= nSearchRad; iy++ )
  1206. {
  1207. for ( int ix = -nSearchRad; ix <= nSearchRad; ix++ )
  1208. {
  1209. int cx = MAX( 0, MIN( (source.Width()-1), (ix + nOrig_x) ) );
  1210. int cy = MAX( 0, MIN( (source.Height()-1), (iy + nOrig_y) ) );
  1211. int nOffset = 3+ 4 * ( cx + cy * source.Width() );
  1212. uint8 alphaValue = source.GetBits()[nOffset];
  1213. bool bIn =( alphaValue > DISTANCE_CODE_ALPHA_INOUT_THRESHOLD );
  1214. if ( bInOrOut != bIn ) // transition?
  1215. {
  1216. float flTryDist = sqrt( (float) ( ix*ix + iy*iy ) );
  1217. flClosest_Dist = MIN( flClosest_Dist, flTryDist );
  1218. }
  1219. }
  1220. }
  1221. // now, map signed distance to alpha value
  1222. float flOutDist = MIN( 0.5f, FLerp( 0.0f, 0.5f, 0.0f, flMaxRad, flClosest_Dist ) );
  1223. if ( ! bInOrOut )
  1224. {
  1225. // negative distance
  1226. flOutDist = -flOutDist;
  1227. }
  1228. uint8 &nOutAlpha = pDest[ 3 + 4*( x + pTexture->Width()*y ) ];
  1229. nOutAlpha = MIN( 255.0f, 255.0f*( 0.5f + flOutDist ) );
  1230. if ( ( nOutAlpha != 0 ) &&
  1231. ( ( x == 0 ) ||
  1232. ( y == 0 ) ||
  1233. ( x == pTexture->Width() -1 ) ||
  1234. ( y == pTexture->Height()-1 ) ) )
  1235. {
  1236. bWarnEdges = true;
  1237. nOutAlpha = 0; // force it.
  1238. }
  1239. }
  1240. }
  1241. if ( bWarnEdges )
  1242. {
  1243. VTexWarning( "%s: There are non-zero distance pixels along the image edge. You may need"
  1244. " to reduce your distance spread or reduce the image less"
  1245. " or add a border to the image.\n",
  1246. info.m_SrcName );
  1247. }
  1248. }
  1249. }
  1250. //-----------------------------------------------------------------------------
  1251. // Converts a bitmap into a subrect thereof (for cubemaps)
  1252. //-----------------------------------------------------------------------------
  1253. void ExtractFaceSubrect( Bitmap_t &srcBitmap, Bitmap_t *pDstBitmap, int nFace )
  1254. {
  1255. if ( !( ( nFace >= 0 ) && ( nFace < 6 ) ) )
  1256. {
  1257. Assert( 0 );
  1258. return;
  1259. }
  1260. if ( &srcBitmap == pDstBitmap )
  1261. {
  1262. // NOTE: This is no longer valid! Use a new destination bitmap that varies from the source.
  1263. Assert( 0 );
  1264. return;
  1265. }
  1266. int nFaceWidth = srcBitmap.Width() / 4;
  1267. int nFaceHeight = srcBitmap.Height() / 3;
  1268. Rect_t faceRects[ 6 ] = { { 3*nFaceWidth, 1*nFaceHeight, nFaceWidth, nFaceHeight }, // CUBEMAP_FACE_RIGHT
  1269. { 1*nFaceWidth, 1*nFaceHeight, nFaceWidth, nFaceHeight }, // CUBEMAP_FACE_LEFT
  1270. { 2*nFaceWidth, 1*nFaceHeight, nFaceWidth, nFaceHeight }, // CUBEMAP_FACE_BACK
  1271. { 0*nFaceWidth, 1*nFaceHeight, nFaceWidth, nFaceHeight }, // CUBEMAP_FACE_FRONT
  1272. { 3*nFaceWidth, 0*nFaceHeight, nFaceWidth, nFaceHeight }, // CUBEMAP_FACE_UP
  1273. { 3*nFaceWidth, 2*nFaceHeight, nFaceWidth, nFaceHeight } }; // CUBEMAP_FACE_DOWN
  1274. Rect_t &srcRect = faceRects[ nFace ];
  1275. if ( &srcBitmap != pDstBitmap )
  1276. {
  1277. pDstBitmap->Init( nFaceWidth, nFaceHeight, srcBitmap.Format() );
  1278. }
  1279. // NOTE: Copying lines from top to bottom avoids ordering issues, due to our cubemap layout:
  1280. int nPixelSize = ImageLoader::SizeInBytes( srcBitmap.Format() );
  1281. for ( int y = 0; y < nFaceHeight; y++ )
  1282. {
  1283. unsigned char *pSrc = srcBitmap.GetPixel( srcRect.x, srcRect.y + y );
  1284. unsigned char *pDst = pDstBitmap->GetBits()+ y*nPixelSize * nFaceWidth;
  1285. memcpy( pDst, pSrc, nPixelSize*nFaceWidth );
  1286. }
  1287. }
  1288. //-----------------------------------------------------------------------------
  1289. // Loads a TGA file into a Bitmap_t as RGBA8888 data
  1290. //-----------------------------------------------------------------------------
  1291. bool TGAReadFileRGBA8888( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, float flGamma )
  1292. {
  1293. // Get the information from the file...
  1294. int nWidth, nHeight;
  1295. ImageFormat sourceFormat;
  1296. float flSrcGamma;
  1297. if ( !TGALoader::GetInfo( fileBuffer, &nWidth, &nHeight, &sourceFormat, &flSrcGamma ) )
  1298. return false;
  1299. // Init the bitmap
  1300. bitmap.Init( nWidth, nHeight, IMAGE_FORMAT_RGBA8888 );
  1301. // Read the texels
  1302. bool bNoMipMaps = false;
  1303. fileBuffer.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  1304. return TGALoader::Load( bitmap.GetBits(), fileBuffer, nWidth, nHeight, IMAGE_FORMAT_RGBA8888, flGamma, bNoMipMaps );
  1305. }
  1306. bool TGAReadFile( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, ImageFormat fmt, float flGamma )
  1307. {
  1308. // Get the information from the file...
  1309. int nWidth, nHeight;
  1310. ImageFormat sourceFormat;
  1311. float flSrcGamma;
  1312. if ( !TGALoader::GetInfo( fileBuffer, &nWidth, &nHeight, &sourceFormat, &flSrcGamma ) )
  1313. return false;
  1314. // Init the bitmap
  1315. bitmap.Init( nWidth, nHeight, fmt );
  1316. // Read the texels
  1317. fileBuffer.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  1318. return TGALoader::Load( bitmap.GetBits(), fileBuffer, nWidth, nHeight, fmt, flGamma, false );
  1319. }
  1320. //-----------------------------------------------------------------------------
  1321. //-----------------------------------------------------------------------------
  1322. // Loads a face from an image file
  1323. // - load a subrect if this is a cubemap
  1324. // - resamples if 'reduce' is requested
  1325. // - performs 'alphatodist' conversion if requested
  1326. //-----------------------------------------------------------------------------
  1327. static bool LoadFaceFromFile( IVTFTexture *pTexture, CUtlBuffer &fileBuffer, int z, int nFrame, int nFace, int nMipLevel,
  1328. float flGamma, const VTexConfigInfo_t &info )
  1329. {
  1330. // Load the image data as one of 2 fixed formats (so we can do resampling, etc)
  1331. Bitmap_t srcBitmap;
  1332. // Wrinkle displacement maps hold three channels of data
  1333. ImageFormat dMapFormat = info.m_bDisplacementWrinkleMap ? IMAGE_FORMAT_RGBA32323232F : IMAGE_FORMAT_R32F;
  1334. ImageFormat bitmapFormat = ( g_eMode == BITMAP_FILE_TYPE_PFM ) ? info.m_bDisplacementMap ? dMapFormat : IMAGE_FORMAT_RGB323232F : IMAGE_FORMAT_RGBA8888;
  1335. // Load the bits
  1336. fileBuffer.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  1337. bool bOK = false;
  1338. switch ( g_eMode )
  1339. {
  1340. case BITMAP_FILE_TYPE_PSD:
  1341. bOK = PSDReadFileRGBA8888( fileBuffer, srcBitmap );
  1342. break;
  1343. case BITMAP_FILE_TYPE_TGA:
  1344. bOK = TGAReadFileRGBA8888( fileBuffer, srcBitmap, flGamma );
  1345. break;
  1346. case BITMAP_FILE_TYPE_PFM:
  1347. if ( info.m_bDisplacementMap )
  1348. {
  1349. // Displacement wrinkle maps have three channels
  1350. if ( info.m_bDisplacementWrinkleMap )
  1351. {
  1352. bOK = PFMReadFileRGBA32323232F( fileBuffer, srcBitmap, info.m_pfmscale );
  1353. }
  1354. else
  1355. {
  1356. bOK = PFMReadFileR32F( fileBuffer, srcBitmap, info.m_pfmscale );
  1357. }
  1358. }
  1359. else
  1360. {
  1361. bOK = PFMReadFileRGB323232F( fileBuffer, srcBitmap, info.m_pfmscale );
  1362. }
  1363. break;
  1364. }
  1365. if ( !bOK )
  1366. return false;
  1367. Bitmap_t faceBitmap;
  1368. // If this is a cubemap, reduce the bitmap to the subrect for the face we're interested in
  1369. if ( info.m_bIsCubeMap && !g_bOldCubemapPath )
  1370. {
  1371. ExtractFaceSubrect( srcBitmap, &faceBitmap, nFace );
  1372. }
  1373. else
  1374. {
  1375. // Reference the source bitmap below
  1376. faceBitmap.MakeLogicalCopyOf( srcBitmap );
  1377. }
  1378. // Check that the image is the right size for this mip level
  1379. int nMipLevelWidth, nMipLevelHeight, nMipLevelDepth;
  1380. pTexture->ComputeMipLevelDimensions( nMipLevel, &nMipLevelWidth, &nMipLevelHeight, &nMipLevelDepth );
  1381. if ( ( faceBitmap.Width() != info.m_nReduceX*nMipLevelWidth ) || ( faceBitmap.Height() != info.m_nReduceY*nMipLevelHeight ) )
  1382. {
  1383. VTexError( "'manualmip' source image wrong size for mip %d! (%s)\n", nMipLevel, info.m_SrcName );
  1384. }
  1385. unsigned char *pDestBits = pTexture->ImageData( nFrame, nFace, nMipLevel, 0, 0, z );
  1386. if ( ( info.m_bAlphaToDistance ) || ( info.m_nReduceX != 1 ) || ( info.m_nReduceY != 1 ) )
  1387. {
  1388. int texelSize = ImageLoader::SizeInBytes( faceBitmap.Format() );
  1389. CUtlMemory<uint8> tmpDest( 0, pTexture->Width() * pTexture->Height() * texelSize );
  1390. ImageLoader::ResampleInfo_t resInfo;
  1391. resInfo.m_pSrc = faceBitmap.GetBits();
  1392. resInfo.m_pDest = tmpDest.Base();
  1393. resInfo.m_nSrcWidth = faceBitmap.Width();
  1394. resInfo.m_nSrcHeight = faceBitmap.Height();
  1395. resInfo.m_nDestWidth = pTexture->Width();
  1396. resInfo.m_nDestHeight = pTexture->Height();
  1397. resInfo.m_flSrcGamma = flGamma;
  1398. resInfo.m_flDestGamma = flGamma;
  1399. if (info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_FILTER_NICE )
  1400. {
  1401. resInfo.m_nFlags |= ImageLoader::RESAMPLE_NICE_FILTER;
  1402. }
  1403. // Resample
  1404. Assert( ( bitmapFormat == IMAGE_FORMAT_RGBA8888 ) || ( bitmapFormat == IMAGE_FORMAT_RGB323232F ) );
  1405. if ( bitmapFormat == IMAGE_FORMAT_RGBA8888 )
  1406. {
  1407. ResampleRGBA8888( resInfo );
  1408. }
  1409. else if ( bitmapFormat == IMAGE_FORMAT_RGB323232F )
  1410. {
  1411. ResampleRGB323232F( resInfo );
  1412. }
  1413. // Convert alpha to distance
  1414. ConvertAlphaToDistance( pTexture, info, faceBitmap, tmpDest.Base() );
  1415. // now, store in dest
  1416. ImageLoader::ConvertImageFormat( tmpDest.Base(), bitmapFormat, pDestBits,
  1417. pTexture->Format(), pTexture->Width(), pTexture->Height() );
  1418. }
  1419. else
  1420. {
  1421. // Just convert the format
  1422. ImageLoader::ConvertImageFormat( faceBitmap.GetBits(), bitmapFormat, pDestBits,
  1423. pTexture->Format(), faceBitmap.Width(), faceBitmap.Height() );
  1424. }
  1425. return true;
  1426. }
  1427. static bool LoadFace( IVTFTexture *pTexture, CUtlBuffer &tgaBuffer, int z, int nFrame, int nFace, int nMipLevel,
  1428. float flGamma, const VTexConfigInfo_t &info )
  1429. {
  1430. if ( !LoadFaceFromFile( pTexture, tgaBuffer, z, nFrame, nFace, nMipLevel, flGamma, info ) )
  1431. return false;
  1432. // Restricting number of channels by painting white into the rest
  1433. if ( info.m_numChannelsMax < 1 || info.m_numChannelsMax > 4 )
  1434. {
  1435. VTexWarning( "%s: Invalid setting restricting number of channels to %d, discarded!\n", info.m_SrcName, info.m_numChannelsMax );
  1436. }
  1437. else if ( info.m_numChannelsMax < 4 )
  1438. {
  1439. if ( 4 != ImageLoader::SizeInBytes( pTexture->Format() ) )
  1440. {
  1441. VTexWarning( "%s: Channels restricted to %d, but cannot fill white"
  1442. " because pixel format is %d (size in bytes %d)!"
  1443. " Proceeding with unmodified channels.\n",
  1444. info.m_SrcName,
  1445. info.m_numChannelsMax, pTexture->Format(), ImageLoader::SizeInBytes( pTexture->Format() ) );
  1446. Assert( 0 );
  1447. }
  1448. else
  1449. {
  1450. // Fill other channels with white
  1451. unsigned char *pDestBits = pTexture->ImageData( nFrame, nFace, 0, 0, 0, z );
  1452. int nWidth = pTexture->Width();
  1453. int nHeight = pTexture->Height();
  1454. int nPaintOff = info.m_numChannelsMax;
  1455. int nPaintBytes = 4 - nPaintOff;
  1456. pDestBits += nPaintOff;
  1457. for( int j = 0; j < nHeight; ++ j )
  1458. {
  1459. for ( int k = 0; k < nWidth; ++ k, pDestBits += 4 )
  1460. {
  1461. memset( pDestBits, 0xFF, nPaintBytes );
  1462. }
  1463. }
  1464. }
  1465. }
  1466. return true;
  1467. }
  1468. //-----------------------------------------------------------------------------
  1469. // Loads source image data
  1470. //-----------------------------------------------------------------------------
  1471. static bool LoadSourceImages( IVTFTexture *pTexture, const char *pFullNameWithoutExtension,
  1472. VTexConfigInfo_t &info )
  1473. {
  1474. // The input file name here is simply for error reporting
  1475. char *pInputFileName = ( char * )stackalloc( strlen( pFullNameWithoutExtension ) + strlen( GetSourceExtension() ) + 1 );
  1476. strcpy( pInputFileName, pFullNameWithoutExtension );
  1477. strcat( pInputFileName, GetSourceExtension() );
  1478. int nFrameCount;
  1479. bool bAnimated = !( info.m_nStartFrame == -1 || info.m_nEndFrame == -1 );
  1480. if( !bAnimated )
  1481. {
  1482. nFrameCount = 1;
  1483. }
  1484. else
  1485. {
  1486. nFrameCount = info.m_nEndFrame - info.m_nStartFrame + 1;
  1487. }
  1488. bool bIsVolumeTexture = ( info.m_nVolumeTextureDepth > 1 );
  1489. // Iterate over all faces of all frames
  1490. // UNDONE: optimize the below for cubemaps (so it doesn't load+crop the source image 6 times!)
  1491. int nFaceCount = info.m_bIsCubeMap ? CUBEMAP_FACE_COUNT : 1;
  1492. int nMipCount = 1;
  1493. for( int iFrame = 0; iFrame < nFrameCount; ++iFrame )
  1494. {
  1495. for( int iFace = 0; iFace < nFaceCount; ++iFace )
  1496. {
  1497. for( int iMip = 0; iMip < nMipCount; ++iMip )
  1498. {
  1499. for ( int z = 0; z < info.m_nVolumeTextureDepth; ++z )
  1500. {
  1501. // Generate the filename to load....
  1502. MakeSrcFileName( info, pFullNameWithoutExtension, iFrame, iFace, iMip, bIsVolumeTexture ? z : -1 );
  1503. // Don't fail if we run out of 'manualmip' mip levels to load (the rest can be generated).
  1504. // FIXME: upgrade PostProcess to take 'nLoadedMipLevels' instead of 'bLoadedMipLevels', so it generates
  1505. // only *missing* mip levels in 'manualmip' mode (as it is, missing levels end up opaque white!)
  1506. bool bFailOnError = ( iMip == 0 );
  1507. // Load the image file from disk...
  1508. CUtlBuffer fileBuffer;
  1509. if ( !LoadFile( info.m_SrcName, fileBuffer, bFailOnError,
  1510. ( g_eMode != BITMAP_FILE_TYPE_PSD ) ? &info.m_uiInputHash : NULL ) )
  1511. {
  1512. // If we want to fail on error and VTexError didn't abort then
  1513. // simply notify the caller that we failed
  1514. if ( bFailOnError )
  1515. return false;
  1516. continue;
  1517. }
  1518. // Initialize the VTF Texture here if we haven't already....
  1519. // Note that we have to do it here because we have to get the width + height from the file
  1520. if ( !pTexture->ImageData() )
  1521. {
  1522. InitializeSrcTexture( pTexture, info.m_SrcName, fileBuffer, info.m_nVolumeTextureDepth, nFrameCount, info );
  1523. nMipCount = info.m_bManualMip ? pTexture->MipCount() : 1;
  1524. }
  1525. // NOTE: if doing 'manualmip', this loads individual MIPs, otherwise we'll just load
  1526. // mip 0 and the rest will be generated later, by pVTFTexture::PostProcess
  1527. if ( !LoadFace( pTexture, fileBuffer, z, iFrame, iFace, iMip, 2.2, info ) )
  1528. {
  1529. Error( "Cannot load texture %s\n", pInputFileName );
  1530. }
  1531. }
  1532. }
  1533. }
  1534. }
  1535. return true;
  1536. }
  1537. //-----------------------------------------------------------------------------
  1538. // Initializes an image
  1539. //-----------------------------------------------------------------------------
  1540. static CDmeImageArray *InitializeImageArray( CDmePrecompiledTexture *pPrecompiledTexture, int nBitmapCount, Bitmap_t *pBitmap )
  1541. {
  1542. int nWidth = pBitmap[0].Width();
  1543. int nHeight = pBitmap[0].Height();
  1544. ImageFormat fmt = pBitmap[0].Format();
  1545. int nFaceCount = pPrecompiledTexture->m_nTextureArraySize;
  1546. int nDepth = pPrecompiledTexture->m_nVolumeTextureDepth;
  1547. // FIXME: Is there a better way of dealing with gamma?
  1548. float flGamma = pPrecompiledTexture->m_bNormalMap ? 1.0f : 2.2f;
  1549. // Deal with cubemaps, which are authored all in the same image
  1550. Bitmap_t cubeBitmaps[CUBEMAP_FACE_COUNT];
  1551. bool bIsCubemap = ( pPrecompiledTexture->m_nTextureType == DMETEXTURE_TYPE_CUBEMAP );
  1552. if ( bIsCubemap )
  1553. {
  1554. if ( ( nWidth % 4 ) || ( nHeight % 3 ) )
  1555. {
  1556. VTexError( "TGA is wrong size for cubemap - [%d,%d] - should be 4x3 grid of squares!\n", nWidth, nHeight );
  1557. return NULL;
  1558. }
  1559. Assert( nDepth == 1 && nBitmapCount == 1 );
  1560. nWidth /= 4;
  1561. nHeight /= 3;
  1562. for ( int i = 0; i < CUBEMAP_FACE_COUNT; ++i )
  1563. {
  1564. ExtractFaceSubrect( pBitmap[0], &cubeBitmaps[i], i );
  1565. }
  1566. nBitmapCount = nFaceCount = CUBEMAP_FACE_COUNT;
  1567. pBitmap = cubeBitmaps;
  1568. }
  1569. int nBitmapIndex = 0;
  1570. CDmeImageArray *pImageArray = CreateElement< CDmeImageArray >( "mip", pPrecompiledTexture->GetFileId() );
  1571. for ( int i = 0; i < nFaceCount; ++i )
  1572. {
  1573. CDmeImage *pImage = CreateElement< CDmeImage >( "image", pImageArray->GetFileId() );
  1574. pImage->Init( nWidth, nHeight, nDepth, fmt, flGamma );
  1575. int nSizeToCopy = pImage->ZSliceSizeInBytes();
  1576. CUtlBinaryBlock &buf = pImage->BeginModification();
  1577. uint8 *pBase = (uint8*)buf.Get();
  1578. for ( int j = 0; j < nDepth; ++j, ++nBitmapIndex )
  1579. {
  1580. Q_memcpy( pBase + j * nSizeToCopy, pBitmap[nBitmapIndex].GetBits(), nSizeToCopy );
  1581. }
  1582. pImage->EndModification();
  1583. pImageArray->AddImage( pImage );
  1584. }
  1585. return pImageArray;
  1586. }
  1587. //-----------------------------------------------------------------------------
  1588. // Loads all bitmaps for a slice
  1589. //-----------------------------------------------------------------------------
  1590. static bool LoadBitmaps( CDmePrecompiledTexture *pPrecompiledTexture, const char *pFullPath,
  1591. BitmapFileType_t mode, int nFrame, int nMip, Bitmap_t *pBitmaps, CRC32_t *pInputHash )
  1592. {
  1593. bool bIsVolumeTexture = ( pPrecompiledTexture->m_nVolumeTextureDepth > 1 );
  1594. int nFaceCount = pPrecompiledTexture->m_nTextureArraySize;
  1595. int nBitmapIndex = 0;
  1596. for( int iFace = 0; iFace < nFaceCount; ++iFace )
  1597. {
  1598. for ( int z = 0; z < pPrecompiledTexture->m_nVolumeTextureDepth; ++z )
  1599. {
  1600. // Generate the filename to load....
  1601. char pSrcFile[ MAX_PATH ];
  1602. MakeSrcFileName( pPrecompiledTexture, pFullPath, nFrame, iFace, nMip, bIsVolumeTexture ? z : -1, pSrcFile, sizeof(pSrcFile) );
  1603. // Don't fail if we run out of 'manualmip' mip levels to load (the rest can be generated).
  1604. // FIXME: upgrade PostProcess to take 'nLoadedMipLevels' instead of 'bLoadedMipLevels', so it generates
  1605. // only *missing* mip levels in 'manualmip' mode (as it is, missing levels end up opaque white!)
  1606. bool bFailOnError = ( nMip == 0 );
  1607. // Load the image file from disk...
  1608. CUtlBuffer fileBuffer;
  1609. if ( !LoadFile( pSrcFile, fileBuffer, bFailOnError, pInputHash ) )
  1610. {
  1611. // If we want to fail on error and VTexError didn't abort then
  1612. // simply notify the caller that we failed
  1613. if ( bFailOnError )
  1614. return false;
  1615. continue;
  1616. }
  1617. // NOTE: if doing 'manualmip', this loads individual MIPs, otherwise we'll just load
  1618. // mip 0 and the rest will be generated later, by pVTFTexture::PostProcess
  1619. BitmapFileType_t nLoadedMode = LoadBitmapFile( fileBuffer, &pBitmaps[nBitmapIndex] );
  1620. if ( nLoadedMode == BITMAP_FILE_TYPE_UNKNOWN )
  1621. {
  1622. VTexError( "Cannot load texture %s\n", pSrcFile );
  1623. return false;
  1624. }
  1625. if ( nBitmapIndex > 0 )
  1626. {
  1627. if ( pBitmaps[0].Width() != pBitmaps[nBitmapIndex].Width() ||
  1628. pBitmaps[0].Height() != pBitmaps[nBitmapIndex].Height() ||
  1629. pBitmaps[0].Format() != pBitmaps[nBitmapIndex].Format() )
  1630. {
  1631. VTexError( "Found inconsistent sizes or color formats in texture faces\\z slices!\n" );
  1632. return false;
  1633. }
  1634. }
  1635. ++nBitmapIndex;
  1636. }
  1637. }
  1638. return true;
  1639. }
  1640. //-----------------------------------------------------------------------------
  1641. // Loads source image data
  1642. //-----------------------------------------------------------------------------
  1643. static bool LoadSourceImages( const char *pFullDir, CDmePrecompiledTexture *pPrecompiledTexture,
  1644. BitmapFileType_t mode, CRC32_t *pInputHash )
  1645. {
  1646. // The input file name here is simply for error reporting
  1647. char pFullPath[ MAX_PATH ];
  1648. const char *pInputFileName = pPrecompiledTexture->m_ImageFileName;
  1649. Q_ComposeFileName( pFullDir, pInputFileName, pFullPath, sizeof(pFullPath) );
  1650. bool bAnimated = !( pPrecompiledTexture->m_nStartFrame == -1 || pPrecompiledTexture->m_nEndFrame == -1 );
  1651. int nFrameCount = ( !bAnimated ) ? 1 : pPrecompiledTexture->m_nEndFrame - pPrecompiledTexture->m_nStartFrame + 1;
  1652. // Iterate over all faces of all frames
  1653. // NOTE: Cubemaps are handled specially. Only 1 texture is loaded, but it
  1654. // has all cube side faces in the same texture
  1655. bool bComputedMipCount = false;
  1656. int nMipCount = 1;
  1657. int nImageCount = pPrecompiledTexture->m_nVolumeTextureDepth * pPrecompiledTexture->m_nTextureArraySize;
  1658. Bitmap_t *pBitmaps = (Bitmap_t*)stackalloc( nImageCount * sizeof(Bitmap_t) );
  1659. memset( pBitmaps, 0, nImageCount * sizeof(Bitmap_t) );
  1660. for( int iFrame = 0; iFrame < nFrameCount; ++iFrame )
  1661. {
  1662. CDmeTextureFrame *pFrame = pPrecompiledTexture->m_pSourceTexture->AddFrame();
  1663. for( int iMip = 0; iMip < nMipCount; ++iMip )
  1664. {
  1665. if ( !LoadBitmaps( pPrecompiledTexture, pFullPath, mode, iFrame, iMip, pBitmaps, pInputHash ) )
  1666. return false;
  1667. /*
  1668. // Check that the image is the right size for this mip level
  1669. int nMipLevelWidth, nMipLevelHeight, nMipLevelDepth;
  1670. pTexture->ComputeMipLevelDimensions( nMipLevel, &nMipLevelWidth, &nMipLevelHeight, &nMipLevelDepth );
  1671. if ( ( bitmap.m_nWidth != info.m_nReduceX*nMipLevelWidth ) || ( bitmap.m_nHeight != info.m_nReduceY*nMipLevelHeight ) )
  1672. {
  1673. VTexError( "'manualmip' source image wrong size for mip %d! (%s)\n", nMipLevel, info.m_SrcName );
  1674. }
  1675. */
  1676. CDmeImageArray *pMipLevel = InitializeImageArray( pPrecompiledTexture, nImageCount, pBitmaps );
  1677. pFrame->AddMipLevel( pMipLevel );
  1678. // Note that we have to do compute mip count here
  1679. // because we have to get the width + height from the file
  1680. if ( !bComputedMipCount && pPrecompiledTexture->m_bLoadMipLevels )
  1681. {
  1682. nMipCount = ImageLoader::GetNumMipMapLevels( pMipLevel->Width(), pMipLevel->Height(), pMipLevel->Depth() );
  1683. bComputedMipCount = true;
  1684. }
  1685. }
  1686. }
  1687. stackfree( pBitmaps );
  1688. return true;
  1689. }
  1690. void NormalInvertGreen( IVTFTexture *pTexture )
  1691. {
  1692. if ( pTexture->Format() != IMAGE_FORMAT_RGBA8888 )
  1693. VTexError( "Cannot 'invert green', normal map in unexpected format\n" );
  1694. if ( pTexture->Depth() > 1 )
  1695. VTexError( "Cannot 'invert green', normal map is a volume texture?!\n" );
  1696. for ( int iFrame = 0; iFrame < pTexture->FrameCount(); iFrame++ )
  1697. {
  1698. for ( int iFace = 0; iFace < pTexture->FaceCount(); iFace++ )
  1699. {
  1700. for ( int iMip = 0; iMip < pTexture->MipCount(); iMip++ )
  1701. {
  1702. int nWidth, nHeight, nDepth;
  1703. pTexture->ComputeMipLevelDimensions( iMip, &nWidth, &nHeight, &nDepth );
  1704. unsigned char *pPixels = pTexture->ImageData( iFrame, iFace, iMip );
  1705. for ( int i = 0; i < nWidth*nHeight*nDepth; i++ )
  1706. {
  1707. pPixels[ 1 ] = 255 - pPixels[ 1 ];
  1708. pPixels += 4;
  1709. }
  1710. }
  1711. }
  1712. }
  1713. }
  1714. void PreprocessSkyBox( char *pFullNameWithoutExtension, int *iSkyboxFace )
  1715. {
  1716. // This is now an old codepath, possibly to be deprecated (though 'buildcubemaps' still depends on it)
  1717. Assert( g_bOldCubemapPath );
  1718. // When we get here, it means that we're processing one face of a skybox, but we're going to
  1719. // load all the faces and treat it as a cubemap so we can do the edge matching.
  1720. // Since they passed in only one face of the skybox, there's a 2 letter extension we want to get rid of.
  1721. int len = strlen( pFullNameWithoutExtension );
  1722. if ( len >= 3 )
  1723. {
  1724. // Make sure there really is a 2 letter extension.
  1725. char *pEnd = &pFullNameWithoutExtension[ len - 2 ];
  1726. *iSkyboxFace = -1;
  1727. for ( int i=0; i < ARRAYSIZE( g_CubemapFacingNames ); i++ )
  1728. {
  1729. if ( stricmp( pEnd, g_CubemapFacingNames[i] ) == 0 )
  1730. {
  1731. *iSkyboxFace = i;
  1732. break;
  1733. }
  1734. }
  1735. // Cut off the 2 letter extension.
  1736. if ( *iSkyboxFace != -1 )
  1737. {
  1738. pEnd[0] = 0;
  1739. return;
  1740. }
  1741. }
  1742. Error( "PreprocessSkyBox: filename %s doesn't have a proper extension (bk, dn, rt, etc..)\n", pFullNameWithoutExtension );
  1743. }
  1744. void PostProcessSkyBox( SmartIVTFTexture &pSrcTexture, const char *pDstFileName,
  1745. const char *pOutputDir, const char *pBaseName, VTexConfigInfo_t const &info,
  1746. CUtlVector< OutputTexture_t > &outputTextures, int iSkyboxFace )
  1747. {
  1748. // Split the cubemap into 6 separate images, optionally optimizing (cropping) them
  1749. int startFace = 0;
  1750. int numFaces = 6;
  1751. if ( g_bOldCubemapPath )
  1752. {
  1753. // NOTE: this is the old path, possibly to be deprecated
  1754. // Right now, we've got a full cubemap, and we want to return the one face of the
  1755. // skybox that we're supposed to be processing.
  1756. startFace = iSkyboxFace;
  1757. numFaces = 1;
  1758. }
  1759. // Input is a cubemap, output is one flat texture per face
  1760. int nFlags = pSrcTexture->Flags();
  1761. Assert( info.m_bIsCubeMap && info.m_bIsSkyBox && ( nFlags & TEXTUREFLAGS_ENVMAP ) );
  1762. nFlags &= ~TEXTUREFLAGS_ENVMAP;
  1763. for ( int iFace = startFace; iFace < ( startFace + numFaces ); iFace++ )
  1764. {
  1765. int nWidth = pSrcTexture->Width();
  1766. int nHeight = pSrcTexture->Height();
  1767. if ( info.m_bIsCroppedSkyBox && ( iFace != CUBEMAP_FACE_UP ) )
  1768. {
  1769. // Crop skybox output images to avoid wasting memory on unseen portions below the horizon
  1770. if ( iFace == CUBEMAP_FACE_DOWN )
  1771. {
  1772. nWidth = ( nWidth > 4 ) ? 4 : nWidth;
  1773. nHeight = ( nHeight > 4 ) ? 4 : nHeight;
  1774. }
  1775. else
  1776. {
  1777. nHeight = ( nHeight + 1 ) / 2;
  1778. }
  1779. }
  1780. IVTFTexture *pFaceTexture = CreateVTFTexture();
  1781. if ( !pFaceTexture->Init( nWidth, nHeight, 1, pSrcTexture->Format(), nFlags, pSrcTexture->FrameCount() ) )
  1782. Error( "PostProcessSkyBox: IVTFTexture::Init() failed.\n" );
  1783. // Copy across the data for this face - cropping happens because "destSize < sourceSize"
  1784. // NOTE: This only works because:
  1785. // (a) we are cropping the bottom half of the image (except for
  1786. // CUBEMAP_FACE_DOWN, for which output colours don't actually matter)
  1787. // (b) we assume power-of-two input textures
  1788. // (c) DXT-compressed mip levels are padded to 4x4 block sizes
  1789. // There are a few things that don't get copied here, like alpha test
  1790. // threshold and bumpscale, but we shouldn't need those for skyboxes anyway.
  1791. int nMips = pFaceTexture->MipCount();
  1792. for ( int iMip = 0; iMip < nMips; iMip++ )
  1793. {
  1794. int srcMipSize = pSrcTexture->ComputeMipSize( iMip );
  1795. int dstMipSize = pFaceTexture->ComputeMipSize( iMip );
  1796. if ( ( srcMipSize != dstMipSize ) &&
  1797. ( ( srcMipSize < dstMipSize ) || ( ( srcMipSize != 2*dstMipSize ) && ( iFace != CUBEMAP_FACE_DOWN ) ) ) )
  1798. {
  1799. Error( "PostProcessSkyBox: src/dest mipmap size mismatch during skybox cropping (src=%d, dest=%d)\n", srcMipSize, dstMipSize );
  1800. }
  1801. for ( int iFrame = 0; iFrame < pSrcTexture->FrameCount(); iFrame++ )
  1802. {
  1803. unsigned char *pDst = pFaceTexture->ImageData( iFrame, 0, iMip );
  1804. const unsigned char *pSrc = pSrcTexture->ImageData( iFrame, iFace, iMip );
  1805. memcpy( pDst, pSrc, dstMipSize );
  1806. }
  1807. }
  1808. // Add this to the list of outputs
  1809. OutputTexture_t outputTexture;
  1810. outputTexture.pTexture = pFaceTexture;
  1811. const char *pSuffix = g_bOldCubemapPath ? "" : g_CubemapFacingNames[ iFace ];
  1812. MakeOutputFileName( outputTexture.dstFileName, ARRAYSIZE( outputTexture.dstFileName ), pOutputDir, pBaseName, pSuffix, info );
  1813. outputTextures.AddToTail( outputTexture );
  1814. }
  1815. // Get rid of the full cubemap one and return the single-face one.
  1816. DestroyVTFTexture( pSrcTexture.Get() );
  1817. pSrcTexture.Assign( NULL );
  1818. }
  1819. //-----------------------------------------------------------------------------
  1820. // Does a file exist? (doesn't use search paths)
  1821. //-----------------------------------------------------------------------------
  1822. bool FileExistsAbsolute( const char *pFileName )
  1823. {
  1824. return ( 00 == access( pFileName, 00 ) );
  1825. }
  1826. void MakeDirHier( const char *pPath )
  1827. {
  1828. #ifdef PLATFORM_POSIX
  1829. #define mkdir(s) mkdir(s, S_IRWXU | S_IRWXG | S_IRWXO )
  1830. #endif
  1831. char temp[1024];
  1832. Q_strncpy( temp, pPath, 1024 );
  1833. int i;
  1834. for( i = 0; i < strlen( temp ); i++ )
  1835. {
  1836. if( temp[i] == '/' || temp[i] == '\\' )
  1837. {
  1838. temp[i] = '\0';
  1839. // DebugOut( "mkdir( %s )\n", temp );
  1840. mkdir( temp );
  1841. temp[i] = '\\';
  1842. }
  1843. }
  1844. // DebugOut( "mkdir( %s )\n", temp );
  1845. mkdir( temp );
  1846. }
  1847. static uint8 GetClampingValue( int nClampSize )
  1848. {
  1849. if ( nClampSize <= 0 )
  1850. return 30; // ~1 billion
  1851. int nRet = 0;
  1852. while ( nClampSize > 1 )
  1853. {
  1854. nClampSize >>= 1;
  1855. nRet++;
  1856. }
  1857. return nRet;
  1858. }
  1859. static void SetTextureLodData( IVTFTexture *pTexture, VTexConfigInfo_t const &info )
  1860. {
  1861. if (
  1862. ( info.m_nMaxDimensionX > 0 && info.m_nMaxDimensionX < pTexture->Width() ) ||
  1863. ( info.m_nMaxDimensionY > 0 && info.m_nMaxDimensionY < pTexture->Height() ) ||
  1864. ( info.m_nMaxDimensionX_360 > 0 && info.m_nMaxDimensionX_360 < pTexture->Width() ) ||
  1865. ( info.m_nMaxDimensionY_360 > 0 && info.m_nMaxDimensionY_360 < pTexture->Height() )
  1866. )
  1867. {
  1868. TextureLODControlSettings_t lodChunk;
  1869. memset( &lodChunk, 0, sizeof( lodChunk ) );
  1870. lodChunk.m_ResolutionClampX = GetClampingValue( info.m_nMaxDimensionX );
  1871. lodChunk.m_ResolutionClampY = GetClampingValue( info.m_nMaxDimensionY );
  1872. lodChunk.m_ResolutionClampX_360 = GetClampingValue( info.m_nMaxDimensionX_360 );
  1873. lodChunk.m_ResolutionClampY_360 = GetClampingValue( info.m_nMaxDimensionY_360 );
  1874. pTexture->SetResourceData( VTF_RSRC_TEXTURE_LOD_SETTINGS, &lodChunk, sizeof( lodChunk ) );
  1875. }
  1876. }
  1877. static void AttachShtFile( const char *pFullNameWithoutExtension, IVTFTexture *pTexture, CRC32_t *puiHash )
  1878. {
  1879. char shtName[MAX_PATH];
  1880. Q_strncpy( shtName, pFullNameWithoutExtension, sizeof(shtName) );
  1881. Q_SetExtension( shtName, ".sht", sizeof(shtName) );
  1882. if ( !FileExistsAbsolute( shtName ) )
  1883. return;
  1884. VTexMsg( "Attaching .sht file %s.\n", shtName );
  1885. // Ok, the file exists. Read it.
  1886. CUtlBuffer buf;
  1887. if ( !LoadFile( shtName, buf, false, puiHash ) )
  1888. return;
  1889. pTexture->SetResourceData( VTF_RSRC_SHEET, buf.Base(), buf.TellPut() );
  1890. }
  1891. //-----------------------------------------------------------------------------
  1892. // Does the dirty deed and generates a VTF file
  1893. //-----------------------------------------------------------------------------
  1894. bool ProcessFiles( const char *pFullNameWithoutExtension, const char *pOutputDir, const char *pBaseName, VTexConfigInfo_t &info )
  1895. {
  1896. // force clamps/clampt for cube maps
  1897. if( info.m_bIsCubeMap )
  1898. {
  1899. info.m_nFlags |= TEXTUREFLAGS_ENVMAP;
  1900. info.m_nFlags |= TEXTUREFLAGS_CLAMPS;
  1901. info.m_nFlags |= TEXTUREFLAGS_CLAMPT;
  1902. }
  1903. // Create the texture we're gonna store out
  1904. SmartIVTFTexture pVTFTexture( CreateVTFTexture() );
  1905. int iSkyboxFace = 0;
  1906. char fullNameTemp[512];
  1907. if ( info.m_bIsSkyBox && g_bOldCubemapPath )
  1908. {
  1909. Q_strncpy( fullNameTemp, pFullNameWithoutExtension, sizeof( fullNameTemp ) );
  1910. pFullNameWithoutExtension = fullNameTemp;
  1911. PreprocessSkyBox( fullNameTemp, &iSkyboxFace );
  1912. }
  1913. // Load the source images into the texture
  1914. bool bLoadedSourceImages = LoadSourceImages( pVTFTexture.Get(), pFullNameWithoutExtension, info );
  1915. if ( !bLoadedSourceImages )
  1916. {
  1917. VTexError( "Can't load source images for \"%s\"\n", pFullNameWithoutExtension );
  1918. return false;
  1919. }
  1920. // Attach a sheet file if present
  1921. AttachShtFile( pFullNameWithoutExtension, pVTFTexture.Get(), &info.m_uiInputHash );
  1922. // No more file loads, finalize the sources hash
  1923. CRC32_Final( &info.m_uiInputHash );
  1924. pVTFTexture->SetResourceData( VTexConfigInfo_t::VTF_INPUTSRC_CRC, &info.m_uiInputHash, sizeof( info.m_uiInputHash ) );
  1925. CRC32_t crcWritten = info.m_uiInputHash;
  1926. // Name of the destination file
  1927. char dstFileName[1024];
  1928. const char *pSuffix = "";
  1929. MakeOutputFileName( dstFileName, ARRAYSIZE( dstFileName ), pOutputDir, pBaseName, pSuffix, info );
  1930. // Now if we are only validating the CRC
  1931. if( CommandLine()->FindParm( "-crcvalidate" ) )
  1932. {
  1933. CUtlBuffer bufFile;
  1934. bool bLoad = LoadFile( dstFileName, bufFile, false, NULL );
  1935. if ( !bLoad )
  1936. {
  1937. VTexMsgEx( stderr, "LOAD ERROR: %s\n", dstFileName );
  1938. return false;
  1939. }
  1940. SmartIVTFTexture spExistingVtf( CreateVTFTexture() );
  1941. bLoad = spExistingVtf->Unserialize( bufFile );
  1942. if ( !bLoad )
  1943. {
  1944. VTexMsgEx( stderr, "UNSERIALIZE ERROR: %s\n", dstFileName );
  1945. return false;
  1946. }
  1947. size_t numDataBytes;
  1948. void *pCrcData = spExistingVtf->GetResourceData( VTexConfigInfo_t::VTF_INPUTSRC_CRC, &numDataBytes );
  1949. if ( !pCrcData || numDataBytes != sizeof( CRC32_t ) )
  1950. {
  1951. VTexMsgEx( stderr, "OLD TEXTURE FORMAT: %s\n", dstFileName );
  1952. return false;
  1953. }
  1954. CRC32_t crcFile = * reinterpret_cast< CRC32_t const * >( pCrcData );
  1955. if ( crcFile != crcWritten )
  1956. {
  1957. VTexMsgEx( stderr, "CRC MISMATCH: %s\n", dstFileName );
  1958. return false;
  1959. }
  1960. VTexMsgEx( stderr, "OK: %s\n", dstFileName );
  1961. return true;
  1962. }
  1963. // Now if we are not forcing the CRC
  1964. if( !CommandLine()->FindParm( "-crcforce" ) )
  1965. {
  1966. CUtlBuffer bufFile;
  1967. if ( LoadFile( dstFileName, bufFile, false, NULL ) )
  1968. {
  1969. SmartIVTFTexture spExistingVtf( CreateVTFTexture() );
  1970. if ( spExistingVtf->Unserialize( bufFile ) )
  1971. {
  1972. size_t numDataBytes;
  1973. void *pCrcData = spExistingVtf->GetResourceData( VTexConfigInfo_t::VTF_INPUTSRC_CRC, &numDataBytes );
  1974. if ( pCrcData && numDataBytes == sizeof( CRC32_t ) )
  1975. {
  1976. CRC32_t crcFile = * reinterpret_cast< CRC32_t const * >( pCrcData );
  1977. if ( crcFile == crcWritten )
  1978. {
  1979. if( !g_Quiet )
  1980. VTexMsg( "SUCCESS: %s is up-to-date\n", dstFileName );
  1981. if( !CommandLine()->FindParm( "-crcforce" ) )
  1982. return true;
  1983. }
  1984. }
  1985. }
  1986. }
  1987. }
  1988. // Bumpmap scale..
  1989. pVTFTexture->SetBumpScale( info.m_flBumpScale );
  1990. // Alpha test threshold
  1991. pVTFTexture->SetAlphaTestThreshholds( info.m_flAlphaThreshhold, info.m_flAlphaHiFreqThreshhold );
  1992. // Set texture lod data
  1993. SetTextureLodData( pVTFTexture.Get(), info );
  1994. // Get the texture all internally consistent and happy
  1995. bool bAllowFixCubemapOrientation = !info.m_bIsSkyBox; // Don't let it rotate our pseudo-cubemap faces around if it's a skybox.
  1996. pVTFTexture->SetPostProcessingSettings( &info.m_vtfProcOptions );
  1997. pVTFTexture->PostProcess( false, LOOK_DOWN_Z, bAllowFixCubemapOrientation, info.m_bManualMip );
  1998. // Compute the preferred image format
  1999. ImageFormat vtfImageFormat = ComputeDesiredImageFormat( pVTFTexture.Get(), info );
  2000. // Set up the low-res image
  2001. if ( info.m_bIsCubeMap )
  2002. {
  2003. // "Stage 1" of matching cubemap borders. Sometimes, it has to store off the original image.
  2004. pVTFTexture->MatchCubeMapBorders( 1, vtfImageFormat, info.m_bIsSkyBox );
  2005. }
  2006. else
  2007. {
  2008. if ( !IsPowerOfTwo( pVTFTexture->Width() ) || !IsPowerOfTwo( pVTFTexture->Height() ) || !IsPowerOfTwo( pVTFTexture->Depth() ) )
  2009. VTexError( "Cannot create low-res image for non-power of two texture (did you forget to set 'cubemap 1' or 'skybox 1'?)\n" );
  2010. CreateLowResImage( pVTFTexture.Get() );
  2011. }
  2012. // Invert the green channel for some normal maps (depends which package they were authored in)
  2013. if ( info.m_bNormalInvertGreen )
  2014. {
  2015. if ( pVTFTexture->Flags() & TEXTUREFLAGS_NORMAL )
  2016. {
  2017. NormalInvertGreen( pVTFTexture.Get() );
  2018. }
  2019. else
  2020. {
  2021. VTexWarning( "'invertgreen' specified for texture which is not being processed as a normal map!\n" );
  2022. }
  2023. }
  2024. // DXT5 GA compressor assumes we're coming from IMAGE_FORMAT_ARGB8888 but
  2025. // we want to swap XY (RG) for the shader decode, so we first convert to BGRA here as a trick to flop the channels
  2026. if ( info.m_bNormalToDXT5GA )
  2027. {
  2028. pVTFTexture->ConvertImageFormat( IMAGE_FORMAT_BGRA8888, false, false );
  2029. }
  2030. // Convert to the final format
  2031. pVTFTexture->ConvertImageFormat( vtfImageFormat, info.m_bNormalToDuDv, info.m_bNormalToDXT5GA );
  2032. // Stage 2 of matching cubemap borders.
  2033. pVTFTexture->MatchCubeMapBorders( 2, vtfImageFormat, info.m_bIsSkyBox );
  2034. // Finally, write out the VTF(s)
  2035. CUtlVector< OutputTexture_t > outputTextures;
  2036. if ( info.m_bIsSkyBox )
  2037. {
  2038. // Skyboxes need splitting into multiple images (some of which may be cropped)
  2039. PostProcessSkyBox( pVTFTexture, dstFileName, pOutputDir, pBaseName, info, outputTextures, iSkyboxFace );
  2040. }
  2041. else
  2042. {
  2043. OutputTexture_t singleOutput;
  2044. singleOutput.pTexture = pVTFTexture.Get();
  2045. strncpy( singleOutput.dstFileName, dstFileName, MAX_PATH );
  2046. outputTextures.AddToTail( singleOutput );
  2047. pVTFTexture.Assign( NULL );
  2048. }
  2049. for ( int i = 0; i < outputTextures.Count(); i++ )
  2050. {
  2051. if ( info.IsSettings0Valid() )
  2052. {
  2053. outputTextures[ i ].pTexture->SetResourceData( VTF_RSRC_TEXTURE_SETTINGS_EX, &info.m_exSettings0, sizeof( info.m_exSettings0 ) );
  2054. }
  2055. // Write it!
  2056. if ( g_CreateDir == true )
  2057. MakeDirHier( pOutputDir ); //It'll create it if it doesn't exist.
  2058. // Make sure the CRC hasn't been modified since finalized
  2059. Assert( crcWritten == info.m_uiInputHash );
  2060. CUtlBuffer outputBuf;
  2061. if (!outputTextures[ i ].pTexture->Serialize( outputBuf ))
  2062. {
  2063. VTexError( "\"%s\": Unable to serialize the VTF file!\n", dstFileName );
  2064. }
  2065. CP4AutoEditAddFile autop4( outputTextures[ i ].dstFileName );
  2066. FILE *fp = fopen( outputTextures[ i ].dstFileName, "wb" );
  2067. if( !fp )
  2068. {
  2069. VTexError( "Can't open: %s\n", outputTextures[ i ].dstFileName );
  2070. }
  2071. fwrite( outputBuf.Base(), 1, outputBuf.TellPut(), fp );
  2072. fclose( fp );
  2073. if ( CommandLine()->FindParm( "-source2" ) )
  2074. {
  2075. char pCmdLine[1024];
  2076. Q_snprintf( pCmdLine, sizeof(pCmdLine), "resourcecompiler.exe -i %s\n", outputTextures[ i ].dstFileName );
  2077. system( pCmdLine );
  2078. }
  2079. }
  2080. VTexMsg( "SUCCESS: Vtf file created\n" );
  2081. return true;
  2082. }
  2083. const char *GetPossiblyQuotedWord( const char *pInBuf, char *pOutbuf )
  2084. {
  2085. pInBuf += strspn( pInBuf, " \t" ); // skip whitespace
  2086. const char *pWordEnd;
  2087. bool bQuote = false;
  2088. if (pInBuf[0]=='"')
  2089. {
  2090. pInBuf++;
  2091. pWordEnd=strchr(pInBuf,'"');
  2092. bQuote = true;
  2093. }
  2094. else
  2095. {
  2096. pWordEnd=strchr(pInBuf,' ');
  2097. if (! pWordEnd )
  2098. pWordEnd = strchr(pInBuf,'\t' );
  2099. if (! pWordEnd )
  2100. pWordEnd = pInBuf+strlen(pInBuf);
  2101. }
  2102. if ((! pWordEnd ) || (pWordEnd == pInBuf ) )
  2103. return NULL; // no word found
  2104. memcpy( pOutbuf, pInBuf, pWordEnd-pInBuf );
  2105. pOutbuf[pWordEnd-pInBuf]=0;
  2106. pInBuf = pWordEnd;
  2107. if ( bQuote )
  2108. pInBuf++;
  2109. return pInBuf;
  2110. }
  2111. // GetKeyValueFromBuffer:
  2112. // fills in "key" and "val" respectively and returns "true" if succeeds.
  2113. // returns false if:
  2114. // a) end-of-buffer is reached (then "val" is empty)
  2115. // b) error occurs (then "val" is the error message)
  2116. //
  2117. static bool GetKeyValueFromBuffer( CUtlBuffer &buffer, char *key, char *val )
  2118. {
  2119. char buf[2048];
  2120. while( buffer.GetBytesRemaining() )
  2121. {
  2122. buffer.GetLine( buf, sizeof( buf ) );
  2123. // Scanning algorithm
  2124. char *pComment = strpbrk( buf, "#\n\r" );
  2125. if ( pComment )
  2126. *pComment = 0;
  2127. pComment = strstr( buf, "//" );
  2128. if ( pComment)
  2129. *pComment = 0;
  2130. const char *scan = buf;
  2131. scan=GetPossiblyQuotedWord( scan, key );
  2132. if ( scan )
  2133. {
  2134. scan=GetPossiblyQuotedWord( scan, val );
  2135. if ( scan )
  2136. return true;
  2137. else
  2138. {
  2139. sprintf( val, "parameter %s has no value", key );
  2140. return false;
  2141. }
  2142. }
  2143. }
  2144. val[0] = 0;
  2145. return false;
  2146. }
  2147. bool HasSuffix( const char *pFileName, const char *pSuffix )
  2148. {
  2149. if ( !pFileName || !*pFileName || !pSuffix || !*pSuffix )
  2150. return false;
  2151. int fileLen = Q_strlen( pFileName );
  2152. int suffixLen = Q_strlen( pSuffix );
  2153. if ( fileLen <= suffixLen )
  2154. return false;
  2155. return !Q_strnicmp( pFileName + fileLen - suffixLen, pSuffix, suffixLen );
  2156. }
  2157. //-----------------------------------------------------------------------------
  2158. // Loads the config information from a PSD file
  2159. //-----------------------------------------------------------------------------
  2160. static bool LoadConfigFromPSD( const char *pFileName, CUtlBuffer &buf, bool bMissingConfigOk, CRC32_t *pCRC )
  2161. {
  2162. CUtlBuffer bufFile;
  2163. bool bOK = LoadFile( pFileName, bufFile, false, pCRC );
  2164. if ( !bOK )
  2165. {
  2166. VTexError( "VTex: \"%s\" is not a valid PSD file!\n", pFileName );
  2167. return false;
  2168. }
  2169. VTexMsg( "config file %s\n", pFileName );
  2170. bOK = IsPSDFile( bufFile );
  2171. if ( !bOK )
  2172. {
  2173. VTexError( "VTex: \"%s\" is not a valid PSD file!\n", pFileName );
  2174. return true;
  2175. }
  2176. PSDImageResources imgres = PSDGetImageResources( bufFile );
  2177. PSDResFileInfo resFileInfo( imgres.FindElement( PSDImageResources::eResFileInfo ) );
  2178. PSDResFileInfo::ResFileInfoElement descr = resFileInfo.FindElement( PSDResFileInfo::eDescription );
  2179. if ( !descr.m_pvData )
  2180. {
  2181. if ( bMissingConfigOk )
  2182. return true;
  2183. VTexError( "VTex: \"%s\" does not contain vtex configuration info!\n", pFileName );
  2184. return true;
  2185. }
  2186. buf.EnsureCapacity( descr.m_numBytes );
  2187. buf.Put( descr.m_pvData, descr.m_numBytes );
  2188. return true;
  2189. }
  2190. //-----------------------------------------------------------------------------
  2191. // Loads the .psd file or .txt file associated with the .tga and gets out various data
  2192. //-----------------------------------------------------------------------------
  2193. static bool LoadConfigFile( const char *pFileBaseName, VTexConfigInfo_t &info )
  2194. {
  2195. // Tries to load .txt, then .psd
  2196. bool bOK = false;
  2197. int lenBaseName = strlen( pFileBaseName );
  2198. char *pFileName = ( char * )stackalloc( lenBaseName + strlen( ".tga" ) + 1 );
  2199. strcpy( pFileName, pFileBaseName );
  2200. // Try TGA file with config
  2201. memcpy( pFileName + lenBaseName, ".tga", 5 );
  2202. bool bTgaExists = FileExistsAbsolute( pFileName );
  2203. if ( bTgaExists && !g_bNoTga )
  2204. {
  2205. // Look for the TGA's associated TXT file
  2206. g_eMode = BITMAP_FILE_TYPE_TGA;
  2207. memcpy( pFileName + lenBaseName, ".txt", 5 );
  2208. CUtlBuffer bufFile( 0, 0, CUtlBuffer::TEXT_BUFFER );
  2209. bOK = LoadFile( pFileName, bufFile, false, &info.m_uiInputHash );
  2210. if ( bOK )
  2211. {
  2212. VTexMsg( "Config file %s\n", pFileName );
  2213. {
  2214. char key[2048];
  2215. char val[2048];
  2216. while( GetKeyValueFromBuffer( bufFile, key, val ) )
  2217. {
  2218. info.ParseOptionKey( key, val );
  2219. }
  2220. if ( val[0] )
  2221. {
  2222. VTexError( "%s: %s\n", pFileName, val );
  2223. return false;
  2224. }
  2225. if ( g_eMode == BITMAP_FILE_TYPE_PFM )
  2226. {
  2227. VTexWarning( "%s specifies PFM, but TGA with same name also exists - possible ambiguity?\n", pFileName );
  2228. }
  2229. }
  2230. }
  2231. else
  2232. {
  2233. memcpy( pFileName + lenBaseName, ".tga", 5 );
  2234. //VTexMsg( "No config file for %s\n", pFileName );
  2235. bOK = true;
  2236. }
  2237. }
  2238. if ( g_bNoTga && bTgaExists )
  2239. {
  2240. VTexWarningNoPause( "-notga disables \"%s\"\n", pFileName );
  2241. }
  2242. // PSD file attempt
  2243. memcpy( pFileName + lenBaseName, ".psd", 5 );
  2244. bool bPsdExists = FileExistsAbsolute( pFileName );
  2245. if ( !bOK && bPsdExists && !g_bNoPsd && !g_bUsePfm ) // If PSD mode was not disabled
  2246. {
  2247. g_eMode = BITMAP_FILE_TYPE_PSD;
  2248. CUtlBuffer bufDescr( 0, 0, CUtlBuffer::TEXT_BUFFER );
  2249. bOK = LoadConfigFromPSD( pFileName, bufDescr, true, &info.m_uiInputHash );
  2250. if ( bufDescr.TellMaxPut() > 0 )
  2251. {
  2252. char key[2048];
  2253. char val[2048];
  2254. while( GetKeyValueFromBuffer( bufDescr, key, val ) )
  2255. {
  2256. info.ParseOptionKey( key, val );
  2257. }
  2258. if ( val[0] )
  2259. {
  2260. VTexError( "%s: %s\n", pFileName, val );
  2261. return false;
  2262. }
  2263. }
  2264. }
  2265. else if ( bPsdExists && !g_bUsePfm )
  2266. {
  2267. if ( bOK )
  2268. {
  2269. VTexWarningNoPause( "psd file \"%s\" exists, but not used, delete tga and txt files to use psd file directly\n", pFileName );
  2270. }
  2271. else if ( g_bNoPsd )
  2272. {
  2273. VTexWarningNoPause( "-nopsd disables \"%s\"\n", pFileName );
  2274. }
  2275. }
  2276. // PFM file attempt
  2277. memcpy( pFileName + lenBaseName, ".pfm", 5 );
  2278. bool bPfmExists = FileExistsAbsolute( pFileName );
  2279. if ( !bOK && bPfmExists && g_bUsePfm )
  2280. {
  2281. g_eMode = BITMAP_FILE_TYPE_PFM;
  2282. CUtlBuffer bufFile( 0, 0, CUtlBuffer::TEXT_BUFFER );
  2283. bOK = LoadFile( pFileName, bufFile, false, &info.m_uiInputHash );
  2284. }
  2285. // Try TXT file as config again for TGA cubemap / PFM
  2286. memcpy( pFileName + lenBaseName, ".txt", 5 );
  2287. bool bTxtExists = FileExistsAbsolute( pFileName );
  2288. if ( !bOK && bTxtExists )
  2289. {
  2290. g_eMode = BITMAP_FILE_TYPE_TGA;
  2291. CUtlBuffer bufFile( 0, 0, CUtlBuffer::TEXT_BUFFER );
  2292. bOK = LoadFile( pFileName, bufFile, false, &info.m_uiInputHash );
  2293. if ( bOK )
  2294. {
  2295. VTexMsg( "Config file %s\n", pFileName );
  2296. {
  2297. char key[2048];
  2298. char val[2048];
  2299. while( GetKeyValueFromBuffer( bufFile, key, val ) )
  2300. {
  2301. info.ParseOptionKey( key, val );
  2302. }
  2303. if ( val[0] )
  2304. {
  2305. VTexError( "%s: %s\n", pFileName, val );
  2306. return false;
  2307. }
  2308. }
  2309. }
  2310. }
  2311. if ( g_eMode == BITMAP_FILE_TYPE_PFM )
  2312. {
  2313. if ( g_bUsedAsLaunchableDLL && !( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_NOCOMPRESS ) )
  2314. {
  2315. info.m_nFlags |= TEXTUREFLAGS_NOMIP;
  2316. }
  2317. if ( g_bUsedAsLaunchableDLL && !HasSuffix( pFileBaseName, "_hdr" ) && !HasSuffix( pFileBaseName, "_disp" ) )
  2318. {
  2319. VTexWarning( "PFM files should be suffixed with '_hdr' or '_disp'\n" );
  2320. }
  2321. }
  2322. else if ( g_bUsedAsLaunchableDLL && HasSuffix( pFileBaseName, "hdr" ) )
  2323. {
  2324. VTexWarning( "Only HDR images (.PFM files) should be suffixed with 'hdr'\n" );
  2325. }
  2326. if ( !bOK )
  2327. {
  2328. VTexWarning( " \"%s\" does not specify valid %s%sPFM+TXT files!\n",
  2329. pFileBaseName,
  2330. g_bNoPsd ? "" : "PSD or ",
  2331. g_bNoTga ? "" : "TGA or " );
  2332. return false;
  2333. }
  2334. if ( ( info.m_bNormalToDuDv || ( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_NORMAL_DUDV ) ) &&
  2335. !( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_PREMULT_COLOR_ONEOVERMIP ) )
  2336. {
  2337. VTexMsg( "Implicitly setting premultcolorbyoneovermiplevel since you are generating a dudv map\n" );
  2338. info.m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_PREMULT_COLOR_ONEOVERMIP;
  2339. }
  2340. if ( ( info.m_bNormalToDuDv || ( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_NORMAL_DUDV ) ) )
  2341. {
  2342. VTexMsg( "Implicitly setting trilinear since you are generating a dudv map\n" );
  2343. info.m_nFlags |= TEXTUREFLAGS_TRILINEAR;
  2344. }
  2345. if ( Q_stristr( pFileBaseName, "_normal" ) )
  2346. {
  2347. if( !( info.m_nFlags & TEXTUREFLAGS_NORMAL ) )
  2348. {
  2349. if( !g_Quiet )
  2350. {
  2351. VTexMsgEx( stderr,
  2352. "Implicitly setting:\n"
  2353. "\t\"normal\" \"1\"\n"
  2354. "since filename ends in \"_normal\"\n"
  2355. );
  2356. }
  2357. info.m_nFlags |= TEXTUREFLAGS_NORMAL;
  2358. }
  2359. }
  2360. if ( Q_stristr( pFileBaseName, "ssbump" ) )
  2361. {
  2362. if( !( info.m_nFlags & TEXTUREFLAGS_SSBUMP ) )
  2363. {
  2364. if( !g_Quiet )
  2365. {
  2366. VTexMsgEx( stderr,
  2367. "Implicitly setting:\n"
  2368. "\t\"ssbump\" \"1\"\n"
  2369. "since filename includes \"ssbump\"\n"
  2370. );
  2371. }
  2372. info.m_nFlags |= TEXTUREFLAGS_SSBUMP;
  2373. }
  2374. }
  2375. if ( Q_stristr( pFileBaseName, "_dudv" ) )
  2376. {
  2377. if( !info.m_bNormalToDuDv && !info.m_bDuDv )
  2378. {
  2379. if( !g_Quiet )
  2380. {
  2381. VTexMsgEx( stderr,
  2382. "Implicitly setting:\n"
  2383. "\t\"dudv\" \"1\"\n"
  2384. "since filename ends in \"_dudv\"\n"
  2385. "If you are trying to convert from a normal map to a dudv map, put \"normaltodudv\" \"1\" in description.\n"
  2386. );
  2387. }
  2388. info.m_bDuDv = true;
  2389. }
  2390. }
  2391. // Displacement map
  2392. if ( Q_stristr( pFileBaseName, "_disp" ) )
  2393. {
  2394. if( !info.m_bDisplacementMap )
  2395. {
  2396. info.m_bDisplacementMap = true;
  2397. if ( ( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_FILTER_NICE ) && !g_Quiet )
  2398. {
  2399. VTexMsgEx( stderr, "Implicitly disabling nice filtering\n" );
  2400. }
  2401. info.m_vtfProcOptions.flags0 &= ~VtfProcessingOptions::OPT_FILTER_NICE;
  2402. if ( !( info.m_nFlags & TEXTUREFLAGS_NOMIP ) && !g_Quiet )
  2403. {
  2404. VTexMsgEx( stderr, "Implicitly disabling mip map generation\n" );
  2405. }
  2406. info.m_nFlags &= ~TEXTUREFLAGS_NOMIP;
  2407. }
  2408. }
  2409. // Turn off nice filtering if we are a cube map (takes too long with buildcubemaps) or
  2410. // if we are a normal map (looks like terd.)
  2411. if ( ( info.m_nFlags & TEXTUREFLAGS_NORMAL ) || info.m_bIsCubeMap )
  2412. {
  2413. if ( ( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_FILTER_NICE ) && !g_Quiet )
  2414. {
  2415. VTexMsgEx( stderr, "Implicitly disabling nice filtering\n" );
  2416. }
  2417. info.m_vtfProcOptions.flags0 &= ~VtfProcessingOptions::OPT_FILTER_NICE;
  2418. }
  2419. return true;
  2420. }
  2421. //-----------------------------------------------------------------------------
  2422. // Loads the .psd file or .txt file associated with the .tga and gets out various data
  2423. //-----------------------------------------------------------------------------
  2424. static CDmePrecompiledTexture *LoadConfigFile( const char *pFullPath, BitmapFileType_t *pMode, CRC32_t *pInputHash )
  2425. {
  2426. // Based on the extension, we do different things
  2427. const char *pExt = Q_GetFileExtension( pFullPath );
  2428. if ( !pExt )
  2429. {
  2430. VTexError( "VTex: Bogus file name \"%s\"!\n", pFullPath );
  2431. return NULL;
  2432. }
  2433. const char *pOrigFullPath = pFullPath;
  2434. bool bConfigFileSpecified = ( !Q_stricmp( pExt, "txt" ) || !Q_stricmp( pExt, "tex" ) );
  2435. CDmePrecompiledTexture *pPrecompiledTexture = NULL;
  2436. *pMode = BITMAP_FILE_TYPE_UNKNOWN;
  2437. char pTexFile[MAX_PATH];
  2438. if ( !Q_stricmp( pExt, "tga" ) || !Q_stricmp( pExt, "pfm" ) )
  2439. {
  2440. *pMode = !Q_stricmp( pExt, "tga" ) ? BITMAP_FILE_TYPE_TGA : BITMAP_FILE_TYPE_PFM;
  2441. Q_strncpy( pTexFile, pFullPath, sizeof(pTexFile) );
  2442. Q_SetExtension( pTexFile, "tex", sizeof(pTexFile) );
  2443. if ( FileExistsAbsolute( pTexFile ) )
  2444. {
  2445. // If we ask for a tga or pfm, and a tex file exists, use the tex loader below
  2446. pFullPath = pTexFile;
  2447. pExt = "tex";
  2448. }
  2449. else
  2450. {
  2451. Q_SetExtension( pTexFile, "txt", sizeof(pTexFile) );
  2452. if ( FileExistsAbsolute( pTexFile ) )
  2453. {
  2454. // If we ask for a tga or pfm, and a tex file exists, use the tex loader below
  2455. pFullPath = pTexFile;
  2456. pExt = "txt";
  2457. }
  2458. else
  2459. {
  2460. pPrecompiledTexture = CreateElement< CDmePrecompiledTexture >( "root", DMFILEID_INVALID );
  2461. pPrecompiledTexture->m_ImageFileName = Q_UnqualifiedFileName( pFullPath );
  2462. pPrecompiledTexture->AddProcessor< CDmeTP_ComputeMipmaps >( "computeMipmaps" );
  2463. }
  2464. }
  2465. }
  2466. if ( !Q_stricmp( pExt, "psd" ) )
  2467. {
  2468. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  2469. if ( !LoadConfigFromPSD( pFullPath, buf, false, pInputHash ) )
  2470. return NULL;
  2471. if ( buf.TellMaxPut() == 0 )
  2472. return NULL;
  2473. char pTempPath[MAX_PATH];
  2474. Q_strncpy( pTempPath, pFullPath, sizeof(pTempPath) );
  2475. Q_SetExtension( pTempPath, "__psdtxt", sizeof(pTempPath) );
  2476. DmElementHandle_t hElement;
  2477. const char *pEncoding = g_pDataModel->IsDMXFormat( buf ) ? "keyvalues2" : "tex_source1";
  2478. if ( !g_pDataModel->Unserialize( buf, pEncoding, "tex", NULL, pTempPath, CR_FORCE_COPY, hElement ) )
  2479. return NULL;
  2480. pPrecompiledTexture = GetElement< CDmePrecompiledTexture >( hElement );
  2481. pPrecompiledTexture->m_ImageFileName = Q_UnqualifiedFileName( pFullPath );
  2482. *pMode = BITMAP_FILE_TYPE_PSD;
  2483. }
  2484. bool bIsImportedFile = !Q_stricmp( pExt, "txt" );
  2485. if ( !Q_stricmp( pExt, "tex" ) || bIsImportedFile )
  2486. {
  2487. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  2488. bool bOK = LoadFile( pFullPath, buf, false, pInputHash );
  2489. if ( !bOK )
  2490. return NULL;
  2491. DmElementHandle_t hElement;
  2492. if ( !g_pDataModel->Unserialize( buf, !bIsImportedFile ? "keyvalues2" : "tex_source1", "tex", NULL, pFullPath, CR_FORCE_COPY, hElement ) )
  2493. return NULL;
  2494. pPrecompiledTexture = GetElement< CDmePrecompiledTexture >( hElement );
  2495. char pFileBase[ MAX_PATH ];
  2496. Q_strncpy( pFileBase, Q_UnqualifiedFileName( pOrigFullPath ), sizeof(pFileBase) );
  2497. if ( Q_stricmp( pPrecompiledTexture->m_ImageFileName, "__unspecified_texture" ) )
  2498. {
  2499. if ( !bConfigFileSpecified && Q_stricmp( pPrecompiledTexture->m_ImageFileName, pFileBase ) )
  2500. {
  2501. Warning( ".tex specified a different file name (\"%s\") than the command-line did (\"%s\")!\n",
  2502. pPrecompiledTexture->m_ImageFileName.Get(), pFileBase );
  2503. return NULL;
  2504. }
  2505. }
  2506. else
  2507. {
  2508. if ( bConfigFileSpecified )
  2509. {
  2510. char *pTestExt[3] = { ".tga", ".pfm", ".psd" };
  2511. char pTestFileTest[ MAX_PATH ];
  2512. char pTestFile[ MAX_PATH ];
  2513. Q_strncpy( pTestFile, pFullPath, sizeof(pTestFile) );
  2514. int i;
  2515. for ( i = 0; i < 3; ++i )
  2516. {
  2517. Q_SetExtension( pTestFile, pTestExt[i], sizeof(pTestFile) );
  2518. MakeSrcFileName( pPrecompiledTexture, pTestFile, 0, 0, 0,
  2519. pPrecompiledTexture->m_nVolumeTextureDepth > 1 ? 0 : -1, pTestFileTest, sizeof(pTestFileTest) );
  2520. if ( FileExistsAbsolute( pTestFileTest ) )
  2521. {
  2522. Q_strncpy( pFileBase, Q_UnqualifiedFileName( pTestFile ), sizeof(pFileBase) );
  2523. break;
  2524. }
  2525. }
  2526. if ( i == 3 )
  2527. {
  2528. Warning( "Unable to find image file associated with file \"%s\"!\n", pFullPath );
  2529. return false;
  2530. }
  2531. }
  2532. pPrecompiledTexture->m_ImageFileName = pFileBase;
  2533. }
  2534. const char *pTextureExt = Q_GetFileExtension( pPrecompiledTexture->m_ImageFileName );
  2535. BitmapFileType_t nTextureMode = BITMAP_FILE_TYPE_UNKNOWN;
  2536. if ( !Q_stricmp( pTextureExt, "tga" ) )
  2537. {
  2538. nTextureMode = BITMAP_FILE_TYPE_TGA;
  2539. }
  2540. else if ( !Q_stricmp( pTextureExt, "pfm" ) )
  2541. {
  2542. nTextureMode = BITMAP_FILE_TYPE_PFM;
  2543. }
  2544. else if ( pTextureExt )
  2545. {
  2546. VTexError( "VTex: Bogus texture file name encountered \"%s\"!\n", pPrecompiledTexture->m_ImageFileName.Get() );
  2547. return NULL;
  2548. }
  2549. if ( *pMode != BITMAP_FILE_TYPE_UNKNOWN && *pMode != nTextureMode )
  2550. {
  2551. VTexError( "VTex: Specified to build file \"%s\", but file \"%s\" is specified in the associated .tex file!\n", (char *)pFullPath, pPrecompiledTexture->m_ImageFileName.Get() );
  2552. return NULL;
  2553. }
  2554. }
  2555. /*
  2556. if ( g_eMode == BITMAP_FILE_TYPE_PFM )
  2557. {
  2558. if ( g_bUsedAsLaunchableDLL && !( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_NOCOMPRESS ) )
  2559. {
  2560. info.m_nFlags |= TEXTUREFLAGS_NOMIP;
  2561. }
  2562. if ( g_bUsedAsLaunchableDLL && !HasSuffix( pFileBaseName, "_hdr" ) && !HasSuffix( pFileBaseName, "_disp" ) )
  2563. {
  2564. VTexWarning( "PFM files should be suffixed with '_hdr' or '_disp'\n" );
  2565. }
  2566. }
  2567. else if ( g_bUsedAsLaunchableDLL && HasSuffix( pFileBaseName, "hdr" ) )
  2568. {
  2569. VTexWarning( "Only HDR images (.PFM files) should be suffixed with 'hdr'\n" );
  2570. }
  2571. if ( ( info.m_bNormalToDuDv || ( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_NORMAL_DUDV ) ) &&
  2572. !( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_PREMULT_COLOR_ONEOVERMIP ) )
  2573. {
  2574. VTexMsg( "Implicitly setting premultcolorbyoneovermiplevel since you are generating a dudv map\n" );
  2575. info.m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_PREMULT_COLOR_ONEOVERMIP;
  2576. }
  2577. if ( ( info.m_bNormalToDuDv || ( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_NORMAL_DUDV ) ) )
  2578. {
  2579. VTexMsg( "Implicitly setting trilinear since you are generating a dudv map\n" );
  2580. info.m_nFlags |= TEXTUREFLAGS_TRILINEAR;
  2581. }
  2582. // Turn off nice filtering if we are a cube map (takes too long with buildcubemaps) or
  2583. // if we are a normal map (looks like terd.)
  2584. if ( ( info.m_nFlags & TEXTUREFLAGS_NORMAL ) || info.m_bIsCubeMap )
  2585. {
  2586. if ( ( info.m_vtfProcOptions.flags0 & VtfProcessingOptions::OPT_FILTER_NICE ) && !g_Quiet )
  2587. {
  2588. VTexMsgEx( stderr, "implicitly disabling nice filtering\n" );
  2589. }
  2590. info.m_vtfProcOptions.flags0 &= ~VtfProcessingOptions::OPT_FILTER_NICE;
  2591. }
  2592. */
  2593. char pTextureName[MAX_PATH];
  2594. GenerateResourceName( pFullPath, NULL, pTextureName, sizeof(pTextureName) );
  2595. pPrecompiledTexture->SetName( pTextureName );
  2596. return pPrecompiledTexture;
  2597. }
  2598. void Usage( void )
  2599. {
  2600. VTexError(
  2601. "\n"
  2602. " Usage: vtex [-outdir dir] [-nopause] [-mkdir] [-shader ShaderName] [-vmtparam Param Value] tex1.txt tex2.txt ...\n"
  2603. "\n"
  2604. " -quiet : don't print anything out, don't pause for input\n"
  2605. " -warningsaserrors : treat warnings as errors\n"
  2606. " -nopause : don't pause for input\n"
  2607. " -nomkdir : don't create destination folder if it doesn't exist\n"
  2608. " -shader : make a .vmt for this texture, using this shader (e.g. \"vtex -shader UnlitGeneric blah.tga\")\n"
  2609. " -vmtparam : adds parameter and value to the .vmt file\n"
  2610. " -outdir <dir> : write output to the specified dir regardless of source filename and vproject\n"
  2611. " -deducepath : deduce path of sources by target file names\n"
  2612. " -extractsrc : extract approximate src art out of a vtf\n"
  2613. " -dontbuild : don't build the input files into VTFs (usually used with extractsrc)\n"
  2614. " -quickconvert : use with \"-nop4 -dontusegamedir -quickconvert\" to upgrade old .vmt files\n"
  2615. " -dontusegamedir : output files in same folder as inputs (for use with -extractsrc and -quickconvert)\n"
  2616. " -crcvalidate : validate .vmt against the sources\n"
  2617. " -crcforce : generate a new .vmt even if sources crc matches\n"
  2618. " -nop4 : don't check files out in Perforce\n"
  2619. " -nopsd : skip .psd files (e.g. use this with \"vtex *.*\")\n"
  2620. " -notga : skip .tga files (e.g. use this with \"vtex *.*\")\n"
  2621. " -oldcubepath : old cubemap method, expects 6 input files, suffixed: 'up', 'dn', 'lf', 'rt', 'ft', 'bk'\n"
  2622. "\n"
  2623. "\teg: -vmtparam $ignorez 1 -vmtparam $translucent 1\n"
  2624. "\n"
  2625. " Note that you can use wildcards and that you can also chain them\n"
  2626. " e.g. materialsrc/monster1/*.tga materialsrc/monster2/*.tga\n" );
  2627. }
  2628. bool GetOutputDir( const char *inputName, char *outputDir )
  2629. {
  2630. if ( g_ForcedOutputDir[0] )
  2631. {
  2632. strcpy( outputDir, g_ForcedOutputDir );
  2633. }
  2634. else
  2635. {
  2636. // Is inputName a relative path?
  2637. char buf[MAX_PATH];
  2638. Q_MakeAbsolutePath( buf, sizeof( buf ), inputName, NULL );
  2639. Q_FixSlashes( buf );
  2640. const char *pTmp = Q_stristr( buf, "materialsrc\\" );
  2641. if( !pTmp )
  2642. {
  2643. return false;
  2644. }
  2645. pTmp += strlen( "materialsrc/" );
  2646. strcpy( outputDir, gamedir );
  2647. strcat( outputDir, "materials/" );
  2648. strcat( outputDir, pTmp );
  2649. Q_StripFilename( outputDir );
  2650. }
  2651. if( !g_Quiet )
  2652. {
  2653. VTexMsg( "Output directory: %s\n", outputDir );
  2654. }
  2655. return true;
  2656. }
  2657. bool IsCubeFromFileNames( const char *inputBaseName )
  2658. {
  2659. char fileName[MAX_PATH];
  2660. // Do Strcmp for ".hdr" to make sure we aren't ripping too much stuff off.
  2661. Q_StripExtension( inputBaseName, fileName, MAX_PATH );
  2662. const char *pInputExtension = inputBaseName + Q_strlen( fileName );
  2663. Q_strncat( fileName, "rt", MAX_PATH, COPY_ALL_CHARACTERS );
  2664. Q_strncat( fileName, pInputExtension, MAX_PATH, COPY_ALL_CHARACTERS );
  2665. Q_strncat( fileName, GetSourceExtension(), MAX_PATH, COPY_ALL_CHARACTERS );
  2666. struct _stat buf;
  2667. if( _stat( fileName, &buf ) != -1 )
  2668. {
  2669. return true;
  2670. }
  2671. else
  2672. {
  2673. return false;
  2674. }
  2675. }
  2676. #ifdef PLATFORM_WINDOWS
  2677. int Find_Files( WIN32_FIND_DATA &wfd, HANDLE &hResult, const char *basedir, const char *extension )
  2678. {
  2679. char filename[MAX_PATH] = {0};
  2680. BOOL bMoreFiles = TRUE;
  2681. if ( hResult && ( INVALID_HANDLE_VALUE != hResult ) )
  2682. {
  2683. bMoreFiles = FindNextFile( hResult, &wfd);
  2684. }
  2685. else
  2686. {
  2687. memset(&wfd, 0, sizeof(WIN32_FIND_DATA));
  2688. char search[260] = {0};
  2689. sprintf( search, "%s\\*.*", basedir );
  2690. hResult = FindFirstFile( search, &wfd );
  2691. if ( INVALID_HANDLE_VALUE == hResult )
  2692. return 0;
  2693. }
  2694. if ( bMoreFiles )
  2695. {
  2696. // Skip . and ..
  2697. if ( wfd.cFileName[0] == '.' )
  2698. {
  2699. return FF_TRYAGAIN;
  2700. }
  2701. // If it's a subdirectory, just recurse down it
  2702. if ( (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) )
  2703. {
  2704. char subdir[MAX_PATH];
  2705. sprintf( subdir, "%s\\%s", basedir, wfd.cFileName );
  2706. // Recurse
  2707. // -- Find_Files( wfd, hResult, basedir, extension );
  2708. return FF_TRYAGAIN;
  2709. }
  2710. // Check that it's a tga
  2711. //
  2712. char fname[_MAX_FNAME] = {0};
  2713. char ext[_MAX_EXT] = {0};
  2714. _splitpath( wfd.cFileName, NULL, NULL, fname, ext );
  2715. // Not the type we want.
  2716. if ( stricmp( ext, extension ) )
  2717. return FF_DONTPROCESS;
  2718. // Check for .vmt
  2719. sprintf( filename, "%s\\%s.vmt", basedir, fname );
  2720. // Exists, so don't overwrite it
  2721. if ( FileExistsAbsolute( filename ) )
  2722. return FF_PROCESS;
  2723. char texturename[ _MAX_PATH ] = {0};
  2724. char *p = ( char * )basedir;
  2725. // Skip over the base path to get a material system relative path
  2726. // p += strlen( wfd.cFileName ) + 1;
  2727. // Construct texture name
  2728. sprintf( texturename, "%s\\%s", p, fname );
  2729. // Convert all to lower case
  2730. strlwr( texturename );
  2731. strlwr( filename );
  2732. return FF_PROCESS;
  2733. }
  2734. else
  2735. {
  2736. FindClose( hResult );
  2737. hResult = INVALID_HANDLE_VALUE;
  2738. return 0;
  2739. }
  2740. }
  2741. #endif // PLATFORM_WINDOWS
  2742. char const *GetBareFileName( char const *pszPathName )
  2743. {
  2744. const char *pBaseName = &pszPathName[strlen( pszPathName ) - 1];
  2745. while( (pBaseName >= pszPathName) && *pBaseName != '\\' && *pBaseName != '/' )
  2746. {
  2747. pBaseName--;
  2748. }
  2749. pBaseName++;
  2750. return pBaseName;
  2751. }
  2752. bool Process_File_Internal( char *pInputBaseName, int maxlen, bool bOutputPwlColorConversion, bool bConvertTo360PwlSrgb );
  2753. bool Process_File( char *pInputBaseName, int maxlen )
  2754. {
  2755. const char *pExtension = V_GetFileExtension( pInputBaseName );
  2756. if ( pExtension != NULL )
  2757. {
  2758. if ( V_stricmp( pExtension, "pfm" ) == 0 )
  2759. {
  2760. g_bNoTga = true;
  2761. g_bNoPsd = true;
  2762. g_bUsePfm = true;
  2763. }
  2764. }
  2765. // Build standard .vtf
  2766. bool ret = Process_File_Internal( pInputBaseName, maxlen, false, false );
  2767. if ( ret == false )
  2768. return ret;
  2769. // Build XBox 360 srgb .pwl.vtf
  2770. if ( g_bSupportsXBox360 == true )
  2771. {
  2772. ret = Process_File_Internal( pInputBaseName, maxlen, true, true );
  2773. }
  2774. return ret;
  2775. }
  2776. bool Process_File_Internal( char *pInputBaseName, int maxlen, bool bOutputPwlColorConversion, bool bConvertTo360PwlSrgb )
  2777. {
  2778. Q_FixSlashes( pInputBaseName, '/' );
  2779. char requestedInputBaseName[1024];
  2780. Q_strncpy( requestedInputBaseName, pInputBaseName, maxlen );
  2781. char outputDir[1024];
  2782. Q_StripExtension( pInputBaseName, pInputBaseName, maxlen );
  2783. if ( CommandLine()->FindParm( "-deducepath" ) )
  2784. {
  2785. strcpy( outputDir, pInputBaseName );
  2786. // If it is not a full path, try making it a full path
  2787. if ( pInputBaseName[0] != '/' &&
  2788. pInputBaseName[1] != ':' )
  2789. {
  2790. // Convert to full path
  2791. getcwd( outputDir, sizeof( outputDir ) );
  2792. Q_FixSlashes( outputDir, '/' );
  2793. Q_strncat( outputDir, "/", sizeof( outputDir ) );
  2794. Q_strncat( outputDir, pInputBaseName, sizeof( outputDir ) );
  2795. }
  2796. // If it is pointing inside "/materials/" make it go for "/materialsrc/"
  2797. char *pGame = strstr( outputDir, "/game/" );
  2798. char *pMaterials = strstr( outputDir, "/materials/" );
  2799. if ( pGame && pMaterials && ( pGame < pMaterials ) )
  2800. {
  2801. // "u:/data/game/tf/materials/" -> "u:/data/content/tf/materialsrc/"
  2802. int numExtraBytes = strlen( "/content/.../materialsrc/" ) - strlen( "/game/.../materials/" );
  2803. int numConvertBytes = pMaterials + strlen( "/materials/" ) - outputDir;
  2804. memmove( outputDir + numConvertBytes + numExtraBytes, outputDir + numConvertBytes, strlen( outputDir ) - numConvertBytes + 1 );
  2805. int numMidBytes = pMaterials - pGame - strlen( "/game" );
  2806. memmove( pGame + strlen( "/content" ), pGame + strlen( "/game" ), numMidBytes );
  2807. memmove( pGame, "/content", strlen( "/content" ) );
  2808. memmove( pGame + strlen( "/content" ) + numMidBytes, "/materialsrc/", strlen( "/materialsrc/" ) );
  2809. }
  2810. Q_strncpy( pInputBaseName, outputDir, maxlen );
  2811. }
  2812. if( !g_Quiet )
  2813. {
  2814. VTexMsg( "\nInput file: %s\n", pInputBaseName );
  2815. }
  2816. if( g_UseGameDir )
  2817. {
  2818. if ( !GetOutputDir( pInputBaseName, outputDir ) )
  2819. {
  2820. VTexError( "Problem figuring out outputdir for %s\n", pInputBaseName );
  2821. return FALSE;
  2822. }
  2823. sprintf( requestedInputBaseName, "%s/%s.vtf", outputDir, GetBareFileName( pInputBaseName ) );
  2824. }
  2825. else // if (!g_UseGameDir)
  2826. {
  2827. strcpy( outputDir, pInputBaseName );
  2828. sprintf( requestedInputBaseName, "%s.vtf", outputDir );
  2829. Q_StripFilename(outputDir);
  2830. }
  2831. // Usage:
  2832. // vtex -nop4 -dontusegamedir -quickconvert u:\data\game\tf\texture.vtf
  2833. // Will read the old texture format and write the new texture format
  2834. //
  2835. if ( CommandLine()->FindParm( "-quickconvert" ) )
  2836. {
  2837. VTexMsg( "Quick convert of '%s'...\n", pInputBaseName );
  2838. char chFileNameConvert[ 512 ];
  2839. sprintf( chFileNameConvert, "%s.vtf", pInputBaseName );
  2840. IVTFTexture *pVtf = CreateVTFTexture();
  2841. CUtlBuffer bufFile;
  2842. LoadFile( chFileNameConvert, bufFile, true, NULL );
  2843. bool bRes = pVtf->Unserialize( bufFile );
  2844. if ( !bRes )
  2845. VTexError( "Failed to read '%s'!\n", chFileNameConvert );
  2846. // Determine the CRC if it was there
  2847. // CRC32_t uiDataHash = 0;
  2848. // CRC32_t *puiDataHash = &uiDataHash;
  2849. // Assert( sizeof( uiDataHash ) == sizeof( int ) );
  2850. // if ( !pVtf->GetResourceData( VTexConfigInfo_t::VTF_INPUTSRC_CRC, ... ) )
  2851. AttachShtFile( pInputBaseName, pVtf, NULL );
  2852. // Update the CRC
  2853. // if ( puiDataHash )
  2854. // {
  2855. // pVtf->InitResourceDataSection( VTexConfigInfo_t::VTF_INPUTSRC_CRC, *puiDataHash );
  2856. // }
  2857. // Remove the CRC when quick-converting
  2858. pVtf->SetResourceData( VTexConfigInfo_t::VTF_INPUTSRC_CRC, NULL, 0 );
  2859. bufFile.Clear();
  2860. bRes = pVtf->Serialize( bufFile );
  2861. if ( !bRes )
  2862. VTexError( "Failed to write '%s'!\n", chFileNameConvert );
  2863. DestroyVTFTexture( pVtf );
  2864. if ( FILE *fw = fopen( chFileNameConvert, "wb" ) )
  2865. {
  2866. fwrite( bufFile.Base(), 1, bufFile.TellPut(), fw );
  2867. fclose( fw );
  2868. }
  2869. else
  2870. VTexError( "Failed to open '%s' for writing!\n", chFileNameConvert );
  2871. VTexMsg( "... succeeded.\n" );
  2872. return TRUE;
  2873. }
  2874. VTexConfigInfo_t info;
  2875. bool bConfigLoaded = LoadConfigFile( pInputBaseName, info );
  2876. // Usage:
  2877. // vtex -nop4 -dontusegamedir -deducepath -extractsrc u:\data\game\tf\texture.vtf
  2878. // Will read the vtf texture and write the texture src files
  2879. //
  2880. if ( CommandLine()->FindParm( "-extractsrc" ) && !bConvertTo360PwlSrgb ) //&& !bConfigLoaded )
  2881. {
  2882. VTexMsg( "Extracting from vtf '%s'.\n", requestedInputBaseName );
  2883. VTexMsg( "Saving extracted src as '%s'.\n", pInputBaseName );
  2884. // Create directory
  2885. char szCreateDirectoryCommand[2048];
  2886. V_snprintf( szCreateDirectoryCommand, 1024, "if not exist %s mkdir %s", pInputBaseName, pInputBaseName );
  2887. V_StripFilename( szCreateDirectoryCommand );
  2888. V_FixSlashes( szCreateDirectoryCommand, '\\' );
  2889. //Msg( "*** system: %s\n", szCreateDirectoryCommand );
  2890. system( szCreateDirectoryCommand );
  2891. // Create the texture and unserialize file data
  2892. SmartIVTFTexture pVtf( CreateVTFTexture() );
  2893. CUtlBuffer bufFile;
  2894. LoadFile( requestedInputBaseName, bufFile, true, NULL );
  2895. bool bRes = pVtf->Unserialize( bufFile );
  2896. if ( !bRes )
  2897. {
  2898. VTexError( "Failed to read '%s'!\n", requestedInputBaseName );
  2899. return FALSE;
  2900. }
  2901. Msg( "vtf width: %d\n", pVtf->Width() );
  2902. Msg( "vtf height: %d\n", pVtf->Height() );
  2903. Msg( "vtf numFrames: %d\n", pVtf->FrameCount() );
  2904. Msg( "vtf numFaces: %d\n", pVtf->FaceCount() );
  2905. Msg( "vtf cubemap: %s\n", pVtf->IsCubeMap() ? "true" : "false" );
  2906. Vector vecReflectivity = pVtf->Reflectivity();
  2907. Msg( "vtf reflectivity: %f %f %f\n", vecReflectivity[0], vecReflectivity[1], vecReflectivity[2] );
  2908. ImageFormat srcFormat = pVtf->Format();
  2909. char const *szFormatName = ImageLoader::GetName( srcFormat );
  2910. Msg( "vtf format: %s\n", szFormatName );
  2911. if ( pVtf->FrameCount() > 1 )
  2912. {
  2913. VTexError( "Vtf source extraction is not implemented for multiple frames!\n" );
  2914. return FALSE;
  2915. }
  2916. if ( pVtf->FaceCount() > 1 || pVtf->IsCubeMap() )
  2917. {
  2918. VTexError( "Vtf source extraction is not implemented for cubemaps!\n" );
  2919. return FALSE;
  2920. }
  2921. VTexMsg( "Extracting image data '%s.tga'...\n", pInputBaseName );
  2922. {
  2923. char chVtf2TgaCommand[2048];
  2924. Q_snprintf( chVtf2TgaCommand, sizeof( chVtf2TgaCommand ) - 1,
  2925. "vtf2tga.exe -i %s -o %s.tga",
  2926. requestedInputBaseName, pInputBaseName );
  2927. int iSysCall = system( chVtf2TgaCommand );
  2928. if ( iSysCall )
  2929. {
  2930. VTexError( "Failed to extract image data!\n" );
  2931. return FALSE;
  2932. }
  2933. char chTgaFile[ MAX_PATH ];
  2934. sprintf( chTgaFile, "%s.tga", pInputBaseName );
  2935. CP4AutoAddFile autop4( chTgaFile );
  2936. }
  2937. // Now create the accompanying text file with texture settings
  2938. char chTxtFileName[1024];
  2939. Q_snprintf( chTxtFileName, sizeof( chTxtFileName ) - 1,
  2940. "%s.txt", pInputBaseName );
  2941. VTexMsg( "Saving text data '%s.txt'...\n", pInputBaseName );
  2942. for ( int i = 0; i < 2; i++ ) // First see if we need to write a txt file, then write it
  2943. {
  2944. FILE *fTxtFile = NULL;
  2945. if ( i == 1 )
  2946. {
  2947. // Try to open for writing without p4 edit
  2948. fTxtFile = fopen( chTxtFileName, "wt" );
  2949. if ( !fTxtFile )
  2950. {
  2951. // p4 edit, then try to open for writing
  2952. CP4AutoEditFile autop4( chTxtFileName );
  2953. fTxtFile = fopen( chTxtFileName, "wt" );
  2954. if ( !fTxtFile )
  2955. {
  2956. // Can't open file for writing
  2957. VTexError( "Failed to create '%s'!\n", chTxtFileName );
  2958. return FALSE;
  2959. }
  2960. }
  2961. }
  2962. if ( strstr( szFormatName, "BGR" ) || strstr( szFormatName, "RGB" ) )
  2963. {
  2964. if ( i == 0 )
  2965. {
  2966. continue;
  2967. }
  2968. else
  2969. {
  2970. fprintf( fTxtFile, "nocompress 1\n" );
  2971. }
  2972. }
  2973. if ( pVtf->Flags() & TEXTUREFLAGS_NOMIP )
  2974. {
  2975. if ( i == 0 )
  2976. {
  2977. continue;
  2978. }
  2979. else
  2980. {
  2981. fprintf( fTxtFile, "nomip 1\n" );
  2982. }
  2983. }
  2984. if ( pVtf->Flags() & TEXTUREFLAGS_NOLOD )
  2985. {
  2986. if ( i == 0 )
  2987. {
  2988. continue;
  2989. }
  2990. else
  2991. {
  2992. fprintf( fTxtFile, "nolod 1\n" );
  2993. }
  2994. }
  2995. if ( pVtf->Flags() & TEXTUREFLAGS_CLAMPS )
  2996. {
  2997. if ( i == 0 )
  2998. {
  2999. continue;
  3000. }
  3001. else
  3002. {
  3003. fprintf( fTxtFile, "clamps 1\n" );
  3004. }
  3005. }
  3006. if ( pVtf->Flags() & TEXTUREFLAGS_CLAMPT )
  3007. {
  3008. if ( i == 0 )
  3009. {
  3010. continue;
  3011. }
  3012. else
  3013. {
  3014. fprintf( fTxtFile, "clampt 1\n" );
  3015. }
  3016. }
  3017. if ( pVtf->Flags() & TEXTUREFLAGS_CLAMPU )
  3018. {
  3019. if ( i == 0 )
  3020. {
  3021. continue;
  3022. }
  3023. else
  3024. {
  3025. fprintf( fTxtFile, "clampu 1\n" );
  3026. }
  3027. }
  3028. if ( pVtf->Flags() & TEXTUREFLAGS_PROCEDURAL )
  3029. {
  3030. if ( i == 0 )
  3031. {
  3032. continue;
  3033. }
  3034. else
  3035. {
  3036. fprintf( fTxtFile, "procedural 1\n" );
  3037. }
  3038. }
  3039. if ( pVtf->Flags() & TEXTUREFLAGS_TRILINEAR )
  3040. {
  3041. if ( i == 0 )
  3042. {
  3043. continue;
  3044. }
  3045. else
  3046. {
  3047. fprintf( fTxtFile, "trilinear 1\n" );
  3048. }
  3049. }
  3050. if ( pVtf->Flags() & TEXTUREFLAGS_POINTSAMPLE )
  3051. {
  3052. if ( i == 0 )
  3053. {
  3054. continue;
  3055. }
  3056. else
  3057. {
  3058. fprintf( fTxtFile, "pointsample 1\n" );
  3059. }
  3060. }
  3061. if ( pVtf->Flags() & TEXTUREFLAGS_ANISOTROPIC )
  3062. {
  3063. if ( i == 0 )
  3064. {
  3065. continue;
  3066. }
  3067. else
  3068. {
  3069. fprintf( fTxtFile, "anisotropic 1\n" );
  3070. }
  3071. }
  3072. if ( pVtf->Flags() & TEXTUREFLAGS_HINT_DXT5 )
  3073. {
  3074. if ( i == 0 )
  3075. {
  3076. continue;
  3077. }
  3078. else
  3079. {
  3080. fprintf( fTxtFile, "dxt5 1\n" );
  3081. }
  3082. }
  3083. if ( pVtf->Flags() & TEXTUREFLAGS_NORMAL )
  3084. {
  3085. if ( i == 0 )
  3086. {
  3087. continue;
  3088. }
  3089. else
  3090. {
  3091. fprintf( fTxtFile, "normal 1\n" );
  3092. }
  3093. }
  3094. if ( pVtf->Flags() & TEXTUREFLAGS_ALL_MIPS )
  3095. {
  3096. if ( i == 0 )
  3097. {
  3098. continue;
  3099. }
  3100. else
  3101. {
  3102. fprintf( fTxtFile, "allmips 1\n" );
  3103. }
  3104. }
  3105. if ( pVtf->Flags() & TEXTUREFLAGS_MOST_MIPS )
  3106. {
  3107. if ( i == 0 )
  3108. {
  3109. continue;
  3110. }
  3111. else
  3112. {
  3113. fprintf( fTxtFile, "mostmips 1\n" );
  3114. }
  3115. }
  3116. if ( i == 1 )
  3117. {
  3118. fclose( fTxtFile );
  3119. CP4AutoAddFile autop4( chTxtFileName );
  3120. }
  3121. // Didn't find anything to write, so quit
  3122. if ( i == 0 )
  3123. {
  3124. break;
  3125. }
  3126. }
  3127. VTexMsg( "'%s' extracted.\n", pInputBaseName );
  3128. }
  3129. if ( CommandLine()->FindParm( "-dontbuild" ) )
  3130. return TRUE;
  3131. if ( !bConfigLoaded )
  3132. {
  3133. bConfigLoaded = LoadConfigFile( pInputBaseName, info );
  3134. }
  3135. if ( !bConfigLoaded )
  3136. return FALSE;
  3137. if ( !g_bOldCubemapPath && !g_bUsePfm )
  3138. {
  3139. // Error out if we find old-style cubemap filenames
  3140. if ( IsCubeFromFileNames( pInputBaseName ) )
  3141. {
  3142. VTexError( "File is old-style cubemap. Convert to new-style cubemap (single 'T-shape' image) or use old path with '-oldcubepath'" );
  3143. }
  3144. }
  3145. else if ( !info.m_bIsCubeMap )
  3146. {
  3147. // Look for old-style cubemap filenames
  3148. info.m_bIsCubeMap = IsCubeFromFileNames( pInputBaseName );
  3149. }
  3150. if ( bConvertTo360PwlSrgb )
  3151. {
  3152. SetFlagValue( info.m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_SRGB_PC_TO_360, 1 );
  3153. }
  3154. if( ( info.m_nStartFrame == -1 && info.m_nEndFrame != -1 ) ||
  3155. ( info.m_nStartFrame != -1 && info.m_nEndFrame == -1 ) )
  3156. {
  3157. VTexError( "%s: If you use startframe, you must use endframe, and vice versa.\n", pInputBaseName );
  3158. return FALSE;
  3159. }
  3160. const char *pBaseName = GetBareFileName( pInputBaseName );
  3161. bool bProcessedFilesOK = ProcessFiles( pInputBaseName, outputDir, pBaseName, info );
  3162. if ( !bProcessedFilesOK )
  3163. return FALSE;
  3164. // create vmts if necessary
  3165. if( g_ShaderName )
  3166. {
  3167. char buf[1024];
  3168. sprintf( buf, "%s/%s.vmt", outputDir, pBaseName );
  3169. const char *tmp = Q_stristr( outputDir, "materials" );
  3170. FILE *fp;
  3171. if( tmp )
  3172. {
  3173. // check if the file already exists.
  3174. fp = fopen( buf, "r" );
  3175. if( fp )
  3176. {
  3177. if ( !g_Quiet )
  3178. VTexMsgEx( stderr, "vmt file \"%s\" already exists\n", buf );
  3179. fclose( fp );
  3180. }
  3181. else
  3182. {
  3183. fp = fopen( buf, "w" );
  3184. if( fp )
  3185. {
  3186. if ( !g_Quiet )
  3187. VTexMsgEx( stderr, "Creating vmt file: %s/%s\n", tmp, pBaseName );
  3188. tmp += strlen( "materials/" );
  3189. fprintf( fp, "\"%s\"\n", g_ShaderName );
  3190. fprintf( fp, "{\n" );
  3191. fprintf( fp, "\t\"$baseTexture\" \"%s/%s\"\n", tmp, pBaseName );
  3192. int i;
  3193. for( i=0;i<g_NumVMTParams;i++ )
  3194. {
  3195. fprintf( fp, "\t\"%s\" \"%s\"\n", g_VMTParams[i].m_szParam, g_VMTParams[i].m_szValue );
  3196. }
  3197. fprintf( fp, "}\n" );
  3198. fclose( fp );
  3199. CP4AutoAddFile autop4( buf );
  3200. }
  3201. else
  3202. {
  3203. VTexWarning( "Couldn't open \"%s\" for writing\n", buf );
  3204. }
  3205. }
  3206. }
  3207. else
  3208. {
  3209. VTexWarning( "Couldn't find \"materials/\" in output path\n", buf );
  3210. }
  3211. }
  3212. return TRUE;
  3213. }
  3214. class CVTexLoggingListener : public ILoggingListener
  3215. {
  3216. public:
  3217. virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage )
  3218. {
  3219. VTexMsg( "%s", pMessage );
  3220. if ( pContext->m_Severity == LS_ERROR )
  3221. {
  3222. Pause();
  3223. }
  3224. }
  3225. };
  3226. CVTexLoggingListener g_VTexLoggingListener;
  3227. class CVTex : public IVTex, public ILaunchableDLL
  3228. {
  3229. public:
  3230. int VTex( int argc, char **argv );
  3231. // ILaunchableDLL, used by vtex.exe.
  3232. virtual int main( int argc, char **argv )
  3233. {
  3234. g_bUsedAsLaunchableDLL = true;
  3235. // Run the vtex logic
  3236. int iResult = VTex( argc, argv );
  3237. return iResult;
  3238. }
  3239. virtual int VTex( CreateInterfaceFn fsFactory, const char *pGameDir, int argc, char **argv )
  3240. {
  3241. g_pFileSystem = g_pFullFileSystem = (IFileSystem*)fsFactory( FILESYSTEM_INTERFACE_VERSION, NULL );
  3242. if ( !g_pFileSystem )
  3243. {
  3244. Error( "IVTex3::VTex - fsFactory can't get '%s' interface.", FILESYSTEM_INTERFACE_VERSION );
  3245. return 0;
  3246. }
  3247. Q_strncpy( gamedir, pGameDir, sizeof( gamedir ) );
  3248. Q_AppendSlash( gamedir, sizeof( gamedir ) );
  3249. return VTex( argc, argv );
  3250. }
  3251. };
  3252. static class CSuggestGameDirHelper
  3253. {
  3254. public:
  3255. static bool SuggestFn( CFSSteamSetupInfo const *pFsSteamSetupInfo, char *pchPathBuffer, int nBufferLength, bool *pbBubbleDirectories );
  3256. bool MySuggestFn( CFSSteamSetupInfo const *pFsSteamSetupInfo, char *pchPathBuffer, int nBufferLength, bool *pbBubbleDirectories );
  3257. public:
  3258. CSuggestGameDirHelper() : m_pszInputFiles( NULL ), m_numInputFiles( 0 ) {}
  3259. public:
  3260. char const * const *m_pszInputFiles;
  3261. size_t m_numInputFiles;
  3262. } g_suggestGameDirHelper;
  3263. bool CSuggestGameDirHelper::SuggestFn( CFSSteamSetupInfo const *pFsSteamSetupInfo, char *pchPathBuffer, int nBufferLength, bool *pbBubbleDirectories )
  3264. {
  3265. return g_suggestGameDirHelper.MySuggestFn( pFsSteamSetupInfo, pchPathBuffer, nBufferLength, pbBubbleDirectories );
  3266. }
  3267. bool CSuggestGameDirHelper::MySuggestFn( CFSSteamSetupInfo const *pFsSteamSetupInfo, char *pchPathBuffer, int nBufferLength, bool *pbBubbleDirectories )
  3268. {
  3269. if ( !m_numInputFiles || !m_pszInputFiles )
  3270. return false;
  3271. if ( pbBubbleDirectories )
  3272. *pbBubbleDirectories = true;
  3273. for ( int k = 0; k < m_numInputFiles; ++ k )
  3274. {
  3275. Q_MakeAbsolutePath( pchPathBuffer, nBufferLength, m_pszInputFiles[ k ] );
  3276. return true;
  3277. }
  3278. return false;
  3279. }
  3280. int CVTex::VTex( int argc, char **argv )
  3281. {
  3282. CommandLine()->CreateCmdLine( argc, argv );
  3283. if ( g_bUsedAsLaunchableDLL )
  3284. {
  3285. LoggingSystem_PushLoggingState();
  3286. LoggingSystem_RegisterLoggingListener( &g_VTexLoggingListener );
  3287. }
  3288. MathLib_Init( 2.2f, 2.2f, 0.0f, 1.0f, false, false, false, false );
  3289. if( argc < 2 )
  3290. {
  3291. Usage();
  3292. }
  3293. g_UseGameDir = true; // make sure this is initialized to true.
  3294. int i;
  3295. i = 1;
  3296. while( i < argc )
  3297. {
  3298. if( stricmp( argv[i], "-quiet" ) == 0 )
  3299. {
  3300. i++;
  3301. g_Quiet = true;
  3302. g_NoPause = true; // no point in pausing if we aren't going to print anything out.
  3303. }
  3304. else if( stricmp( argv[i], "-nopause" ) == 0 )
  3305. {
  3306. i++;
  3307. g_NoPause = true;
  3308. }
  3309. else if ( stricmp( argv[i], "-WarningsAsErrors" ) == 0 )
  3310. {
  3311. i++;
  3312. g_bWarningsAsErrors = true;
  3313. }
  3314. else if ( stricmp( argv[i], "-UseStandardError" ) == 0 )
  3315. {
  3316. i++;
  3317. g_bUseStandardError = true;
  3318. }
  3319. else if ( stricmp( argv[i], "-nopsd" ) == 0 )
  3320. {
  3321. i++;
  3322. g_bNoPsd = true;
  3323. }
  3324. else if ( stricmp( argv[i], "-notga" ) == 0 )
  3325. {
  3326. i++;
  3327. g_bNoTga = true;
  3328. }
  3329. else if ( stricmp( argv[i], "-nomkdir" ) == 0 )
  3330. {
  3331. i++;
  3332. g_CreateDir = false;
  3333. }
  3334. else if ( stricmp( argv[i], "-mkdir" ) == 0 )
  3335. {
  3336. i++;
  3337. g_CreateDir = true;
  3338. }
  3339. else if ( stricmp( argv[i], "-game" ) == 0 )
  3340. {
  3341. i += 2;
  3342. }
  3343. else if ( stricmp( argv[i], "-outdir" ) == 0 )
  3344. {
  3345. strcpy( g_ForcedOutputDir, argv[i+1] );
  3346. i += 2;
  3347. }
  3348. else if ( stricmp( argv[i], "-dontusegamedir" ) == 0)
  3349. {
  3350. ++i;
  3351. g_UseGameDir = false;
  3352. }
  3353. else if( stricmp( argv[i], "-shader" ) == 0 )
  3354. {
  3355. i++;
  3356. if( i < argc )
  3357. {
  3358. g_ShaderName = argv[i];
  3359. i++;
  3360. }
  3361. }
  3362. else if( stricmp( argv[i], "-vproject" ) == 0 )
  3363. {
  3364. // skip this one. . we dont' use it internally.
  3365. i += 2;
  3366. }
  3367. else if( stricmp( argv[i], "-vmtparam" ) == 0 )
  3368. {
  3369. if( g_NumVMTParams < MAX_VMT_PARAMS )
  3370. {
  3371. i++;
  3372. if( i < argc - 1 )
  3373. {
  3374. g_VMTParams[g_NumVMTParams].m_szParam = argv[i];
  3375. i++;
  3376. if( i < argc - 1 )
  3377. {
  3378. g_VMTParams[g_NumVMTParams].m_szValue = argv[i];
  3379. i++;
  3380. }
  3381. else
  3382. {
  3383. g_VMTParams[g_NumVMTParams].m_szValue = "";
  3384. }
  3385. if( !g_Quiet )
  3386. {
  3387. VTexMsgEx( stderr, "Adding .vmt parameter: \"%s\"\t\"%s\"\n",
  3388. g_VMTParams[g_NumVMTParams].m_szParam,
  3389. g_VMTParams[g_NumVMTParams].m_szValue );
  3390. }
  3391. g_NumVMTParams++;
  3392. }
  3393. }
  3394. else
  3395. {
  3396. VTexMsgEx( stderr, "Exceeded max number of vmt parameters, extra ignored ( max %d )\n", MAX_VMT_PARAMS );
  3397. }
  3398. }
  3399. else if ( stricmp( argv[i], "-oldcubepath" ) == 0 )
  3400. {
  3401. // Revert to the old cube/skybox authoring path, which expects 6 suffixed input files (up, dn, lf, rt, ft, bk)
  3402. VTexMsg( "Using old cubemap method. Expecting 6 input files, suffixed: 'up', 'dn', 'lf', 'rt', 'ft', 'bk'\n" );
  3403. g_bOldCubemapPath = true;
  3404. i++;
  3405. }
  3406. else if( argv[i][0] == '-' )
  3407. {
  3408. // Just assuming that these are valid flags with no args
  3409. ++ i;
  3410. }
  3411. else
  3412. {
  3413. break;
  3414. }
  3415. }
  3416. // Set the suggest game info directory helper
  3417. g_suggestGameDirHelper.m_pszInputFiles = argv + i;
  3418. g_suggestGameDirHelper.m_numInputFiles = argc - i;
  3419. SetSuggestGameInfoDirFn( CSuggestGameDirHelper::SuggestFn );
  3420. // g_pFileSystem may have been inherited with -inherit_filesystem.
  3421. if (g_UseGameDir && !g_pFileSystem)
  3422. {
  3423. FileSystem_Init( argv[i] );
  3424. Q_FixSlashes( gamedir, '/' );
  3425. }
  3426. // Check if we need to build 360 .pwl.vtf versions
  3427. KeyValues *pKeyValues = new KeyValues( "gameinfo.txt" );
  3428. if ( pKeyValues != NULL )
  3429. {
  3430. if ( g_pFileSystem && pKeyValues->LoadFromFile( g_pFileSystem, "gameinfo.txt" ) )
  3431. {
  3432. g_bSupportsXBox360 = pKeyValues->GetBool( "SupportsXBox360" );
  3433. }
  3434. pKeyValues->deleteThis();
  3435. }
  3436. // Initialize P4
  3437. bool bP4DLLExists = false;
  3438. if ( g_pFullFileSystem )
  3439. {
  3440. bP4DLLExists = g_pFullFileSystem->FileExists( "p4lib.dll", "EXECUTABLE_PATH" );
  3441. }
  3442. if ( g_bUsedAsLaunchableDLL && !CommandLine()->FindParm( "-nop4" ) && bP4DLLExists )
  3443. {
  3444. const char *pModuleName = "p4lib.dll";
  3445. CSysModule *pModule = Sys_LoadModule( pModuleName );
  3446. if ( !pModule )
  3447. {
  3448. VTexMsg( "Can't load %s.\n", pModuleName );
  3449. return -1;
  3450. }
  3451. CreateInterfaceFn fn = Sys_GetFactory( pModule );
  3452. if ( !fn )
  3453. {
  3454. VTexMsg( "Can't get factory from %s.\n", pModuleName );
  3455. Sys_UnloadModule( pModule );
  3456. return -1;
  3457. }
  3458. p4 = (IP4 *)fn( P4_INTERFACE_VERSION, NULL );
  3459. if ( !p4 )
  3460. {
  3461. VTexMsg( "Can't get IP4 interface from %s, proceeding with -nop4.\n", pModuleName );
  3462. g_p4factory->SetDummyMode( true );
  3463. }
  3464. else
  3465. {
  3466. p4->Connect( FileSystem_GetFactory() );
  3467. p4->Init();
  3468. }
  3469. }
  3470. else
  3471. {
  3472. g_p4factory->SetDummyMode( true );
  3473. }
  3474. //
  3475. // Setup p4 factory
  3476. //
  3477. {
  3478. // Set the named changelist
  3479. g_p4factory->SetOpenFileChangeList( "VTex Auto Checkout" );
  3480. }
  3481. // Parse args
  3482. for( ; i < argc; i++ )
  3483. {
  3484. if ( argv[i][0] == '-' )
  3485. continue; // Assuming flags
  3486. char pInputBaseName[MAX_PATH];
  3487. Q_strncpy( pInputBaseName, argv[i], sizeof(pInputBaseName) );
  3488. // int maxlen = Q_strlen( pInputBaseName ) + 1;
  3489. if ( !Q_strstr( pInputBaseName, "*." ) )
  3490. {
  3491. Process_File( pInputBaseName, sizeof(pInputBaseName) );
  3492. continue;
  3493. }
  3494. #ifdef PLATFORM_WINDOWS
  3495. char basedir[MAX_PATH];
  3496. char ext[_MAX_EXT];
  3497. char filename[_MAX_FNAME];
  3498. _splitpath( pInputBaseName, NULL, NULL, NULL, ext ); //find extension wanted
  3499. if ( !Q_ExtractFilePath ( pInputBaseName, basedir, sizeof( basedir ) ) )
  3500. strcpy( basedir, ".\\" );
  3501. WIN32_FIND_DATA wfd;
  3502. HANDLE hResult = INVALID_HANDLE_VALUE;
  3503. for ( int iFFType;
  3504. ( iFFType = Find_Files( wfd, hResult, basedir, ext ) ) != 0; )
  3505. {
  3506. sprintf( filename, "%s%s", basedir, wfd.cFileName );
  3507. if ( wfd.cFileName[0] != '.' && iFFType == FF_PROCESS )
  3508. Process_File( filename, sizeof( filename ) );
  3509. }
  3510. #endif
  3511. }
  3512. // Shutdown P4
  3513. if ( g_bUsedAsLaunchableDLL && p4 )
  3514. {
  3515. p4->Shutdown();
  3516. p4->Disconnect();
  3517. }
  3518. Pause();
  3519. if ( g_bUsedAsLaunchableDLL )
  3520. {
  3521. LoggingSystem_PopLoggingState();
  3522. }
  3523. return 0;
  3524. }
  3525. CVTex g_VTex;
  3526. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVTex, IVTex, IVTEX_VERSION_STRING, g_VTex );
  3527. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVTex, ILaunchableDLL, LAUNCHABLE_DLL_INTERFACE_VERSION, g_VTex );
  3528. //-----------------------------------------------------------------------------
  3529. // New vtex compiler entry point
  3530. //-----------------------------------------------------------------------------
  3531. class CVTexCompiler : public CTier2DmAppSystem< IResourceCompiler >
  3532. {
  3533. typedef CTier2DmAppSystem< IResourceCompiler > BaseClass;
  3534. // Methods of IAppSystem
  3535. public:
  3536. virtual InitReturnVal_t Init();
  3537. // Methods of IVTexCompiler
  3538. public:
  3539. virtual bool CompileResource( const char *pFullPath, IResourceCompilerRegistry *pRegistry, CResourceStream *pPermanentStream, CResourceStream *pDataStream );
  3540. virtual bool CompileResource( CUtlBuffer &buf, const char *pFullPath, IResourceCompilerRegistry *pRegistry, CResourceStream *pPermanentStream, CResourceStream *pDataStream );
  3541. private:
  3542. bool CompileResource( CDmElement *pElement, const char *pElementFileName, IResourceCompilerRegistry *pRegistry, CResourceStream *pPermanentStream, CResourceStream *pDataStream );
  3543. CDmeTexture *CompileResource( CDmePrecompiledTexture *pPrecompiledResource );
  3544. };
  3545. static CVTexCompiler s_VTexCompiler;
  3546. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVTexCompiler, IResourceCompiler, RESOURCE_COMPILER_INTERFACE_VERSION, s_VTexCompiler );
  3547. //-----------------------------------------------------------------------------
  3548. // Init
  3549. //-----------------------------------------------------------------------------
  3550. InitReturnVal_t CVTexCompiler::Init()
  3551. {
  3552. InitReturnVal_t nRetVal = BaseClass::Init();
  3553. if ( nRetVal != INIT_OK )
  3554. return nRetVal;
  3555. MathLib_Init( 2.2f, 2.2f, 0.0f, 1.0f, false, false, false, false );
  3556. InstallDmElementFactories();
  3557. // FIXME: Should I use the same datamodel as in resource compiler?
  3558. g_pDataModel->SetUndoEnabled( false );
  3559. g_Quiet = true;
  3560. g_NoPause = true;
  3561. g_bWarningsAsErrors = false;
  3562. g_CreateDir = true;
  3563. g_UseGameDir = false;
  3564. g_bSupportsXBox360 = true;
  3565. return INIT_OK;
  3566. }
  3567. //-----------------------------------------------------------------------------
  3568. // Creates a texture from a precompiled texture
  3569. //-----------------------------------------------------------------------------
  3570. static void SetTextureStateFromPrecompiledTexture( CDmePrecompiledTexture *pPrecompiledTexture, CDmeTexture *pTexture )
  3571. {
  3572. pTexture->m_bClampS = pPrecompiledTexture->m_bClampS;
  3573. pTexture->m_bClampT = pPrecompiledTexture->m_bClampT;
  3574. pTexture->m_bClampU = pPrecompiledTexture->m_bClampU;
  3575. pTexture->m_bNoDebugOverride = pPrecompiledTexture->m_bNoDebugOverride;
  3576. pTexture->m_bNoLod = pPrecompiledTexture->m_bNoLod;
  3577. pTexture->m_bNormalMap = pPrecompiledTexture->m_bNormalMap;
  3578. pTexture->SetFilterType( (DmeTextureFilter_t)pPrecompiledTexture->m_nFilterType.Get() );
  3579. pTexture->m_flBumpScale = pPrecompiledTexture->m_flBumpScale;
  3580. }
  3581. //-----------------------------------------------------------------------------
  3582. // Loads a resource into a dme element
  3583. //-----------------------------------------------------------------------------
  3584. bool CVTexCompiler::CompileResource( const char *pFullPath, IResourceCompilerRegistry *pRegistry, CResourceStream *pPermanentStream, CResourceStream *pDataStream )
  3585. {
  3586. BitmapFileType_t mode;
  3587. CRC32_t nInputHash;
  3588. CRC32_Init( &nInputHash );
  3589. CDmePrecompiledTexture *pPrecompiledTexture = LoadConfigFile( pFullPath, &mode, &nInputHash );
  3590. if ( !pPrecompiledTexture->ValidateValues() )
  3591. return NULL;
  3592. pPrecompiledTexture->m_pSourceTexture = CreateElement< CDmeTexture >( pPrecompiledTexture->GetName(), DMFILEID_INVALID );
  3593. char pFullDir[ MAX_PATH ];
  3594. Q_strncpy( pFullDir, pFullPath, sizeof( pFullDir ) );
  3595. Q_StripFilename( pFullDir );
  3596. LoadSourceImages( pFullDir, pPrecompiledTexture, mode, &nInputHash );
  3597. CRC32_Final( &nInputHash );
  3598. bool bOK = CompileResource( pPrecompiledTexture, pFullPath, pRegistry, pPermanentStream, pDataStream );
  3599. g_pDataModel->RemoveFileId( pPrecompiledTexture->GetFileId() );
  3600. return bOK;
  3601. }
  3602. bool CVTexCompiler::CompileResource( CUtlBuffer &buf, const char *pFullPath, IResourceCompilerRegistry *pRegistry, CResourceStream *pPermanentStream, CResourceStream *pDataStream )
  3603. {
  3604. DmElementHandle_t hRoot;
  3605. if ( !g_pDataModel->Unserialize( buf, NULL, NULL, NULL, pFullPath, CR_FORCE_COPY, hRoot ) )
  3606. return NULL;
  3607. CDmElement *pElement = GetElement< CDmElement >( hRoot );
  3608. bool bOk = CompileResource( pElement, pFullPath, pRegistry, pPermanentStream, pDataStream );
  3609. g_pDataModel->RemoveFileId( pElement->GetFileId() );
  3610. return bOk;
  3611. }
  3612. CDmeTexture *CVTexCompiler::CompileResource( CDmePrecompiledTexture *pPrecompiledResource )
  3613. {
  3614. CDmeTexture *pTexture = pPrecompiledResource->m_pSourceTexture;
  3615. SetTextureStateFromPrecompiledTexture( pPrecompiledResource, pTexture );
  3616. IThreadPool *pVTexThreadPool = CreateNewThreadPool();
  3617. ThreadPoolStartParams_t startParams;
  3618. startParams.fDistribute = TRS_TRUE;
  3619. pVTexThreadPool->Start( startParams );
  3620. FloatBitMap_t::SetThreadPool( pVTexThreadPool );
  3621. int nCount = pPrecompiledResource->m_Processors.Count();
  3622. for ( int i = 0; i < nCount; ++i )
  3623. {
  3624. CDmeTexture *pDestTexture = CreateElement< CDmeTexture >( pTexture->GetName(), pTexture->GetFileId() );
  3625. CDmeTextureProcessor *pProcessor = pPrecompiledResource->m_Processors[i];
  3626. pProcessor->ProcessTexture( pTexture, pDestTexture );
  3627. DestroyElement( pTexture, TD_DEEP );
  3628. pTexture = pDestTexture;
  3629. }
  3630. ImageFormat dstFormat = ComputeDesiredImageFormat( pPrecompiledResource, pTexture );
  3631. if ( !ImageLoader::IsCompressed( dstFormat ) )
  3632. {
  3633. pTexture->ForEachImage( &CDmeImage::ConvertFormat, dstFormat );
  3634. }
  3635. else
  3636. {
  3637. CDmeTexture *pDestTexture = CreateElement< CDmeTexture >( pTexture->GetName(), pTexture->GetFileId() );
  3638. pDestTexture->CompressTexture( pTexture, dstFormat );
  3639. DestroyElement( pTexture, TD_DEEP );
  3640. pTexture = pDestTexture;
  3641. }
  3642. FloatBitMap_t::SetThreadPool( NULL );
  3643. DestroyThreadPool( pVTexThreadPool );
  3644. return pTexture;
  3645. }
  3646. //-----------------------------------------------------------------------------
  3647. // Writes resources
  3648. //-----------------------------------------------------------------------------
  3649. bool CVTexCompiler::CompileResource( CDmElement *pElement, const char *pElementFileName,
  3650. IResourceCompilerRegistry *pRegistry, CResourceStream *pPermanentStream, CResourceStream *pDataStream )
  3651. {
  3652. CDmePrecompiledTexture *pPrecompiledTexture = CastElement< CDmePrecompiledTexture >( pElement );
  3653. if ( !pPrecompiledTexture )
  3654. return false;
  3655. CDmeTexture *pTexture = CompileResource( pPrecompiledTexture );
  3656. const char *pResourceIdString = pPrecompiledTexture->GetName();
  3657. RegisterResourceInfo_t info;
  3658. info.m_nType = RESOURCE_TYPE_TEXTURE;
  3659. info.m_nId = ComputeResourceIdHash( pResourceIdString );
  3660. info.m_nDataOffset = pDataStream->Tell();
  3661. info.m_nCompressionType = RESOURCE_COMPRESSION_NONE;
  3662. info.m_nFlags = 0;
  3663. // Deal with permanent data
  3664. info.m_nPermanentDataOffset = pPermanentStream->Tell();
  3665. info.m_nPermanentDataSize = sizeof( TextureHeader_t );
  3666. TextureHeader_t *pSpec = pPermanentStream->Allocate< TextureHeader_t >( 1 );
  3667. memset( pSpec, 0, sizeof(TextureHeader_t) );
  3668. pSpec->m_nWidth = pTexture->Width();
  3669. pSpec->m_nHeight = pTexture->Height();
  3670. pSpec->m_nNumMipLevels = pTexture->MipLevelCount();
  3671. pSpec->m_nDepth = pTexture->Depth();
  3672. pSpec->m_nImageFormat = pTexture->Format();
  3673. pSpec->m_nFlags = 0;
  3674. if ( pTexture->m_bClampS )
  3675. {
  3676. pSpec->m_nFlags |= TSPEC_SUGGEST_CLAMPS;
  3677. }
  3678. if ( pTexture->m_bClampT )
  3679. {
  3680. pSpec->m_nFlags |= TSPEC_SUGGEST_CLAMPT;
  3681. }
  3682. if ( pTexture->m_bClampU )
  3683. {
  3684. pSpec->m_nFlags |= TSPEC_SUGGEST_CLAMPU;
  3685. }
  3686. if ( pTexture->m_bNoLod )
  3687. {
  3688. pSpec->m_nFlags |= TSPEC_NO_LOD;
  3689. }
  3690. pSpec->m_nMultisampleType = RENDER_MULTISAMPLE_NONE;
  3691. pSpec->m_Reflectivity.Init( 1, 1, 1, 1 ); // = pTexture->Reflectivity();
  3692. // Deal with cacheable data
  3693. // Allocate space for the structure, ensuring proper alignment
  3694. pDataStream->Align( 16 );
  3695. uint32 nStart = pDataStream->Tell();
  3696. // FIXME: Should store mip levels smallest to largest
  3697. int nMipCount = pTexture->MipLevelCount();
  3698. for ( int m = nMipCount; --m >= 0; )
  3699. {
  3700. int nFrameCount = pTexture->FrameCount();
  3701. for ( int f = 0; f < nFrameCount; ++f )
  3702. {
  3703. CDmeTextureFrame *pFrame = pTexture->GetFrame( f );
  3704. CDmeImageArray *pMipLevel = pFrame->GetMipLevel( m );
  3705. int nImageCount = pMipLevel->ImageCount();
  3706. for ( int i = 0; i < nImageCount; ++i )
  3707. {
  3708. CDmeImage *pImage = pMipLevel->GetImage( i );
  3709. int nSize = pImage->SizeInBytes();
  3710. void *pDest = pDataStream->AllocateBytes( nSize );
  3711. Q_memcpy( pDest, pImage->ImageBits(), nSize );
  3712. }
  3713. }
  3714. }
  3715. info.m_nDataSize = pDataStream->Tell() - nStart;
  3716. info.m_nUncompressedDataSize = info.m_nDataSize;
  3717. pRegistry->RegisterResource( info );
  3718. pRegistry->RegisterUsedType( "TextureBits_t", false );
  3719. pRegistry->RegisterUsedType( "TextureHeader_t", true );
  3720. DestroyElement( pTexture, TD_DEEP );
  3721. return true;
  3722. }