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
343 lines
7.9 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//===========================================================================// |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <direct.h> |
|
#include "bitmap/imageformat.h" |
|
#include "tier1/strtools.h" |
|
#include "mathlib/mathlib.h" |
|
#include "bitmap/TGAWriter.h" |
|
#include "bitmap/TGALoader.h" |
|
#include <math.h> |
|
#include <conio.h> |
|
#include "tier1/utlbuffer.h" |
|
#include "tier2/tier2.h" |
|
#include "filesystem.h" |
|
|
|
static bool g_NoPause = false; |
|
static bool g_Quiet = false; |
|
|
|
static void Pause( void ) |
|
{ |
|
if( !g_NoPause ) |
|
{ |
|
printf( "Hit a key to continue\n" ); |
|
getch(); |
|
} |
|
} |
|
|
|
static bool ImageRGBA8888HasAlpha( unsigned char *pImage, int numTexels ) |
|
{ |
|
int i; |
|
for( i = 0; i < numTexels; i++ ) |
|
{ |
|
if( pImage[i*4+3] != 255 ) |
|
{ |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
static bool GetKeyValueFromBuffer( CUtlBuffer &buf, char **key, char **val ) |
|
{ |
|
char stringBuf[2048]; |
|
while( buf.IsValid() ) |
|
{ |
|
buf.GetLine( stringBuf, sizeof(stringBuf) ); |
|
char *scan = stringBuf; |
|
// search for the first quote for the key. |
|
while( 1 ) |
|
{ |
|
if( *scan == '\"' ) |
|
{ |
|
*key = ++scan; |
|
break; |
|
} |
|
if( *scan == '#' ) |
|
{ |
|
goto next_line; // comment |
|
} |
|
if( *scan == '\0' ) |
|
{ |
|
goto next_line; // end of line. |
|
} |
|
scan++; |
|
} |
|
// read the key until another quote. |
|
while( 1 ) |
|
{ |
|
if( *scan == '\"' ) |
|
{ |
|
*scan = '\0'; |
|
scan++; |
|
break; |
|
} |
|
if( *scan == '\0' ) |
|
{ |
|
goto next_line; |
|
} |
|
scan++; |
|
} |
|
// search for the first quote for the value. |
|
while( 1 ) |
|
{ |
|
if( *scan == '\"' ) |
|
{ |
|
*val = ++scan; |
|
break; |
|
} |
|
if( *scan == '#' ) |
|
{ |
|
goto next_line; // comment |
|
} |
|
if( *scan == '\0' ) |
|
{ |
|
goto next_line; // end of line. |
|
} |
|
scan++; |
|
} |
|
// read the value until another quote. |
|
while( 1 ) |
|
{ |
|
if( *scan == '\"' ) |
|
{ |
|
*scan = '\0'; |
|
scan++; |
|
// got a key and a value, so get the hell out of here. |
|
return true; |
|
} |
|
if( *scan == '\0' ) |
|
{ |
|
goto next_line; |
|
} |
|
scan++; |
|
} |
|
next_line: |
|
; |
|
} |
|
return false; |
|
} |
|
|
|
static void LoadConfigFile( const char *pFileName, float *bumpScale, int *startFrame, int *endFrame ) |
|
{ |
|
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); |
|
if ( !g_pFullFileSystem->ReadFile( pFileName, NULL, buf ) ) |
|
{ |
|
fprintf( stderr, "Can't open: %s\n", pFileName ); |
|
Pause(); |
|
exit( -1 ); |
|
} |
|
|
|
char *key = NULL; |
|
char *val = NULL; |
|
while( GetKeyValueFromBuffer( buf, &key, &val ) ) |
|
{ |
|
if( stricmp( key, "bumpscale" ) == 0 ) |
|
{ |
|
*bumpScale = atof( val ); |
|
} |
|
if( stricmp( key, "startframe" ) == 0 ) |
|
{ |
|
*startFrame = atoi( val ); |
|
} |
|
else if( stricmp( key, "endframe" ) == 0 ) |
|
{ |
|
*endFrame = atoi( val ); |
|
} |
|
} |
|
} |
|
|
|
static void Usage() |
|
{ |
|
fprintf( stderr, "Usage: height2normal [-nopause] [-quiet] tex1_normal.txt tex2_normal.txt . . .\n" ); |
|
fprintf( stderr, "-quiet : don't print anything out, don't pause for input\n" ); |
|
fprintf( stderr, "-nopause : don't pause for input\n" ); |
|
Pause(); |
|
exit( -1 ); |
|
} |
|
|
|
void ProcessFiles( const char *pNormalFileNameWithoutExtension, |
|
int startFrame, int endFrame, |
|
float bumpScale ) |
|
{ |
|
static char heightTGAFileName[1024]; |
|
static char normalTGAFileName[1024]; |
|
static char buf[1024]; |
|
bool animated = !( startFrame == -1 || endFrame == -1 ); |
|
int numFrames = endFrame - startFrame + 1; |
|
int frameID; |
|
|
|
for( frameID = 0; frameID < numFrames; frameID++ ) |
|
{ |
|
if( animated ) |
|
{ |
|
sprintf( normalTGAFileName, "%s%03d.tga", pNormalFileNameWithoutExtension, frameID + startFrame ); |
|
} |
|
else |
|
{ |
|
sprintf( normalTGAFileName, "%s.tga", pNormalFileNameWithoutExtension ); |
|
} |
|
if( !Q_stristr( pNormalFileNameWithoutExtension, "_normal" ) ) |
|
{ |
|
fprintf( stderr, "ERROR: config file name must end in _normal.txt\n" ); |
|
return; |
|
} |
|
|
|
strcpy( buf, pNormalFileNameWithoutExtension ); |
|
|
|
// Strip '_normal' off the end because we're looking for '_height' |
|
char *pcUnderscore = Q_stristr( buf, "_normal" ); |
|
*pcUnderscore = NULL; |
|
|
|
if( animated ) |
|
{ |
|
sprintf( heightTGAFileName, "%s_height%03d.tga", buf, frameID + startFrame ); |
|
} |
|
else |
|
{ |
|
sprintf( heightTGAFileName, "%s_height.tga", buf ); |
|
} |
|
|
|
enum ImageFormat imageFormat; |
|
int width, height; |
|
float sourceGamma; |
|
CUtlBuffer buf; |
|
if ( !g_pFullFileSystem->ReadFile( heightTGAFileName, NULL, buf ) ) |
|
{ |
|
fprintf( stderr, "%s not found\n", heightTGAFileName ); |
|
return; |
|
} |
|
|
|
if ( !TGALoader::GetInfo( buf, &width, &height, &imageFormat, &sourceGamma ) ) |
|
{ |
|
fprintf( stderr, "error in %s\n", heightTGAFileName ); |
|
return; |
|
} |
|
|
|
int memRequired = ImageLoader::GetMemRequired( width, height, 1, IMAGE_FORMAT_IA88, false ); |
|
unsigned char *pImageIA88 = new unsigned char[memRequired]; |
|
|
|
buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); |
|
TGALoader::Load( pImageIA88, buf, width, height, IMAGE_FORMAT_IA88, sourceGamma, false ); |
|
|
|
memRequired = ImageLoader::GetMemRequired( width, height, 1, IMAGE_FORMAT_RGBA8888, false ); |
|
unsigned char *pImageRGBA8888 = new unsigned char[memRequired]; |
|
ImageLoader::ConvertIA88ImageToNormalMapRGBA8888( pImageIA88, width, height, pImageRGBA8888, bumpScale ); |
|
|
|
CUtlBuffer normalBuf; |
|
ImageLoader::NormalizeNormalMapRGBA8888( pImageRGBA8888, width * height ); |
|
if( ImageRGBA8888HasAlpha( pImageRGBA8888, width * height ) ) |
|
{ |
|
TGAWriter::WriteToBuffer( pImageRGBA8888, normalBuf, width, height, IMAGE_FORMAT_RGBA8888, IMAGE_FORMAT_RGBA8888 ); |
|
} |
|
else |
|
{ |
|
memRequired = ImageLoader::GetMemRequired( width, height, 1, IMAGE_FORMAT_RGB888, false ); |
|
unsigned char *pImageRGB888 = new unsigned char[memRequired]; |
|
ImageLoader::ConvertImageFormat( pImageRGBA8888, IMAGE_FORMAT_RGBA8888, |
|
pImageRGB888, IMAGE_FORMAT_RGB888, width, height, 0, 0 ); |
|
TGAWriter::WriteToBuffer( pImageRGB888, normalBuf, width, height, IMAGE_FORMAT_RGB888, IMAGE_FORMAT_RGB888 ); |
|
delete [] pImageRGB888; |
|
} |
|
if ( !g_pFullFileSystem->WriteFile( normalTGAFileName, NULL, normalBuf ) ) |
|
{ |
|
fprintf( stderr, "unable to write %s\n", normalTGAFileName ); |
|
return; |
|
} |
|
delete [] pImageIA88; |
|
delete [] pImageRGBA8888; |
|
} |
|
} |
|
|
|
int main( int argc, char **argv ) |
|
{ |
|
if( argc < 2 ) |
|
{ |
|
Usage(); |
|
} |
|
MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f ); |
|
InitDefaultFileSystem(); |
|
|
|
int i = 1; |
|
while( i < argc ) |
|
{ |
|
if( stricmp( argv[i], "-quiet" ) == 0 ) |
|
{ |
|
i++; |
|
g_Quiet = true; |
|
g_NoPause = true; // no point in pausing if we aren't going to print anything out. |
|
} |
|
if( stricmp( argv[i], "-nopause" ) == 0 ) |
|
{ |
|
i++; |
|
g_NoPause = true; |
|
} |
|
else |
|
{ |
|
break; |
|
} |
|
} |
|
|
|
char pCurrentDirectory[MAX_PATH]; |
|
if ( _getcwd( pCurrentDirectory, sizeof(pCurrentDirectory) ) == NULL ) |
|
{ |
|
fprintf( stderr, "Unable to get the current directory\n" ); |
|
return -1; |
|
} |
|
|
|
Q_FixSlashes( pCurrentDirectory ); |
|
Q_StripTrailingSlash( pCurrentDirectory ); |
|
|
|
for( ; i < argc; i++ ) |
|
{ |
|
static char normalFileNameWithoutExtension[1024]; |
|
char *pFileName; |
|
if ( !Q_IsAbsolutePath( argv[i] ) ) |
|
{ |
|
Q_snprintf( normalFileNameWithoutExtension, sizeof(normalFileNameWithoutExtension), "%s\\%s", pCurrentDirectory, argv[i] ); |
|
pFileName = normalFileNameWithoutExtension; |
|
} |
|
else |
|
{ |
|
pFileName = argv[i]; |
|
} |
|
|
|
if( !g_Quiet ) |
|
{ |
|
printf( "file: %s\n", pFileName ); |
|
} |
|
float bumpScale = -1.0f; |
|
int startFrame = -1; |
|
int endFrame = -1; |
|
LoadConfigFile( pFileName, &bumpScale, &startFrame, &endFrame ); |
|
if( bumpScale == -1.0f ) |
|
{ |
|
fprintf( stderr, "Must specify \"bumpscale\" in config file\n" ); |
|
Pause(); |
|
continue; |
|
} |
|
if( ( startFrame == -1 && endFrame != -1 ) || |
|
( startFrame != -1 && endFrame == -1 ) ) |
|
{ |
|
fprintf( stderr, "ERROR: If you use startframe, you must use endframe, and vice versa.\n" ); |
|
Pause(); |
|
continue; |
|
} |
|
if( !g_Quiet ) |
|
{ |
|
printf( "\tbumpscale: %f\n", bumpScale ); |
|
} |
|
|
|
Q_StripExtension( pFileName, normalFileNameWithoutExtension, sizeof( normalFileNameWithoutExtension ) ); |
|
ProcessFiles( normalFileNameWithoutExtension, |
|
startFrame, endFrame, |
|
bumpScale ); |
|
} |
|
return 0; |
|
}
|
|
|