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.
1078 lines
32 KiB
1078 lines
32 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
|
|
#include "vmtdoc.h" |
|
#include "tier1/KeyValues.h" |
|
#include "tier1/utlbuffer.h" |
|
#include "datamodel/dmelement.h" |
|
#include "vmttool.h" |
|
#include "materialsystem/imaterialsystem.h" |
|
#include "materialsystem/ishader.h" |
|
#include "toolutils/enginetools_int.h" |
|
#include "filesystem.h" |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Standard properties |
|
//----------------------------------------------------------------------------- |
|
struct StandardParam_t |
|
{ |
|
const char *m_pParamName; |
|
ShaderParamType_t m_ParamType; |
|
const char *m_pDefaultValue; |
|
const char *m_pWidgetType; |
|
const char *m_pTextType; |
|
}; |
|
|
|
// NOTE: All entries in here must have all-lowercase param names! |
|
static StandardParam_t g_pStandardParams[] = |
|
{ |
|
{ "$surfaceprop", SHADER_PARAM_TYPE_STRING, "default", "surfacepropertypicker", "surfacePropertyName" }, |
|
{ "%detailtype", SHADER_PARAM_TYPE_STRING, "", "detailtypepicker", "detailTypeName" }, |
|
{ "%compilesky", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compilehint", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compileskip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compileorigin", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compileclip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%playerclip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compilenpcclip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compilenochop", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compiletrigger", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compilenolight", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compileplayercontrolclip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compileladder", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compilewet", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compilenodraw", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compileinvisible", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compilenonsolid", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compiledetail", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compilewater", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ "%compileslime", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL }, |
|
{ NULL, SHADER_PARAM_TYPE_BOOL, NULL, NULL, NULL } |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor |
|
//----------------------------------------------------------------------------- |
|
CVMTDoc::CVMTDoc( IVMTDocCallback *pCallback ) : m_pCallback( pCallback ) |
|
{ |
|
m_hRoot = NULL; |
|
m_pFileName[0] = 0; |
|
m_bDirty = false; |
|
m_pCurrentIShader = NULL; |
|
|
|
KeyValues *pKeyValues = new KeyValues( "Wireframe" ); |
|
m_pScratchMaterial.Init( "VMT Preview", pKeyValues ); |
|
g_pDataModel->InstallNotificationCallback( this ); |
|
} |
|
|
|
CVMTDoc::~CVMTDoc() |
|
{ |
|
if ( m_hRoot.Get() ) |
|
{ |
|
RemoveAllShaderParams( m_hRoot ); |
|
} |
|
g_pDataModel->RemoveNotificationCallback( this ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Inherited from INotifyUI |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags ) |
|
{ |
|
OnDataChanged( pReason, nNotifySource, nNotifyFlags ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Gets the file name |
|
//----------------------------------------------------------------------------- |
|
const char *CVMTDoc::GetFileName() |
|
{ |
|
return m_pFileName; |
|
} |
|
|
|
void CVMTDoc::SetFileName( const char *pFileName ) |
|
{ |
|
Q_strncpy( m_pFileName, pFileName, sizeof( m_pFileName ) ); |
|
Q_FixSlashes( m_pFileName ); |
|
SetDirty( true ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Dirty bits |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::SetDirty( bool bDirty ) |
|
{ |
|
m_bDirty = bDirty; |
|
} |
|
|
|
bool CVMTDoc::IsDirty() const |
|
{ |
|
return m_bDirty; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Creates the root element |
|
//----------------------------------------------------------------------------- |
|
bool CVMTDoc::CreateRootElement() |
|
{ |
|
Assert( !m_hRoot.Get() ); |
|
|
|
DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( GetFileName() ); |
|
|
|
// Create the main element |
|
m_hRoot = g_pDataModel->CreateElement( "DmElement", GetFileName(), fileid ); |
|
if ( m_hRoot == DMELEMENT_HANDLE_INVALID ) |
|
return false; |
|
|
|
g_pDataModel->SetFileRoot( fileid, m_hRoot ); |
|
|
|
// Each VMT list needs to have an editortype associated with it so it displays nicely in editors |
|
m_hRoot->SetValue( "editorType", "vmt" ); |
|
m_hRoot->AddAttribute( "proxies", AT_ELEMENT_ARRAY ); |
|
m_hRoot->AddAttribute( "fallbacks", AT_ELEMENT_ARRAY ); |
|
|
|
m_pCallback->RemoveAllToolParameters(); |
|
|
|
// Add standard parameters |
|
for ( int i = 0; g_pStandardParams[i].m_pParamName; ++i ) |
|
{ |
|
AddNewShaderParam( m_hRoot, g_pStandardParams[i].m_pParamName, g_pStandardParams[i].m_ParamType, |
|
g_pStandardParams[i].m_pDefaultValue ); |
|
|
|
if ( g_pStandardParams[i].m_pParamName[0] == '%' ) |
|
{ |
|
m_pCallback->AddToolParameter( g_pStandardParams[i].m_pParamName, g_pStandardParams[i].m_pWidgetType, g_pStandardParams[i].m_pTextType ); |
|
} |
|
else if ( g_pStandardParams[i].m_pWidgetType || g_pStandardParams[i].m_pTextType ) |
|
{ |
|
m_pCallback->RemoveShaderParameter( g_pStandardParams[i].m_pParamName ); |
|
m_pCallback->AddShaderParameter( g_pStandardParams[i].m_pParamName, g_pStandardParams[i].m_pWidgetType, g_pStandardParams[i].m_pTextType ); |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Creates a new VMT |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::CreateNew() |
|
{ |
|
Assert( !m_hRoot.Get() ); |
|
|
|
// This is not undoable |
|
CAppDisableUndoScopeGuard guard( "CVMTDoc::CreateNew", NOTIFY_CHANGE_OTHER ); |
|
|
|
Q_strncpy( m_pFileName, "untitled", sizeof( m_pFileName ) ); |
|
|
|
// Create the main element |
|
if ( !CreateRootElement() ) |
|
return; |
|
|
|
m_pPreviewMaterial.Init( m_pScratchMaterial ); |
|
|
|
SetShader( "wireframe" ); |
|
SetDirty( false ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Copies VMT parameters into the root |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::CopyParamsFromVMT( CDmElement *pVMT ) |
|
{ |
|
// First, set the shader parameters |
|
SetShader( pVMT->GetValueString( "shader" ) ); |
|
|
|
// Now, copy the shader parameters over |
|
CDmAttribute* pSrc; |
|
CDmAttribute* pDst; |
|
for ( pSrc = pVMT->FirstAttribute(); pSrc; pSrc = pSrc->NextAttribute() ) |
|
{ |
|
// Only copy shader parameters |
|
if ( !IsShaderParam( pSrc ) ) |
|
continue; |
|
|
|
// Adds the attribute if it doesn't exist |
|
const char *pSrcName = pSrc->GetName(); |
|
if ( !m_hRoot->HasAttribute( pSrcName ) ) |
|
{ |
|
m_hRoot->AddAttribute( pSrcName, pSrc->GetType() ); |
|
} |
|
pDst = m_hRoot->GetAttribute( pSrcName ); |
|
pDst->AddFlag( FATTRIB_USERDEFINED ); |
|
|
|
DmAttributeType_t srcType = pSrc->GetType(); |
|
DmAttributeType_t dstType = pDst->GetType(); |
|
if ( dstType == srcType ) |
|
{ |
|
pDst->SetValue( pSrc ); |
|
continue; |
|
} |
|
|
|
// Certain type conversions are allowed |
|
switch( dstType ) |
|
{ |
|
case AT_BOOL: |
|
if ( srcType == AT_INT ) |
|
{ |
|
pDst->SetValue( pSrc ); |
|
} |
|
break; |
|
|
|
case AT_INT: |
|
if ( srcType == AT_BOOL ) |
|
{ |
|
pDst->SetValue( pSrc ); |
|
} |
|
break; |
|
|
|
case AT_COLOR: |
|
if ( srcType == AT_VECTOR3 ) |
|
{ |
|
Color c; |
|
int r, g, b; |
|
Vector v = pSrc->GetValue<Vector>( ); |
|
v *= 255.0f; |
|
r = clamp( v[0], 0, 255 ); |
|
g = clamp( v[1], 0, 255 ); |
|
b = clamp( v[2], 0, 255 ); |
|
c.SetColor( r, g, b, 255 ); |
|
pDst->SetValue( c ); |
|
} |
|
break; |
|
} |
|
} |
|
|
|
// Any shader parameter that isn't in the VMT make undefined |
|
for ( pDst = m_hRoot->FirstAttribute(); pDst; pDst = pDst->NextAttribute() ) |
|
{ |
|
if ( !IsShaderParam( pDst ) ) |
|
continue; |
|
|
|
if ( !pVMT->HasAttribute( pDst->GetName() ) ) |
|
{ |
|
// Special hack for alpha + colors |
|
if ( !Q_stricmp( pDst->GetName(), "$alpha" ) ) |
|
{ |
|
pDst->SetValue( 1.0f ); |
|
} |
|
else if ( pDst->GetType() == AT_COLOR ) |
|
{ |
|
Color c( 255, 255, 255, 255 ); |
|
pDst->SetValue( c ); |
|
} |
|
else |
|
{ |
|
pDst->SetToDefaultValue(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Hooks the preview to an existing material, if there is one |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::SetupPreviewMaterial( ) |
|
{ |
|
// Extract a material name from the material |
|
char pLocalName[MAX_PATH]; |
|
|
|
// relative paths can be passed in for in-game material picking |
|
if ( !g_pFileSystem->FullPathToRelativePath( m_pFileName, pLocalName, sizeof(pLocalName) ) ) |
|
{ |
|
Q_strcpy( pLocalName, m_pFileName ); |
|
} |
|
|
|
if ( Q_strnicmp( pLocalName, "materials", 9 ) ) |
|
goto noMaterialConnection; |
|
|
|
// Skip the '/' also |
|
char pMaterialName[MAX_PATH]; |
|
Q_StripExtension( pLocalName + 10, pMaterialName, sizeof(pMaterialName) ); |
|
IMaterial *pMaterial = g_pMaterialSystem->FindMaterial( pMaterialName, "Editable material", false ); |
|
if ( !pMaterial || pMaterial->IsErrorMaterial() ) |
|
goto noMaterialConnection; |
|
|
|
m_pPreviewMaterial.Init( pMaterial ); |
|
return; |
|
|
|
noMaterialConnection: |
|
m_pPreviewMaterial.Init( m_pScratchMaterial ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Saves/loads from file |
|
//----------------------------------------------------------------------------- |
|
bool CVMTDoc::LoadFromFile( const char *pFileName ) |
|
{ |
|
Assert( !m_hRoot.Get() ); |
|
|
|
SetDirty( false ); |
|
|
|
Q_strncpy( m_pFileName, pFileName, sizeof( m_pFileName ) ); |
|
if ( !m_pFileName[0] ) |
|
return false; |
|
|
|
// This is not undoable |
|
CAppDisableUndoScopeGuard guard( "CVMTDoc::LoadFromFile", NOTIFY_CHANGE_OTHER ); |
|
|
|
// Create the main element |
|
if ( !CreateRootElement() ) |
|
return false; |
|
|
|
// change the filename of all the elements under the root, so we can unload the imported elements later |
|
DmFileId_t rootFileId = g_pDataModel->GetFileId( m_pFileName ); |
|
g_pDataModel->SetFileName( rootFileId, "<temp>" ); |
|
|
|
// This will allow us to edit in context! |
|
SetupPreviewMaterial( ); |
|
|
|
CDmElement *pIVMT = NULL; |
|
g_pDataModel->RestoreFromFile( m_pFileName, NULL, "vmt", &pIVMT ); |
|
CDmElement *pVMT = CastElement< CDmElement >( pIVMT ); |
|
if ( !pVMT ) |
|
return false; |
|
|
|
// FIXME: This is necessary so that all shader parameters appear in |
|
// the same order, with the same type, as what you'd get using File->New. |
|
// If we added a dependency to the material system into dmserializers, |
|
// we could avoid this work here (I think!). |
|
CopyParamsFromVMT( pVMT ); |
|
|
|
// unload the imported elements and change the root's filename back |
|
DmFileId_t vmtFileId = g_pDataModel->GetFileId( m_pFileName ); |
|
g_pDataModel->RemoveFileId( vmtFileId ); |
|
g_pDataModel->SetFileName( rootFileId, m_pFileName ); |
|
|
|
SetDirty( false ); |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Prior to saving to disk, extract all shader parameters which == the default |
|
//----------------------------------------------------------------------------- |
|
CDmElement* CVMTDoc::ExtractDefaultParameters( ) |
|
{ |
|
CDmElement *pMaterial = m_hRoot->Copy( ); |
|
|
|
CDmAttribute* pAttribute = pMaterial->FirstAttribute(); |
|
CDmAttribute* pNextAttribute = NULL; |
|
for ( ; pAttribute; pAttribute = pNextAttribute ) |
|
{ |
|
pNextAttribute = pAttribute->NextAttribute(); |
|
|
|
const char *pShaderParam = pAttribute->GetName(); |
|
|
|
// Check for standard params |
|
int i; |
|
for ( i = 0; g_pStandardParams[i].m_pParamName != NULL; ++i ) |
|
{ |
|
if ( !Q_stricmp( g_pStandardParams[i].m_pParamName, pShaderParam ) ) |
|
{ |
|
char temp[512]; |
|
CUtlBuffer buf( temp, sizeof(temp), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE ); |
|
pAttribute->Serialize( buf ); |
|
|
|
if ( !Q_stricmp( (char*)buf.Base(), g_pStandardParams[i].m_pDefaultValue ) ) |
|
{ |
|
// Buffers match! Therefore it's still using the default parameter |
|
pMaterial->RemoveAttributeByPtr( pAttribute ); |
|
} |
|
break; |
|
} |
|
} |
|
|
|
// Standard attribute found, continue |
|
if ( g_pStandardParams[i].m_pParamName ) |
|
continue; |
|
|
|
// Only remove shader parameters |
|
if ( !IsShaderParam( pAttribute ) ) |
|
continue; |
|
|
|
// Remove flags whose value is 0 |
|
int nCount = g_pMaterialSystem->ShaderFlagCount(); |
|
for ( i = 0; i < nCount; ++i ) |
|
{ |
|
const char *pFlagName = g_pMaterialSystem->ShaderFlagName( i ); |
|
if ( !Q_stricmp( pShaderParam, pFlagName ) ) |
|
break; |
|
} |
|
|
|
// It's a flag! Remove the attribute if its value is 0 |
|
if ( i != nCount ) |
|
{ |
|
if ( pAttribute->GetValue<bool>( ) == 0 ) |
|
{ |
|
pMaterial->RemoveAttributeByPtr( pAttribute ); |
|
} |
|
continue; |
|
} |
|
|
|
// FIXME: We can't do this.. the defaults in the strings need to be changed to |
|
// make it so they actually match the true defaults |
|
continue; |
|
|
|
// Remove parameters which match the default value |
|
nCount = m_pCurrentIShader->GetNumParams(); |
|
for ( i = 0; i < nCount; ++i ) |
|
{ |
|
// FIXME: Check type matches |
|
if ( Q_stricmp( pShaderParam, m_pCurrentIShader->GetParamName( i ) ) ) |
|
continue; |
|
|
|
// NOTE: This isn't particularly efficient. Too bad! |
|
// It's hard to do efficiently owing to all the import conversion |
|
char temp[512]; |
|
char temp2[512]; |
|
CUtlBuffer buf( temp, sizeof(temp), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE ); |
|
CUtlBuffer buf2( temp2, sizeof(temp2), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE ); |
|
pAttribute->Serialize( buf ); |
|
SetAttributeValueFromDefault( pMaterial, pAttribute, m_pCurrentIShader->GetParamDefault( i ) ); |
|
pAttribute->Serialize( buf2 ); |
|
|
|
if ( ( buf.TellMaxPut() == buf2.TellMaxPut() ) && !memcmp( buf.Base(), buf2.Base(), buf.TellMaxPut() ) ) |
|
{ |
|
// Buffers match! Therefore it's still using the default parameter |
|
pMaterial->RemoveAttributeByPtr( pAttribute ); |
|
} |
|
else |
|
{ |
|
// Restore the actual value |
|
pAttribute->Unserialize( buf ); |
|
} |
|
break; |
|
} |
|
} |
|
|
|
return pMaterial; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Saves to disk |
|
//----------------------------------------------------------------------------- |
|
bool CVMTDoc::SaveToFile( ) |
|
{ |
|
if ( m_hRoot.Get() && m_pFileName && m_pFileName[0] ) |
|
{ |
|
CDisableUndoScopeGuard guard; |
|
CDmElement *pSaveRoot = ExtractDefaultParameters(); |
|
bool bOk = g_pDataModel->SaveToFile( m_pFileName, NULL, "keyvalues", "vmt", pSaveRoot ); |
|
DestroyElement( pSaveRoot, TD_DEEP ); |
|
if ( !bOk ) |
|
return false; |
|
} |
|
|
|
SetDirty( false ); |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Finds a shader |
|
//----------------------------------------------------------------------------- |
|
IShader *CVMTDoc::FindShader( const char *pShaderName ) |
|
{ |
|
int nCount = g_pMaterialSystem->ShaderCount(); |
|
IShader **ppShaderList = (IShader**)_alloca( nCount * sizeof(IShader*) ); |
|
g_pMaterialSystem->GetShaders( 0, nCount, ppShaderList ); |
|
for ( int i = 0; i < nCount; ++i ) |
|
{ |
|
if ( !Q_stricmp( pShaderName, ppShaderList[i]->GetName() ) ) |
|
return ppShaderList[i]; |
|
} |
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Is this attribute a shader parameter? |
|
//----------------------------------------------------------------------------- |
|
bool CVMTDoc::IsShaderParam( CDmAttribute* pAttribute ) |
|
{ |
|
const char *pName = pAttribute->GetName(); |
|
|
|
// Shader params start with a $ or % |
|
if ( pName[0] != '$' && pName[0] != '%' ) |
|
return false; |
|
|
|
// Don't remove name, type, or id |
|
if ( pAttribute->IsFlagSet( FATTRIB_STANDARD ) ) |
|
return false; |
|
|
|
// All shader params have USERDEFINED set |
|
if ( !pAttribute->IsFlagSet( FATTRIB_USERDEFINED ) ) |
|
return false; |
|
|
|
// Don't remove arrays... those aren't shader parameters |
|
if ( pAttribute->GetType() == AT_ELEMENT_ARRAY ) |
|
return false; |
|
|
|
// Standard params aren't counted here |
|
for ( int i = 0; g_pStandardParams[i].m_pParamName; ++i ) |
|
{ |
|
if ( !Q_stricmp( g_pStandardParams[i].m_pParamName, pName ) ) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Remove all shader parameters |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::RemoveAllShaderParams( CDmElement *pMaterial ) |
|
{ |
|
CDmAttribute* pAttribute; |
|
CDmAttribute* pNextAttribute = NULL; |
|
for ( pAttribute = pMaterial->FirstAttribute(); pAttribute; pAttribute = pNextAttribute ) |
|
{ |
|
pNextAttribute = pAttribute->NextAttribute(); |
|
|
|
// Only remove shader parameters |
|
if ( !IsShaderParam( pAttribute ) ) |
|
continue; |
|
|
|
m_pCallback->RemoveShaderParameter( pAttribute->GetName() ); |
|
pMaterial->RemoveAttributeByPtr( pAttribute ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Remove all shader parameters that don't exist in the new shader |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::RemoveUnusedShaderParams( CDmElement *pMaterial, IShader *pShader, IShader *pOldShader ) |
|
{ |
|
CDmAttribute* pAttribute = pMaterial->FirstAttribute(); |
|
CDmAttribute* pNextAttribute = NULL; |
|
for ( ; pAttribute; pAttribute = pNextAttribute ) |
|
{ |
|
pNextAttribute = pAttribute->NextAttribute(); |
|
|
|
// Only remove shader parameters |
|
if ( !IsShaderParam( pAttribute ) ) |
|
continue; |
|
|
|
// Don't remove flags |
|
int nCount = g_pMaterialSystem->ShaderFlagCount(); |
|
int i; |
|
for ( i = 0; i < nCount; ++i ) |
|
{ |
|
const char *pFlagName = g_pMaterialSystem->ShaderFlagName( i ); |
|
if ( !Q_stricmp( pAttribute->GetName(), pFlagName ) ) |
|
break; |
|
} |
|
|
|
if ( i != nCount ) |
|
continue; |
|
|
|
const char *pShaderParam = pAttribute->GetName(); |
|
|
|
// Remove parameters we've currently got but which don't exist in the new shader |
|
nCount = pShader->GetNumParams(); |
|
for ( i = 0; i < nCount; ++i ) |
|
{ |
|
// FIXME: Check type matches |
|
if ( !Q_stricmp( pShaderParam, pShader->GetParamName( i ) ) ) |
|
break; |
|
} |
|
|
|
// No match? Remove it! |
|
if ( i == nCount ) |
|
{ |
|
m_pCallback->RemoveShaderParameter( pAttribute->GetName() ); |
|
pMaterial->RemoveAttributeByPtr( pAttribute ); |
|
continue; |
|
} |
|
|
|
// Remove parameters from the old shader which match the default value |
|
// This will make the default values update to the new shader's defaults |
|
if ( pOldShader ) |
|
{ |
|
nCount = pOldShader->GetNumParams(); |
|
for ( i = 0; i < nCount; ++i ) |
|
{ |
|
// FIXME: Check type matches |
|
if ( Q_stricmp( pShaderParam, pOldShader->GetParamName( i ) ) ) |
|
continue; |
|
|
|
// NOTE: This isn't particularly efficient. Too bad! |
|
// It's hard to do efficiently owing to all the import conversion |
|
char temp1[512]; |
|
char temp2[512]; |
|
CUtlBuffer buf1( temp1, sizeof(temp1), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE ); |
|
CUtlBuffer buf2( temp2, sizeof(temp2), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE ); |
|
pAttribute->Serialize( buf1 ); |
|
SetAttributeValueFromDefault( pMaterial, pAttribute, pOldShader->GetParamDefault( i ) ); |
|
pAttribute->Serialize( buf2 ); |
|
|
|
if ( ( buf1.TellMaxPut() == buf2.TellMaxPut() ) && !memcmp( buf1.Base(), buf2.Base(), buf1.TellMaxPut() ) ) |
|
{ |
|
// Buffers match! Therefore it's still using the default parameter |
|
m_pCallback->RemoveShaderParameter( pAttribute->GetName() ); |
|
pMaterial->RemoveAttributeByPtr( pAttribute ); |
|
} |
|
else |
|
{ |
|
pAttribute->Unserialize( buf1 ); |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Add attribute for shader parameter |
|
//----------------------------------------------------------------------------- |
|
CDmAttribute* CVMTDoc::AddAttributeForShaderParameter( CDmElement *pMaterial, const char *pParamName, ShaderParamType_t paramType ) |
|
{ |
|
CDmAttribute *pAttribute = NULL; |
|
switch ( paramType ) |
|
{ |
|
case SHADER_PARAM_TYPE_INTEGER: |
|
pAttribute = pMaterial->AddAttribute( pParamName, AT_INT ); |
|
break; |
|
|
|
case SHADER_PARAM_TYPE_BOOL: |
|
pAttribute = pMaterial->AddAttribute( pParamName, AT_BOOL ); |
|
break; |
|
|
|
case SHADER_PARAM_TYPE_FLOAT: |
|
pAttribute = pMaterial->AddAttribute( pParamName, AT_FLOAT ); |
|
break; |
|
|
|
case SHADER_PARAM_TYPE_STRING: |
|
pAttribute = pMaterial->AddAttribute( pParamName, AT_STRING ); |
|
break; |
|
|
|
case SHADER_PARAM_TYPE_COLOR: |
|
pAttribute = pMaterial->AddAttribute( pParamName, AT_COLOR ); |
|
break; |
|
|
|
case SHADER_PARAM_TYPE_VEC2: |
|
pAttribute = pMaterial->AddAttribute( pParamName, AT_VECTOR2 ); |
|
break; |
|
|
|
case SHADER_PARAM_TYPE_VEC3: |
|
pAttribute = pMaterial->AddAttribute( pParamName, AT_VECTOR3 ); |
|
break; |
|
|
|
case SHADER_PARAM_TYPE_VEC4: |
|
pAttribute = pMaterial->AddAttribute( pParamName, AT_VECTOR4 ); |
|
break; |
|
|
|
case SHADER_PARAM_TYPE_FOURCC: |
|
Assert( 0 ); |
|
break; |
|
|
|
case SHADER_PARAM_TYPE_MATRIX: |
|
pAttribute = pMaterial->AddAttribute( pParamName, AT_VMATRIX ); |
|
break; |
|
|
|
case SHADER_PARAM_TYPE_TEXTURE: |
|
pAttribute = pMaterial->AddAttribute( pParamName, AT_STRING ); |
|
m_pCallback->AddShaderParameter( pParamName, "vtfpicker", "vtfName" ); |
|
break; |
|
|
|
case SHADER_PARAM_TYPE_MATERIAL: |
|
pAttribute = pMaterial->AddAttribute( pParamName, AT_STRING ); |
|
m_pCallback->AddShaderParameter( pParamName, "vmtpicker", "vmtName" ); |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
|
|
if ( pAttribute ) |
|
{ |
|
pAttribute->AddFlag( FATTRIB_USERDEFINED ); |
|
} |
|
|
|
return pAttribute; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// A couple methods to set vmatrix param values from strings (OLD METHOD!) |
|
//----------------------------------------------------------------------------- |
|
bool CVMTDoc::SetVMatrixParamValue( CDmAttribute *pAttribute, const char *pValue ) |
|
{ |
|
// FIXME: Change default strings to match DME? |
|
// Then we could remove this crap |
|
VMatrix mat; |
|
int count = sscanf( pValue, " [ %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ]", |
|
&mat.m[0][0], &mat.m[0][1], &mat.m[0][2], &mat.m[0][3], |
|
&mat.m[1][0], &mat.m[1][1], &mat.m[1][2], &mat.m[1][3], |
|
&mat.m[2][0], &mat.m[2][1], &mat.m[2][2], &mat.m[2][3], |
|
&mat.m[3][0], &mat.m[3][1], &mat.m[3][2], &mat.m[3][3] ); |
|
if (count == 16) |
|
{ |
|
pAttribute->SetValue( mat ); |
|
return true; |
|
} |
|
|
|
Vector2D scale, center; |
|
float angle; |
|
Vector2D translation; |
|
count = sscanf( pValue, " center %f %f scale %f %f rotate %f translate %f %f", |
|
¢er.x, ¢er.y, &scale.x, &scale.y, &angle, &translation.x, &translation.y ); |
|
if (count != 7) |
|
return false; |
|
|
|
VMatrix temp; |
|
MatrixBuildTranslation( mat, -center.x, -center.y, 0.0f ); |
|
MatrixBuildScale( temp, scale.x, scale.y, 1.0f ); |
|
MatrixMultiply( temp, mat, mat ); |
|
MatrixBuildRotateZ( temp, angle ); |
|
MatrixMultiply( temp, mat, mat ); |
|
MatrixBuildTranslation( temp, center.x + translation.x, center.y + translation.y, 0.0f ); |
|
MatrixMultiply( temp, mat, mat ); |
|
pAttribute->SetValue( mat ); |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// A couple methods to set vmatrix param values from strings (OLD METHOD!) |
|
//----------------------------------------------------------------------------- |
|
bool CVMTDoc::SetVector2DParamValue( CDmAttribute *pAttribute, const char *pValue ) |
|
{ |
|
Vector2D vec; |
|
int count = sscanf( pValue, " [ %f %f ]", &vec[0], &vec[1] ); |
|
if ( count == 2 ) |
|
{ |
|
pAttribute->SetValue( vec ); |
|
return true; |
|
} |
|
|
|
count = sscanf( pValue, " { %f %f }", &vec[0], &vec[1] ); |
|
if ( count == 2 ) |
|
{ |
|
vec /= 255.0f; |
|
pAttribute->SetValue( vec ); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// A couple methods to set vmatrix param values from strings (OLD METHOD!) |
|
//----------------------------------------------------------------------------- |
|
bool CVMTDoc::SetVector3DParamValue( CDmAttribute *pAttribute, const char *pValue ) |
|
{ |
|
Vector vec; |
|
int count = sscanf( pValue, " [ %f %f %f ]", &vec[0], &vec[1], &vec[2] ); |
|
if ( count == 3 ) |
|
{ |
|
pAttribute->SetValue( vec ); |
|
return true; |
|
} |
|
|
|
count = sscanf( pValue, " { %f %f %f }", &vec[0], &vec[1], &vec[2] ); |
|
if ( count == 3 ) |
|
{ |
|
vec /= 255.0f; |
|
pAttribute->SetValue( vec ); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// A couple methods to set vmatrix param values from strings (OLD METHOD!) |
|
//----------------------------------------------------------------------------- |
|
bool CVMTDoc::SetVector4DParamValue( CDmAttribute *pAttribute, const char *pValue ) |
|
{ |
|
Vector4D vec; |
|
int count = sscanf( pValue, " [ %f %f %f %f ]", &vec[0], &vec[1], &vec[2], &vec[3] ); |
|
if ( count == 4 ) |
|
{ |
|
pAttribute->SetValue( vec ); |
|
return true; |
|
} |
|
|
|
count = sscanf( pValue, " { %f %f %f %f }", &vec[0], &vec[1], &vec[2], &vec[3] ); |
|
if ( count == 4 ) |
|
{ |
|
vec /= 255.0f; |
|
pAttribute->SetValue( vec ); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// A couple methods to set vmatrix param values from strings (OLD METHOD!) |
|
//----------------------------------------------------------------------------- |
|
bool CVMTDoc::SetColorParamValue( CDmAttribute *pAttribute, const char *pValue ) |
|
{ |
|
Color c; |
|
int r, g, b; |
|
Vector vec; |
|
int count = sscanf( pValue, " [ %f %f %f ]", &vec[0], &vec[1], &vec[2] ); |
|
if ( count == 3 ) |
|
{ |
|
vec *= 255.0f; |
|
r = clamp( vec[0], 0, 255 ); |
|
g = clamp( vec[1], 0, 255 ); |
|
b = clamp( vec[2], 0, 255 ); |
|
c.SetColor( r, g, b, 255 ); |
|
pAttribute->SetValue( c ); |
|
return true; |
|
} |
|
|
|
count = sscanf( pValue, " { %d %d %d }", &r, &g, &b ); |
|
if ( count == 3 ) |
|
{ |
|
c.SetColor( r, g, b, 255 ); |
|
pAttribute->SetValue( c ); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets an attribute value from the shader param default |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::SetAttributeValueFromDefault( CDmElement *pMaterial, CDmAttribute *pAttribute, const char *pValue ) |
|
{ |
|
// FIXME: Change default strings to match DME? |
|
// Then we could remove this crap |
|
switch ( pAttribute->GetType() ) |
|
{ |
|
case AT_VMATRIX: |
|
if ( SetVMatrixParamValue( pAttribute, pValue ) ) |
|
return; |
|
break; |
|
case AT_COLOR: |
|
if ( SetColorParamValue( pAttribute, pValue ) ) |
|
return; |
|
break; |
|
case AT_VECTOR2: |
|
if ( SetVector2DParamValue( pAttribute, pValue ) ) |
|
return; |
|
break; |
|
case AT_VECTOR3: |
|
if ( SetVector3DParamValue( pAttribute, pValue ) ) |
|
return; |
|
break; |
|
case AT_VECTOR4: |
|
if ( SetVector4DParamValue( pAttribute, pValue ) ) |
|
return; |
|
break; |
|
} |
|
|
|
pMaterial->SetValueFromString( pAttribute->GetName(), pValue ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Add a single shader parameter if it doesn't exist |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::AddNewShaderParam( CDmElement *pMaterial, const char *pParamName, ShaderParamType_t paramType, const char *pValue ) |
|
{ |
|
char temp[512]; |
|
Q_strncpy( temp, pParamName, sizeof(temp) ); |
|
Q_strlower( temp ); |
|
pParamName = temp; |
|
|
|
CDmAttribute* pAttribute = NULL; |
|
for ( pAttribute = pMaterial->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() ) |
|
{ |
|
// Don't bother testing against name, type, or id |
|
if ( pAttribute->IsFlagSet( FATTRIB_STANDARD ) ) |
|
continue; |
|
|
|
const char *pAttributeName = pAttribute->GetName(); |
|
if ( !Q_stricmp( pAttributeName, pParamName ) ) |
|
return; |
|
} |
|
|
|
// No match? Add it! |
|
pAttribute = AddAttributeForShaderParameter( pMaterial, pParamName, paramType ); |
|
if ( pAttribute ) |
|
{ |
|
SetAttributeValueFromDefault( pMaterial, pAttribute, pValue ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Add all shader parameters that don't currently exist |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::AddNewShaderParams( CDmElement *pMaterial, IShader *pShader ) |
|
{ |
|
// First add all flags |
|
m_pCallback->RemoveAllFlagParameters(); |
|
int nCount = g_pMaterialSystem->ShaderFlagCount(); |
|
int i; |
|
for ( i = 0; i < nCount; ++i ) |
|
{ |
|
const char *pParamName = g_pMaterialSystem->ShaderFlagName( i ); |
|
AddNewShaderParam( pMaterial, pParamName, SHADER_PARAM_TYPE_BOOL, "0" ); |
|
m_pCallback->AddFlagParameter( pParamName ); |
|
} |
|
|
|
// Next add all shader-specific parameters |
|
nCount = pShader->GetNumParams(); |
|
for ( i = 0; i < nCount; ++i ) |
|
{ |
|
const char *pParamName = pShader->GetParamName( i ); |
|
|
|
// Don't add parameters that don't want to be editable |
|
if ( pShader->GetParamFlags( i ) & SHADER_PARAM_NOT_EDITABLE ) |
|
continue; |
|
|
|
ShaderParamType_t paramType = pShader->GetParamType( i ); |
|
const char *pDefault = pShader->GetParamDefault( i ); |
|
AddNewShaderParam( pMaterial, pParamName, paramType, pDefault ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets shader parameters to the default for that shader |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::SetParamsToDefault() |
|
{ |
|
// This is undoable |
|
CAppUndoScopeGuard guard( 0, "Set Params to Default", "Set Params to Default" ); |
|
|
|
// Next add all shader-specific parameters |
|
int nCount = m_pCurrentIShader->GetNumParams(); |
|
for ( int i = 0; i < nCount; ++i ) |
|
{ |
|
const char *pParamName = m_pCurrentIShader->GetParamName( i ); |
|
|
|
// Don't set parameters that don't want to be editable |
|
if ( m_pCurrentIShader->GetParamFlags( i ) & SHADER_PARAM_NOT_EDITABLE ) |
|
continue; |
|
|
|
char pAttributeName[512]; |
|
Q_strncpy( pAttributeName, pParamName, sizeof(pAttributeName) ); |
|
Q_strlower( pAttributeName ); |
|
|
|
if ( !m_hRoot->HasAttribute( pAttributeName ) ) |
|
continue; |
|
|
|
CDmAttribute *pAttribute = m_hRoot->GetAttribute( pAttributeName ); |
|
const char *pDefault = m_pCurrentIShader->GetParamDefault( i ); |
|
SetAttributeValueFromDefault( m_hRoot, pAttribute, pDefault ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the shader in the material |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::SetShader( const char *pShaderName ) |
|
{ |
|
// No change? don't bother |
|
if ( !Q_stricmp( m_CurrentShader, pShaderName ) ) |
|
return; |
|
|
|
m_CurrentShader = pShaderName; |
|
|
|
// This is undoable |
|
CAppUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Set Shader", "Set Shader" ); |
|
|
|
char pActualShaderName[512]; |
|
g_pMaterialSystem->GetShaderFallback( pShaderName, pActualShaderName, sizeof(pActualShaderName) ); |
|
|
|
m_hRoot->SetValue( "shader", pShaderName ); |
|
|
|
// First, find the shader |
|
IShader *pShader = FindShader( pActualShaderName ); |
|
|
|
// Remove all shader parameters that don't exist in the new shader |
|
// And also remove shader parameters that do match the default value |
|
RemoveUnusedShaderParams( m_hRoot, pShader, m_pCurrentIShader ); |
|
|
|
// Add all shader parameters that don't currently exist |
|
AddNewShaderParams( m_hRoot, pShader ); |
|
|
|
m_pCurrentIShader = pShader; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Gets the preview material |
|
//----------------------------------------------------------------------------- |
|
IMaterial *CVMTDoc::GetPreviewMaterial() |
|
{ |
|
return m_pPreviewMaterial; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Updates the preview material |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::UpdatePreviewMaterial() |
|
{ |
|
if ( !m_hRoot.Get() ) |
|
return; |
|
|
|
// Update all shader parameters |
|
SetShader( m_hRoot->GetValueString( "shader" ) ); |
|
|
|
// Use the file conversion to write to a text format |
|
char buf[1024]; |
|
CUtlBuffer vmtBuf( buf, sizeof(buf), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE ); |
|
g_pDataModel->Serialize( vmtBuf, "vmt", "vmt", m_hRoot ); |
|
|
|
// Now use the text format to create a keyvalues |
|
KeyValues *pVMTKeyValues = new KeyValues( "ShaderName" ); |
|
pVMTKeyValues->LoadFromBuffer( "VMT Preview", vmtBuf, g_pFileSystem, "GAME" ); |
|
|
|
// Finally, hook the keyvalues into the material. |
|
m_pPreviewMaterial->SetShaderAndParams( pVMTKeyValues ); |
|
pVMTKeyValues->deleteThis(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the root object |
|
//----------------------------------------------------------------------------- |
|
CDmElement *CVMTDoc::GetRootObject() |
|
{ |
|
return m_hRoot; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Called when data changes |
|
//----------------------------------------------------------------------------- |
|
void CVMTDoc::OnDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags ) |
|
{ |
|
SetDirty( nNotifyFlags & NOTIFY_SETDIRTYFLAG ? true : false ); |
|
UpdatePreviewMaterial(); |
|
m_pCallback->OnDocChanged( pReason, nNotifySource, nNotifyFlags ); |
|
}
|
|
|