Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

343 lines
7.9 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <direct.h>
  11. #include "bitmap/imageformat.h"
  12. #include "tier1/strtools.h"
  13. #include "mathlib/mathlib.h"
  14. #include "bitmap/TGAWriter.h"
  15. #include "bitmap/TGALoader.h"
  16. #include <math.h>
  17. #include <conio.h>
  18. #include "tier1/utlbuffer.h"
  19. #include "tier2/tier2.h"
  20. #include "filesystem.h"
  21. static bool g_NoPause = false;
  22. static bool g_Quiet = false;
  23. static void Pause( void )
  24. {
  25. if( !g_NoPause )
  26. {
  27. printf( "Hit a key to continue\n" );
  28. getch();
  29. }
  30. }
  31. static bool ImageRGBA8888HasAlpha( unsigned char *pImage, int numTexels )
  32. {
  33. int i;
  34. for( i = 0; i < numTexels; i++ )
  35. {
  36. if( pImage[i*4+3] != 255 )
  37. {
  38. return true;
  39. }
  40. }
  41. return false;
  42. }
  43. static bool GetKeyValueFromBuffer( CUtlBuffer &buf, char **key, char **val )
  44. {
  45. char stringBuf[2048];
  46. while( buf.IsValid() )
  47. {
  48. buf.GetLine( stringBuf, sizeof(stringBuf) );
  49. char *scan = stringBuf;
  50. // search for the first quote for the key.
  51. while( 1 )
  52. {
  53. if( *scan == '\"' )
  54. {
  55. *key = ++scan;
  56. break;
  57. }
  58. if( *scan == '#' )
  59. {
  60. goto next_line; // comment
  61. }
  62. if( *scan == '\0' )
  63. {
  64. goto next_line; // end of line.
  65. }
  66. scan++;
  67. }
  68. // read the key until another quote.
  69. while( 1 )
  70. {
  71. if( *scan == '\"' )
  72. {
  73. *scan = '\0';
  74. scan++;
  75. break;
  76. }
  77. if( *scan == '\0' )
  78. {
  79. goto next_line;
  80. }
  81. scan++;
  82. }
  83. // search for the first quote for the value.
  84. while( 1 )
  85. {
  86. if( *scan == '\"' )
  87. {
  88. *val = ++scan;
  89. break;
  90. }
  91. if( *scan == '#' )
  92. {
  93. goto next_line; // comment
  94. }
  95. if( *scan == '\0' )
  96. {
  97. goto next_line; // end of line.
  98. }
  99. scan++;
  100. }
  101. // read the value until another quote.
  102. while( 1 )
  103. {
  104. if( *scan == '\"' )
  105. {
  106. *scan = '\0';
  107. scan++;
  108. // got a key and a value, so get the hell out of here.
  109. return true;
  110. }
  111. if( *scan == '\0' )
  112. {
  113. goto next_line;
  114. }
  115. scan++;
  116. }
  117. next_line:
  118. ;
  119. }
  120. return false;
  121. }
  122. static void LoadConfigFile( const char *pFileName, float *bumpScale, int *startFrame, int *endFrame )
  123. {
  124. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  125. if ( !g_pFullFileSystem->ReadFile( pFileName, NULL, buf ) )
  126. {
  127. fprintf( stderr, "Can't open: %s\n", pFileName );
  128. Pause();
  129. exit( -1 );
  130. }
  131. char *key = NULL;
  132. char *val = NULL;
  133. while( GetKeyValueFromBuffer( buf, &key, &val ) )
  134. {
  135. if( stricmp( key, "bumpscale" ) == 0 )
  136. {
  137. *bumpScale = atof( val );
  138. }
  139. if( stricmp( key, "startframe" ) == 0 )
  140. {
  141. *startFrame = atoi( val );
  142. }
  143. else if( stricmp( key, "endframe" ) == 0 )
  144. {
  145. *endFrame = atoi( val );
  146. }
  147. }
  148. }
  149. static void Usage()
  150. {
  151. fprintf( stderr, "Usage: height2normal [-nopause] [-quiet] tex1_normal.txt tex2_normal.txt . . .\n" );
  152. fprintf( stderr, "-quiet : don't print anything out, don't pause for input\n" );
  153. fprintf( stderr, "-nopause : don't pause for input\n" );
  154. Pause();
  155. exit( -1 );
  156. }
  157. void ProcessFiles( const char *pNormalFileNameWithoutExtension,
  158. int startFrame, int endFrame,
  159. float bumpScale )
  160. {
  161. static char heightTGAFileName[1024];
  162. static char normalTGAFileName[1024];
  163. static char buf[1024];
  164. bool animated = !( startFrame == -1 || endFrame == -1 );
  165. int numFrames = endFrame - startFrame + 1;
  166. int frameID;
  167. for( frameID = 0; frameID < numFrames; frameID++ )
  168. {
  169. if( animated )
  170. {
  171. sprintf( normalTGAFileName, "%s%03d.tga", pNormalFileNameWithoutExtension, frameID + startFrame );
  172. }
  173. else
  174. {
  175. sprintf( normalTGAFileName, "%s.tga", pNormalFileNameWithoutExtension );
  176. }
  177. if( !Q_stristr( pNormalFileNameWithoutExtension, "_normal" ) )
  178. {
  179. fprintf( stderr, "ERROR: config file name must end in _normal.txt\n" );
  180. return;
  181. }
  182. strcpy( buf, pNormalFileNameWithoutExtension );
  183. // Strip '_normal' off the end because we're looking for '_height'
  184. char *pcUnderscore = Q_stristr( buf, "_normal" );
  185. *pcUnderscore = NULL;
  186. if( animated )
  187. {
  188. sprintf( heightTGAFileName, "%s_height%03d.tga", buf, frameID + startFrame );
  189. }
  190. else
  191. {
  192. sprintf( heightTGAFileName, "%s_height.tga", buf );
  193. }
  194. enum ImageFormat imageFormat;
  195. int width, height;
  196. float sourceGamma;
  197. CUtlBuffer buf;
  198. if ( !g_pFullFileSystem->ReadFile( heightTGAFileName, NULL, buf ) )
  199. {
  200. fprintf( stderr, "%s not found\n", heightTGAFileName );
  201. return;
  202. }
  203. if ( !TGALoader::GetInfo( buf, &width, &height, &imageFormat, &sourceGamma ) )
  204. {
  205. fprintf( stderr, "error in %s\n", heightTGAFileName );
  206. return;
  207. }
  208. int memRequired = ImageLoader::GetMemRequired( width, height, 1, IMAGE_FORMAT_IA88, false );
  209. unsigned char *pImageIA88 = new unsigned char[memRequired];
  210. buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  211. TGALoader::Load( pImageIA88, buf, width, height, IMAGE_FORMAT_IA88, sourceGamma, false );
  212. memRequired = ImageLoader::GetMemRequired( width, height, 1, IMAGE_FORMAT_RGBA8888, false );
  213. unsigned char *pImageRGBA8888 = new unsigned char[memRequired];
  214. ImageLoader::ConvertIA88ImageToNormalMapRGBA8888( pImageIA88, width, height, pImageRGBA8888, bumpScale );
  215. CUtlBuffer normalBuf;
  216. ImageLoader::NormalizeNormalMapRGBA8888( pImageRGBA8888, width * height );
  217. if( ImageRGBA8888HasAlpha( pImageRGBA8888, width * height ) )
  218. {
  219. TGAWriter::WriteToBuffer( pImageRGBA8888, normalBuf, width, height, IMAGE_FORMAT_RGBA8888, IMAGE_FORMAT_RGBA8888 );
  220. }
  221. else
  222. {
  223. memRequired = ImageLoader::GetMemRequired( width, height, 1, IMAGE_FORMAT_RGB888, false );
  224. unsigned char *pImageRGB888 = new unsigned char[memRequired];
  225. ImageLoader::ConvertImageFormat( pImageRGBA8888, IMAGE_FORMAT_RGBA8888,
  226. pImageRGB888, IMAGE_FORMAT_RGB888, width, height, 0, 0 );
  227. TGAWriter::WriteToBuffer( pImageRGB888, normalBuf, width, height, IMAGE_FORMAT_RGB888, IMAGE_FORMAT_RGB888 );
  228. delete [] pImageRGB888;
  229. }
  230. if ( !g_pFullFileSystem->WriteFile( normalTGAFileName, NULL, normalBuf ) )
  231. {
  232. fprintf( stderr, "unable to write %s\n", normalTGAFileName );
  233. return;
  234. }
  235. delete [] pImageIA88;
  236. delete [] pImageRGBA8888;
  237. }
  238. }
  239. int main( int argc, char **argv )
  240. {
  241. if( argc < 2 )
  242. {
  243. Usage();
  244. }
  245. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
  246. InitDefaultFileSystem();
  247. int i = 1;
  248. while( i < argc )
  249. {
  250. if( stricmp( argv[i], "-quiet" ) == 0 )
  251. {
  252. i++;
  253. g_Quiet = true;
  254. g_NoPause = true; // no point in pausing if we aren't going to print anything out.
  255. }
  256. if( stricmp( argv[i], "-nopause" ) == 0 )
  257. {
  258. i++;
  259. g_NoPause = true;
  260. }
  261. else
  262. {
  263. break;
  264. }
  265. }
  266. char pCurrentDirectory[MAX_PATH];
  267. if ( _getcwd( pCurrentDirectory, sizeof(pCurrentDirectory) ) == NULL )
  268. {
  269. fprintf( stderr, "Unable to get the current directory\n" );
  270. return -1;
  271. }
  272. Q_FixSlashes( pCurrentDirectory );
  273. Q_StripTrailingSlash( pCurrentDirectory );
  274. for( ; i < argc; i++ )
  275. {
  276. static char normalFileNameWithoutExtension[1024];
  277. char *pFileName;
  278. if ( !Q_IsAbsolutePath( argv[i] ) )
  279. {
  280. Q_snprintf( normalFileNameWithoutExtension, sizeof(normalFileNameWithoutExtension), "%s\\%s", pCurrentDirectory, argv[i] );
  281. pFileName = normalFileNameWithoutExtension;
  282. }
  283. else
  284. {
  285. pFileName = argv[i];
  286. }
  287. if( !g_Quiet )
  288. {
  289. printf( "file: %s\n", pFileName );
  290. }
  291. float bumpScale = -1.0f;
  292. int startFrame = -1;
  293. int endFrame = -1;
  294. LoadConfigFile( pFileName, &bumpScale, &startFrame, &endFrame );
  295. if( bumpScale == -1.0f )
  296. {
  297. fprintf( stderr, "Must specify \"bumpscale\" in config file\n" );
  298. Pause();
  299. continue;
  300. }
  301. if( ( startFrame == -1 && endFrame != -1 ) ||
  302. ( startFrame != -1 && endFrame == -1 ) )
  303. {
  304. fprintf( stderr, "ERROR: If you use startframe, you must use endframe, and vice versa.\n" );
  305. Pause();
  306. continue;
  307. }
  308. if( !g_Quiet )
  309. {
  310. printf( "\tbumpscale: %f\n", bumpScale );
  311. }
  312. Q_StripExtension( pFileName, normalFileNameWithoutExtension, sizeof( normalFileNameWithoutExtension ) );
  313. ProcessFiles( normalFileNameWithoutExtension,
  314. startFrame, endFrame,
  315. bumpScale );
  316. }
  317. return 0;
  318. }