diff options
Diffstat (limited to 'tools/vmt/vmtdoc.cpp')
| -rw-r--r-- | tools/vmt/vmtdoc.cpp | 1078 |
1 files changed, 1078 insertions, 0 deletions
diff --git a/tools/vmt/vmtdoc.cpp b/tools/vmt/vmtdoc.cpp new file mode 100644 index 0000000..665d932 --- /dev/null +++ b/tools/vmt/vmtdoc.cpp @@ -0,0 +1,1078 @@ +//========= 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 ); +} |