344 lines
7.9 KiB
C++
344 lines
7.9 KiB
C++
//========= 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;
|
|
}
|