|
|
//====== 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; }
|