csgo-2018-source/dmserializers/importtex.cpp
2021-07-24 21:11:47 -07:00

568 lines
17 KiB
C++

//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "importkeyvaluebase.h"
#include "dmserializers.h"
#include "datamodel/idatamodel.h"
#include "datamodel/dmelement.h"
#include "datamodel/dmattributevar.h"
#include "tier1/KeyValues.h"
#include "tier1/UtlBuffer.h"
#include "datamodel/dmattribute.h"
#include "filesystem.h"
#include "tier2/tier2.h"
//-----------------------------------------------------------------------------
// Serialization class for make sheet files
//-----------------------------------------------------------------------------
class CImportTex : public IDmSerializer
{
public:
virtual const char *GetName() const { return "tex_source1"; }
virtual const char *GetDescription() const { return "Valve Texture Configuration File"; }
virtual bool IsBinaryFormat() const { return false; }
virtual bool StoresVersionInFile() const { return false; }
virtual int GetCurrentVersion() const { return 0; } // doesn't store a version
virtual const char *GetImportedFormat() const { return "tex"; }
virtual int GetImportedVersion() const { return 1; }
bool Serialize( CUtlBuffer &outBuf, CDmElement *pRoot ) { return false; }
// Read from the UtlBuffer, return true if successful, and return the read-in root in ppRoot.
bool Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion, const char *pSourceFormatName,
int nSourceFormatVersion, DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot );
private:
bool ParseOptionKey( const char *pKeyName, const char *pKeyValue, CDmElement *pTexture );
const char *GetPossiblyQuotedWord( const char *pInBuf, char *pOutbuf );
bool GetKeyValueFromBuffer( CUtlBuffer &buffer, char *key, char *val );
CDmElement *AddProcessor( CDmElement *pElement, const char *pProcessorType, const char *pName );
CDmElement *FindProcessor( CDmElement *pElement, const char *pProcessorType, const char *pName );
// Information for the mipmap processor
bool m_bNoNice;
bool m_bAlphatestMipmapping;
float m_flAlphatestMipmapThreshhold;
float m_flAlphatestMipmapHiFreqThreshhold;
};
//-----------------------------------------------------------------------------
// Singleton instance
//-----------------------------------------------------------------------------
static CImportTex s_ImportTex;
void InstallTEXImporter( IDataModel *pFactory )
{
pFactory->AddSerializer( &s_ImportTex );
}
//-----------------------------------------------------------------------------
// Processors
//-----------------------------------------------------------------------------
CDmElement *CImportTex::AddProcessor( CDmElement *pElement, const char *pProcessorType, const char *pName )
{
CDmrElementArray< CDmElement > processors( pElement, "processors", true );
CDmElement* pProcessor = CreateElement< CDmElement >( pProcessorType, pName, pElement->GetFileId() );
processors.AddToTail( pProcessor );
return pProcessor;
}
CDmElement *CImportTex::FindProcessor( CDmElement *pElement, const char *pProcessorType, const char *pName )
{
CDmrElementArrayConst< CDmElement > processors( pElement, "processors" );
for ( int i = 0; i < processors.Count(); ++i )
{
if ( !Q_stricmp( pProcessorType, processors[i]->GetTypeString() ) && !Q_stricmp( pName, processors[i]->GetName() ) )
return processors[i];
}
return NULL;
}
//-----------------------------------------------------------------------------
// Parses key values out of the txt file
//-----------------------------------------------------------------------------
bool CImportTex::ParseOptionKey( const char *pKeyName, const char *pKeyValue, CDmElement *pTexture )
{
int iValue = atoi( pKeyValue ); // To properly have "clamps 0" and not enable the clamping
if( !Q_stricmp( pKeyName, "startframe" ) )
{
pTexture->SetValue( "startFrame", iValue );
}
else if( !Q_stricmp( pKeyName, "endframe" ) )
{
pTexture->SetValue( "endFrame", iValue );
}
else if ( !Q_stricmp( pKeyName, "cubemap" ) )
{
pTexture->SetValue( "textureType", iValue ? 1 : 0 );
}
else if( !Q_stricmp( pKeyName, "volumetexture" ) )
{
pTexture->SetValue( "volumeTextureDepth", iValue );
/*
// FIXME: Volume textures don't currently support DXT compression
m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_NOCOMPRESS;
*/
}
else if( !Q_stricmp( pKeyName, "clamps" ) )
{
pTexture->SetValue( "clamps", iValue ? true : false );
}
else if( !Q_stricmp( pKeyName, "clampt" ) )
{
pTexture->SetValue( "clampt", iValue ? true : false );
}
else if( !Q_stricmp( pKeyName, "clampu" ) )
{
pTexture->SetValue( "clampu", iValue ? true : false );
}
else if ( !Q_stricmp( pKeyName, "nodebug" ) )
{
pTexture->SetValue( "noDebugOverride", iValue ? true : false );
}
else if( !Q_stricmp( pKeyName, "pointsample" ) )
{
if ( iValue != 0 )
{
pTexture->SetValue( "filterType", 4 );
}
}
else if( !Q_stricmp( pKeyName, "trilinear" ) )
{
if ( iValue != 0 )
{
pTexture->SetValue( "filterType", 2 );
}
}
else if( !Q_stricmp( pKeyName, "anisotropic" ) )
{
if ( iValue != 0 )
{
pTexture->SetValue( "filterType", 1 );
}
}
else if( !Q_stricmp( pKeyName, "nomip" ) )
{
pTexture->SetValue( "noMip", iValue ? true : false );
}
else if( !Q_stricmp( pKeyName, "nolod" ) )
{
pTexture->SetValue( "noLod", iValue ? true : false );
}
else if( !Q_stricmp( pKeyName, "nonice" ) )
{
m_bNoNice = iValue ? true : false;
}
else if( !Q_stricmp( pKeyName, "alphatest" ) )
{
m_bAlphatestMipmapping = iValue ? true : false;
}
else if( !Q_stricmp( pKeyName, "alphatest_threshhold" ) )
{
m_flAlphatestMipmapThreshhold = (float)atof( pKeyValue );
}
else if( !Q_stricmp( pKeyName, "alphatest_hifreq_threshhold" ) )
{
m_flAlphatestMipmapHiFreqThreshhold = (float)atof( pKeyValue );
}
else if( !Q_stricmp( pKeyName, "dxt5" ) )
{
pTexture->SetValue( "hintDxt5Compression", iValue ? true : false );
}
else if( !Q_stricmp( pKeyName, "nocompress" ) )
{
pTexture->SetValue( "noCompression", iValue ? true : false );
}
else if ( !Q_stricmp( pKeyName, "numchannels" ) )
{
CDmElement *pProcessor = AddProcessor( pTexture, "DmeTP_ChangeColorChannels", "changeColorChannels" );
pProcessor->SetValue( "maxChannels", iValue );
}
else
{
Warning("unrecognized option in text file - %s\n", pKeyName );
}
return true;
#if 0
else if ( !Q_stricmp( pKeyName, "skybox" ) )
{
// We're going to treat it like a cubemap until the very end (we have to load and process all cubemap
// faces at once, so we can match their edges with the texture compression and mipmapping).
m_bIsSkyBox = iValue ? true : false;
m_bIsCubeMap = iValue ? true : false;
}
else if ( !Q_stricmp( pKeyName, "skyboxcropped" ) )
{
m_bIsCroppedSkyBox = iValue ? true : false;
}
else if( !Q_stricmp( pKeyName, "bumpscale" ) )
{
m_flBumpScale = atof( pKeyValue );
}
else if( !Q_stricmp( pKeyName, "border" ) )
{
SetFlagValue( m_nFlags, TEXTUREFLAGS_BORDER, iValue );
// Gets applied to s, t and u We currently assume black border color
}
else if( !Q_stricmp( pKeyName, "normal" ) )
{
SetFlagValue( m_nFlags, TEXTUREFLAGS_NORMAL, iValue );
// Normal maps not supported for manual mip painting
m_bManualMip = false;
}
else if( !Q_stricmp( pKeyName, "normalga" ) )
{
m_bNormalToDXT5GA = iValue ? true : false;
SetFlagValue( m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_NORMAL_GA, iValue );
}
else if( !Q_stricmp( pKeyName, "invertgreen" ) )
{
m_bNormalInvertGreen = iValue ? true : false;
}
else if( !Q_stricmp( pKeyName, "ssbump" ) )
{
SetFlagValue( m_nFlags, TEXTUREFLAGS_SSBUMP, iValue );
}
else if( !Q_stricmp( pKeyName, "allmips" ) )
{
SetFlagValue( m_nFlags, TEXTUREFLAGS_ALL_MIPS, iValue );
}
else if( !Q_stricmp( pKeyName, "rendertarget" ) )
{
SetFlagValue( m_nFlags, TEXTUREFLAGS_RENDERTARGET, iValue );
}
else if( !Q_stricmp( pKeyName, "oneovermiplevelinalpha" ) )
{
SetFlagValue( m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_SET_ALPHA_ONEOVERMIP, iValue );
}
else if( !Q_stricmp( pKeyName, "premultcolorbyoneovermiplevel" ) )
{
SetFlagValue( m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_PREMULT_COLOR_ONEOVERMIP, iValue );
}
else if ( !Q_stricmp( pKeyName, "normaltodudv" ) )
{
m_bNormalToDuDv = iValue ? true : false;
SetFlagValue( m_vtfProcOptions.flags0, VtfProcessingOptions::OPT_NORMAL_DUDV, iValue );
}
else if ( !Q_stricmp( pKeyName, "stripalphachannel" ) )
{
m_bStripAlphaChannel = iValue ? true : false;
}
else if ( !Q_stricmp( pKeyName, "stripcolorchannel" ) )
{
m_bStripColorChannel = iValue ? true : false;
}
else if ( !Q_stricmp( pKeyName, "normalalphatodudvluminance" ) )
{
m_bAlphaToLuminance = iValue ? true : false;
}
else if ( !Q_stricmp( pKeyName, "dudv" ) )
{
m_bDuDv = iValue ? true : false;
}
else if( !Q_stricmp( pKeyName, "reduce" ) )
{
m_nReduceX = atoi(pKeyValue);
m_nReduceY = m_nReduceX;
}
else if( !Q_stricmp( pKeyName, "reducex" ) )
{
m_nReduceX = atoi(pKeyValue);
}
else if( !Q_stricmp( pKeyName, "reducey" ) )
{
m_nReduceY = atoi(pKeyValue);
}
else if( !Q_stricmp( pKeyName, "maxwidth" ) )
{
m_nMaxDimensionX = atoi(pKeyValue);
}
else if( !Q_stricmp( pKeyName, "maxwidth_360" ) )
{
m_nMaxDimensionX_360 = atoi(pKeyValue);
}
else if( !Q_stricmp( pKeyName, "maxheight" ) )
{
m_nMaxDimensionY = atoi(pKeyValue);
}
else if( !Q_stricmp( pKeyName, "maxheight_360" ) )
{
m_nMaxDimensionY_360 = atoi(pKeyValue);
}
else if( !Q_stricmp( pKeyName, "alphatodistance" ) )
{
m_bAlphaToDistance = iValue ? true : false;
}
else if( !Q_stricmp( pKeyName, "distancespread" ) )
{
m_flDistanceSpread = atof(pKeyValue);
}
else if( !Q_stricmp( pKeyName, "pfmscale" ) )
{
m_pfmscale=atof(pKeyValue);
VTexMsg( "pfmscale = %.2f\n", m_pfmscale );
}
else if ( !Q_stricmp( pKeyName, "pfm" ) )
{
if ( iValue )
g_eMode = BITMAP_FILE_TYPE_PFM;
}
else if ( !Q_stricmp( pKeyName, "specvar" ) )
{
int iDecayChannel = -1;
if ( !Q_stricmp( pKeyValue, "red" ) || !Q_stricmp( pKeyValue, "r" ) )
iDecayChannel = 0;
if ( !Q_stricmp( pKeyValue, "green" ) || !Q_stricmp( pKeyValue, "g" ) )
iDecayChannel = 1;
if ( !Q_stricmp( pKeyValue, "blue" ) || !Q_stricmp( pKeyValue, "b" ) )
iDecayChannel = 2;
if ( !Q_stricmp( pKeyValue, "alpha" ) || !Q_stricmp( pKeyValue, "a" ) )
iDecayChannel = 3;
if ( iDecayChannel >= 0 && iDecayChannel < 4 )
{
m_vtfProcOptions.flags0 |= ( VtfProcessingOptions::OPT_DECAY_R | VtfProcessingOptions::OPT_DECAY_EXP_R ) << iDecayChannel;
m_vtfProcOptions.numNotDecayMips[iDecayChannel] = 0;
m_vtfProcOptions.clrDecayGoal[iDecayChannel] = 0;
m_vtfProcOptions.fDecayExponentBase[iDecayChannel] = 0.75;
m_bManualMip = false;
SetFlagValue( m_nFlags, TEXTUREFLAGS_ALL_MIPS, 1 );
}
}
else if ( Q_stricmp( pKeyName, "manualmip" ) == 0 )
{
if ( ( m_nVolumeTextureDepth == 1 ) && !( m_nFlags & ( TEXTUREFLAGS_NORMAL | TEXTUREFLAGS_NOMIP ) ) )
{
m_bManualMip = true;
}
}
else if ( !Q_stricmp( pKeyName, "mipblend" ) )
{
SetFlagValue( m_nFlags, TEXTUREFLAGS_ALL_MIPS, 1 );
// Possible values
if ( !Q_stricmp( pKeyValue, "detail" ) ) // Skip 2 mips and fade to gray -> (128, 128, 128, -)
{
for( int ch = 0; ch < 3; ++ ch )
{
m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_DECAY_R << ch;
// m_vtfProcOptions.flags0 &= ~(VtfProcessingOptions::OPT_DECAY_EXP_R << ch);
m_vtfProcOptions.numNotDecayMips[ch] = 2;
m_vtfProcOptions.clrDecayGoal[ch] = 128;
}
}
/*
else if ( !Q_stricmp( pKeyValue, "additive" ) ) // Skip 2 mips and fade to black -> (0, 0, 0, -)
{
for( int ch = 0; ch < 3; ++ ch )
{
m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_DECAY_R << ch;
m_vtfProcOptions.flags0 &= ~(VtfProcessingOptions::OPT_DECAY_EXP_R << ch);
m_vtfProcOptions.numDecayMips[ch] = 2;
m_vtfProcOptions.clrDecayGoal[ch] = 0;
}
}
else if ( !Q_stricmp( pKeyValue, "alphablended" ) ) // Skip 2 mips and fade out alpha to 0
{
for( int ch = 3; ch < 4; ++ ch )
{
m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_DECAY_R << ch;
m_vtfProcOptions.flags0 &= ~(VtfProcessingOptions::OPT_DECAY_EXP_R << ch);
m_vtfProcOptions.numDecayMips[ch] = 2;
m_vtfProcOptions.clrDecayGoal[ch] = 0;
}
}
*/
else
{
// Parse the given value:
// skip=3:r=255:g=255:b=255:a=255 - linear decay
// r=0e.75 - exponential decay targeting 0 with exponent base 0.75
int nSteps = 0; // default
for ( char const *szParse = pKeyValue; szParse; szParse = strchr( szParse, ':' ), szParse ? ++ szParse : 0 )
{
if ( char const *sz = StringAfterPrefix( szParse, "skip=" ) )
{
szParse = sz;
nSteps = atoi(sz);
}
else if ( StringHasPrefix( szParse, "r=" ) ||
StringHasPrefix( szParse, "g=" ) ||
StringHasPrefix( szParse, "b=" ) ||
StringHasPrefix( szParse, "a=" ) )
{
int ch = 0;
switch ( *szParse )
{
case 'g': case 'G': ch = 1; break;
case 'b': case 'B': ch = 2; break;
case 'a': case 'A': ch = 3; break;
}
szParse += 2;
m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_DECAY_R << ch;
m_vtfProcOptions.flags0 &= ~(VtfProcessingOptions::OPT_DECAY_EXP_R << ch);
m_vtfProcOptions.numNotDecayMips[ch] = nSteps;
m_vtfProcOptions.clrDecayGoal[ch] = atoi( szParse );
while ( isdigit( *szParse ) )
++ szParse;
// Exponential decay
if ( ( *szParse == 'e' || *szParse == 'E' ) && ( szParse[1] == '.' ) )
{
m_vtfProcOptions.flags0 |= VtfProcessingOptions::OPT_DECAY_EXP_R << ch;
m_vtfProcOptions.fDecayExponentBase[ch] = ( float ) atof( szParse + 1 );
}
}
else
{
Warning( "invalid mipblend setting \"%s\"\n", pKeyValue );
}
}
}
}
else if( !Q_stricmp( pKeyName, "srgb" ) )
{
// Do nothing for now...this will be removed shortly
}
#endif
}
const char *CImportTex::GetPossiblyQuotedWord( const char *pInBuf, char *pOutbuf )
{
pInBuf += strspn( pInBuf, " \t" ); // skip whitespace
const char *pWordEnd;
bool bQuote = false;
if (pInBuf[0]=='"')
{
pInBuf++;
pWordEnd=strchr(pInBuf,'"');
bQuote = true;
}
else
{
pWordEnd=strchr(pInBuf,' ');
if (! pWordEnd )
pWordEnd = strchr(pInBuf,'\t' );
if (! pWordEnd )
pWordEnd = pInBuf+strlen(pInBuf);
}
if ((! pWordEnd ) || (pWordEnd == pInBuf ) )
return NULL; // no word found
memcpy( pOutbuf, pInBuf, pWordEnd-pInBuf );
pOutbuf[pWordEnd-pInBuf]=0;
pInBuf = pWordEnd;
if ( bQuote )
pInBuf++;
return pInBuf;
}
// GetKeyValueFromBuffer:
// fills in "key" and "val" respectively and returns "true" if succeeds.
// returns false if:
// a) end-of-buffer is reached (then "val" is empty)
// b) error occurs (then "val" is the error message)
//
bool CImportTex::GetKeyValueFromBuffer( CUtlBuffer &buffer, char *key, char *val )
{
char buf[2048];
while( buffer.GetBytesRemaining() )
{
buffer.GetLine( buf, sizeof( buf ) );
// Scanning algorithm
char *pComment = strpbrk( buf, "#\n\r" );
if ( pComment )
*pComment = 0;
pComment = strstr( buf, "//" );
if ( pComment)
*pComment = 0;
const char *scan = buf;
scan=GetPossiblyQuotedWord( scan, key );
if ( scan )
{
scan=GetPossiblyQuotedWord( scan, val );
if ( scan )
return true;
else
{
sprintf( val, "parameter %s has no value", key );
return false;
}
}
}
val[0] = 0;
return false;
}
//-----------------------------------------------------------------------------
// Main entry point for the unserialization
//-----------------------------------------------------------------------------
bool CImportTex::Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion, const char *pSourceFormatName,
int nSourceFormatVersion, DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot )
{
*ppRoot = NULL;
m_bNoNice = m_bAlphatestMipmapping = false;
m_flAlphatestMipmapThreshhold = m_flAlphatestMipmapHiFreqThreshhold = 0.0f;
CDmElement *pTexture = CreateElement< CDmElement >( "DmePrecompiledTexture", "root", DMFILEID_INVALID );
pTexture->SetValue( "imageFileName", "__unspecified_texture" );
char pKey[2048];
char pVal[2048];
while( GetKeyValueFromBuffer( buf, pKey, pVal ) )
{
ParseOptionKey( pKey, pVal, pTexture );
}
if ( pVal[0] )
{
Warning( "Error importing txt file! %s\n", pVal );
return false;
}
// Prefer to do mipmapping as one of the final processors
if ( !pTexture->GetValue< bool >( "noMip" ) || !pTexture->GetValue< bool >( "noLod" ) )
{
CDmElement *pProcessor = AddProcessor( pTexture, "DmeTP_ComputeMipmaps", "computeMipmaps" );
pProcessor->SetValue( "noNiceFiltering", m_bNoNice );
pProcessor->SetValue( "alphaTestDownsampling", m_bAlphatestMipmapping );
pProcessor->SetValue( "alphaTestDownsampleThreshhold", m_flAlphatestMipmapThreshhold );
pProcessor->SetValue( "alphaTestDownsampleHiFreqThreshhold" , m_flAlphatestMipmapHiFreqThreshhold );
}
*ppRoot = pTexture;
bool bOk = g_pDataModel->UpdateUnserializedElements( pSourceFormatName, nSourceFormatVersion, fileid, idConflictResolution, ppRoot );
if ( !bOk )
{
*ppRoot = NULL;
}
return bOk;
}