@ -12,16 +12,11 @@
# include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
# include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
# include <Atom/RPI.Edit/Material/MaterialUtils.h>
# include <Atom/RPI.Edit/Material/MaterialUtils.h>
# include <Atom/RPI.Public/Material/Material.h>
# include <Atom/RPI.Public/Material/Material.h>
# include <Atom/RPI.Public/Image/ImageSystemInterface.h>
# include <Atom/RPI.Reflect/Image/Image.h>
# include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
# include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
# include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
# include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
# include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
# include <AtomCore/Instance/Instance.h>
# include <AtomCore/Instance/Instance.h>
# include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
# include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
# include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
# include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
# include <AzToolsFramework/API/EditorAssetSystemAPI.h>
# include <AzToolsFramework/SourceControl/SourceControlAPI.h>
# include <Document/MaterialDocument.h>
# include <Document/MaterialDocument.h>
namespace MaterialEditor
namespace MaterialEditor
@ -30,14 +25,12 @@ namespace MaterialEditor
: AtomToolsFramework : : AtomToolsDocument ( )
: AtomToolsFramework : : AtomToolsDocument ( )
{
{
MaterialDocumentRequestBus : : Handler : : BusConnect ( m_id ) ;
MaterialDocumentRequestBus : : Handler : : BusConnect ( m_id ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentCreated , m_id ) ;
}
}
MaterialDocument : : ~ MaterialDocument ( )
MaterialDocument : : ~ MaterialDocument ( )
{
{
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentDestroyed , m_id ) ;
MaterialDocumentRequestBus : : Handler : : BusDisconnect ( ) ;
MaterialDocumentRequestBus : : Handler : : BusDisconnect ( ) ;
Clear ( ) ;
AZ: : TickBus : : Handler : : BusDisconnect ( ) ;
}
}
AZ : : Data : : Asset < AZ : : RPI : : MaterialAsset > MaterialDocument : : GetAsset ( ) const
AZ : : Data : : Asset < AZ : : RPI : : MaterialAsset > MaterialDocument : : GetAsset ( ) const
@ -62,19 +55,16 @@ namespace MaterialEditor
const AZStd : : any & MaterialDocument : : GetPropertyValue ( const AZ : : Name & propertyId ) const
const AZStd : : any & MaterialDocument : : GetPropertyValue ( const AZ : : Name & propertyId ) const
{
{
using namespace AZ ;
using namespace RPI ;
if ( ! IsOpen ( ) )
if ( ! IsOpen ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material d ocument is not open." ) ;
AZ_Error ( " MaterialDocument " , false , " Document is not open. " ) ;
return m_invalidValue ;
return m_invalidValue ;
}
}
const auto it = m_properties . find ( propertyId ) ;
const auto it = m_properties . find ( propertyId ) ;
if ( it = = m_properties . end ( ) )
if ( it = = m_properties . end ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material d ocument property could not be found: '%s'." , propertyId . GetCStr ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " D ocument property could not be found: '%s'." , propertyId . GetCStr ( ) ) ;
return m_invalidValue ;
return m_invalidValue ;
}
}
@ -86,14 +76,14 @@ namespace MaterialEditor
{
{
if ( ! IsOpen ( ) )
if ( ! IsOpen ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material d ocument is not open." ) ;
AZ_Error ( " MaterialDocument " , false , " D ocument is not open." ) ;
return m_invalidProperty ;
return m_invalidProperty ;
}
}
const auto it = m_properties . find ( propertyId ) ;
const auto it = m_properties . find ( propertyId ) ;
if ( it = = m_properties . end ( ) )
if ( it = = m_properties . end ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material d ocument property could not be found: '%s'." , propertyId . GetCStr ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " D ocument property could not be found: '%s'." , propertyId . GetCStr ( ) ) ;
return m_invalidProperty ;
return m_invalidProperty ;
}
}
@ -105,14 +95,14 @@ namespace MaterialEditor
{
{
if ( ! IsOpen ( ) )
if ( ! IsOpen ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material d ocument is not open." ) ;
AZ_Error ( " MaterialDocument " , false , " D ocument is not open." ) ;
return false ;
return false ;
}
}
const auto it = m_propertyGroupVisibility . find ( propertyGroupFullName ) ;
const auto it = m_propertyGroupVisibility . find ( propertyGroupFullName ) ;
if ( it = = m_propertyGroupVisibility . end ( ) )
if ( it = = m_propertyGroupVisibility . end ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material d ocument property group could not be found: '%s'." , propertyGroupFullName . GetCStr ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " D ocument property group could not be found: '%s'." , propertyGroupFullName . GetCStr ( ) ) ;
return false ;
return false ;
}
}
@ -121,25 +111,21 @@ namespace MaterialEditor
void MaterialDocument : : SetPropertyValue ( const AZ : : Name & propertyId , const AZStd : : any & value )
void MaterialDocument : : SetPropertyValue ( const AZ : : Name & propertyId , const AZStd : : any & value )
{
{
using namespace AZ ;
using namespace RPI ;
if ( ! IsOpen ( ) )
if ( ! IsOpen ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material d ocument is not open." ) ;
AZ_Error ( " MaterialDocument " , false , " Document is not open. " ) ;
return ;
return ;
}
}
const auto it = m_properties . find ( propertyId ) ;
const auto it = m_properties . find ( propertyId ) ;
if ( it = = m_properties . end ( ) )
if ( it = = m_properties . end ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material d ocument property could not be found: '%s'." , propertyId . GetCStr ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " D ocument property could not be found: '%s'." , propertyId . GetCStr ( ) ) ;
return ;
return ;
}
}
// This first converts to an acceptable runtime type in case the value came from script
// This first converts to an acceptable runtime type in case the value came from script
const AZ : : RPI : : MaterialPropertyValue propertyValue =
const AZ : : RPI : : MaterialPropertyValue propertyValue = AtomToolsFramework : : ConvertToRuntimeType ( value ) ;
AtomToolsFramework : : ConvertToRuntimeType ( value ) ;
AtomToolsFramework : : DynamicProperty & property = it - > second ;
AtomToolsFramework : : DynamicProperty & property = it - > second ;
property . SetValue ( AtomToolsFramework : : ConvertToEditableType ( propertyValue ) ) ;
property . SetValue ( AtomToolsFramework : : ConvertToEditableType ( propertyValue ) ) ;
@ -149,88 +135,41 @@ namespace MaterialEditor
{
{
if ( m_materialInstance - > SetPropertyValue ( propertyIndex , propertyValue ) )
if ( m_materialInstance - > SetPropertyValue ( propertyIndex , propertyValue ) )
{
{
MaterialPropertyFlags dirtyFlags = m_materialInstance - > GetPropertyDirtyFlags ( ) ;
AZ: : RPI : : MaterialPropertyFlags dirtyFlags = m_materialInstance - > GetPropertyDirtyFlags ( ) ;
Recompile ( ) ;
Recompile ( ) ;
EditorMaterialFunctorResult result = RunEditorMaterialFunctors ( dirtyFlags ) ;
EditorMaterialFunctorResult result = RunEditorMaterialFunctors ( dirtyFlags ) ;
for ( const Name& changedPropertyGroupName : result . m_updatedPropertyGroups )
for ( const AZ: : Name& changedPropertyGroupName : result . m_updatedPropertyGroups )
{
{
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentPropertyGroupVisibilityChanged , m_id , changedPropertyGroupName , IsPropertyGroupVisible ( changedPropertyGroupName ) ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast (
& AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentPropertyGroupVisibilityChanged , m_id ,
changedPropertyGroupName , IsPropertyGroupVisible ( changedPropertyGroupName ) ) ;
}
}
for ( const Name & changedPropertyName : result . m_updatedProperties )
for ( const AZ: : Name& changedPropertyName : result . m_updatedProperties )
{
{
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentPropertyConfigModified , m_id , GetProperty ( changedPropertyName ) ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast (
& AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentPropertyConfigModified , m_id ,
GetProperty ( changedPropertyName ) ) ;
}
}
}
}
}
}
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentPropertyValueModified , m_id , property ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast (
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentModified , m_id ) ;
& AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentPropertyValueModified , m_id , property ) ;
}
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast (
& AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentModified , m_id ) ;
bool MaterialDocument : : Open ( AZStd : : string_view loadPath )
{
if ( ! OpenInternal ( loadPath ) )
{
Clear ( ) ;
AZ_Error ( " MaterialDocument " , false , " Material document could not be opened: '%s'. " , loadPath . data ( ) ) ;
return false ;
}
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentOpened , m_id ) ;
return true ;
}
bool MaterialDocument : : Reopen ( )
{
// Store history and property changes that should be reapplied after reload
auto undoHistoryToRestore = m_undoHistory ;
auto undoHistoryIndexToRestore = m_undoHistoryIndex ;
PropertyValueMap propertyValuesToRestore ;
for ( const auto & propertyPair : m_properties )
{
const AtomToolsFramework : : DynamicProperty & property = propertyPair . second ;
if ( ! AtomToolsFramework : : ArePropertyValuesEqual ( property . GetValue ( ) , property . GetConfig ( ) . m_parentValue ) )
{
propertyValuesToRestore [ property . GetId ( ) ] = property . GetValue ( ) ;
}
}
// Reopen the same document
const AZStd : : string loadPath = m_absolutePath ;
if ( ! OpenInternal ( loadPath ) )
{
Clear ( ) ;
return false ;
}
RestorePropertyValues ( propertyValuesToRestore ) ;
AZStd : : swap ( undoHistoryToRestore , m_undoHistory ) ;
AZStd : : swap ( undoHistoryIndexToRestore , m_undoHistoryIndex ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentOpened , m_id ) ;
return true ;
}
}
bool MaterialDocument : : Save ( )
bool MaterialDocument : : Save ( )
{
{
using namespace AZ ;
if ( ! AtomToolsDocument : : Save ( ) )
using namespace RPI ;
if ( ! IsOpen ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material document is not open to be saved: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
}
if ( ! IsSavable ( ) )
{
AZ_Error ( " MaterialDocument " , false , " Material types can only be saved as a child: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
return false ;
}
}
// create source data from properties
// create source data from properties
MaterialSourceData sourceData ;
AZ : : RPI : : MaterialSourceData sourceData ;
sourceData . m_materialTypeVersion = m_materialAsset - > GetMaterialTypeAsset ( ) - > GetVersion ( ) ;
sourceData . m_materialTypeVersion = m_materialAsset - > GetMaterialTypeAsset ( ) - > GetVersion ( ) ;
sourceData . m_materialType = AtomToolsFramework : : GetExteralReferencePath ( m_absolutePath , m_materialSourceData . m_materialType ) ;
sourceData . m_materialType = AtomToolsFramework : : GetExteralReferencePath ( m_absolutePath , m_materialSourceData . m_materialType ) ;
sourceData . m_parentMaterial = AtomToolsFramework : : GetExteralReferencePath ( m_absolutePath , m_materialSourceData . m_parentMaterial ) ;
sourceData . m_parentMaterial = AtomToolsFramework : : GetExteralReferencePath ( m_absolutePath , m_materialSourceData . m_parentMaterial ) ;
@ -243,14 +182,14 @@ namespace MaterialEditor
if ( ! savedProperties )
if ( ! savedProperties )
{
{
return false ;
return SaveFailed ( ) ;
}
}
// write sourceData to .material file
// write sourceData to .material file
if ( ! AZ : : RPI : : JsonUtils : : SaveObjectToFile ( m_absolutePath , sourceData ) )
if ( ! AZ : : RPI : : JsonUtils : : SaveObjectToFile ( m_absolutePath , sourceData ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material d ocument could not be saved: '%s'." , m_absolutePath . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " D ocument could not be saved: '%s'." , m_absolutePath . c_str ( ) ) ;
return false ;
return SaveFailed ( ) ;
}
}
// after saving, reset to a clean state
// after saving, reset to a clean state
@ -262,181 +201,97 @@ namespace MaterialEditor
property . SetConfig ( propertyConfig ) ;
property . SetConfig ( propertyConfig ) ;
}
}
// Auto add or checkout saved file
return SaveSucceeded ( ) ;
AzToolsFramework : : SourceControlCommandBus : : Broadcast ( & AzToolsFramework : : SourceControlCommandBus : : Events : : RequestEdit ,
m_absolutePath . c_str ( ) , true , [ ] ( bool , const AzToolsFramework : : SourceControlFileInfo & ) { } ) ;
AZ_TracePrintf ( " MaterialDocument " , " Material document saved: '%s'. \n " , m_absolutePath . data ( ) ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentSaved , m_id ) ;
m_saveTriggeredInternally = true ;
return true ;
}
}
bool MaterialDocument : : SaveAsCopy ( AZStd : : string_view savePath )
bool MaterialDocument : : SaveAsCopy ( AZStd : : string_view savePath )
{
{
using namespace AZ ;
if ( ! AtomToolsDocument : : SaveAsCopy ( savePath ) )
using namespace RPI ;
if ( ! IsOpen ( ) )
{
AZ_Error ( " MaterialDocument " , false , " Material document is not open to be saved: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
}
if ( ! IsSavable ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material types can only be saved as a child: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
}
AZStd : : string normalizedSavePath = savePath ;
if ( ! AzFramework : : StringFunc : : Path : : Normalize ( normalizedSavePath ) )
{
AZ_Error ( " MaterialDocument " , false , " Material document save path could not be normalized: '%s'. " , normalizedSavePath . c_str ( ) ) ;
return false ;
return false ;
}
}
// create source data from properties
// create source data from properties
MaterialSourceData sourceData ;
AZ : : RPI : : MaterialSourceData sourceData ;
sourceData . m_materialTypeVersion = m_materialAsset - > GetMaterialTypeAsset ( ) - > GetVersion ( ) ;
sourceData . m_materialTypeVersion = m_materialAsset - > GetMaterialTypeAsset ( ) - > GetVersion ( ) ;
sourceData . m_materialType = AtomToolsFramework : : GetExteralReferencePath ( normalizedSavePath , m_materialSourceData . m_materialType ) ;
sourceData . m_materialType = AtomToolsFramework : : GetExteralReferencePath ( m_savePathNormalized , m_materialSourceData . m_materialType ) ;
sourceData . m_parentMaterial = AtomToolsFramework : : GetExteralReferencePath ( normalizedSavePath , m_materialSourceData . m_parentMaterial ) ;
sourceData . m_parentMaterial = AtomToolsFramework : : GetExteralReferencePath ( m_savePathNormalized , m_materialSourceData . m_parentMaterial ) ;
// populate sourceData with modified or overwritten properties
// populate sourceData with modified or overwritten properties
const bool savedProperties = SavePropertiesToSourceData ( normalizedSavePath , sourceData , [ ] ( const AtomToolsFramework : : DynamicProperty & property )
const bool savedProperties = SavePropertiesToSourceData ( m_savePathNormalized , sourceData , [ ] ( const AtomToolsFramework : : DynamicProperty & property )
{
{
return ! AtomToolsFramework : : ArePropertyValuesEqual ( property . GetValue ( ) , property . GetConfig ( ) . m_parentValue ) ;
return ! AtomToolsFramework : : ArePropertyValuesEqual ( property . GetValue ( ) , property . GetConfig ( ) . m_parentValue ) ;
} ) ;
} ) ;
if ( ! savedProperties )
if ( ! savedProperties )
{
{
return false ;
return SaveFailed ( ) ;
}
}
// write sourceData to .material file
// write sourceData to .material file
if ( ! AZ : : RPI : : JsonUtils : : SaveObjectToFile ( normalizedSavePath , sourceData ) )
if ( ! AZ : : RPI : : JsonUtils : : SaveObjectToFile ( m_savePathNormalized , sourceData ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material document could not be saved: '%s'." , normalizedSavePath . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Document could not be saved: '%s'." , m_savePathNormalized . c_str ( ) ) ;
return false ;
return SaveFailed ( ) ;
}
}
// Auto add or checkout saved file
AzToolsFramework : : SourceControlCommandBus : : Broadcast ( & AzToolsFramework : : SourceControlCommandBus : : Events : : RequestEdit ,
normalizedSavePath . c_str ( ) , true , [ ] ( bool , const AzToolsFramework : : SourceControlFileInfo & ) { } ) ;
AZ_TracePrintf ( " MaterialDocument " , " Material document saved: '%s'. \n " , normalizedSavePath . c_str ( ) ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentSaved , m_id ) ;
// If the document is saved to a new file we need to reopen the new document to update assets, paths, property deltas.
// If the document is saved to a new file we need to reopen the new document to update assets, paths, property deltas.
if ( ! Open ( normalizedSavePath ) )
if ( ! Open ( m_savePathNormalized ) )
{
{
return false ;
return SaveFailed ( ) ;
}
}
// Setting flag after reopening becausse it's cleared on open
return SaveSucceeded ( ) ;
m_saveTriggeredInternally = true ;
return true ;
}
}
bool MaterialDocument : : SaveAsChild ( AZStd : : string_view savePath )
bool MaterialDocument : : SaveAsChild ( AZStd : : string_view savePath )
{
{
using namespace AZ ;
if ( ! AtomToolsDocument : : SaveAsChild ( savePath ) )
using namespace RPI ;
if ( ! IsOpen ( ) )
{
AZ_Error ( " MaterialDocument " , false , " Material document is not open to be saved: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
}
AZStd : : string normalizedSavePath = savePath ;
if ( ! AzFramework : : StringFunc : : Path : : Normalize ( normalizedSavePath ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material document save path could not be normalized: '%s'. " , normalizedSavePath . c_str ( ) ) ;
return false ;
}
if ( m_absolutePath = = normalizedSavePath )
{
// ToDo: this should scan the entire hierarchy so we don't overwrite parent's parent, for example
AZ_Error ( " MaterialDocument " , false , " Can't overwrite parent material with a child that depends on it. " ) ;
return false ;
return false ;
}
}
// create source data from properties
// create source data from properties
MaterialSourceData sourceData ;
AZ : : RPI : : MaterialSourceData sourceData ;
sourceData . m_materialTypeVersion = m_materialAsset - > GetMaterialTypeAsset ( ) - > GetVersion ( ) ;
sourceData . m_materialTypeVersion = m_materialAsset - > GetMaterialTypeAsset ( ) - > GetVersion ( ) ;
sourceData . m_materialType = AtomToolsFramework : : GetExteralReferencePath ( normalizedSavePath , m_materialSourceData . m_materialType ) ;
sourceData . m_materialType = AtomToolsFramework : : GetExteralReferencePath ( m_savePathNormalized , m_materialSourceData . m_materialType ) ;
// Only assign a parent path if the source was a .material
// Only assign a parent path if the source was a .material
if ( AzFramework : : StringFunc : : Path : : IsExtension ( m_ rel ativ ePath. c_str ( ) , MaterialSourceData : : Extension ) )
if ( AzFramework : : StringFunc : : Path : : IsExtension ( m_ absolu tePath. c_str ( ) , AZ : : RPI : : MaterialSourceData : : Extension ) )
{
{
sourceData . m_parentMaterial = AtomToolsFramework : : GetExteralReferencePath ( normalizedSavePath , m_absolutePath ) ;
sourceData . m_parentMaterial = AtomToolsFramework : : GetExteralReferencePath ( m_savePathNormalized , m_absolutePath ) ;
}
}
// populate sourceData with modified properties
// populate sourceData with modified properties
const bool savedProperties = SavePropertiesToSourceData ( normalizedSavePath , sourceData , [ ] ( const AtomToolsFramework : : DynamicProperty & property )
const bool savedProperties = SavePropertiesToSourceData ( m_savePathNormalized , sourceData , [ ] ( const AtomToolsFramework : : DynamicProperty & property )
{
{
return ! AtomToolsFramework : : ArePropertyValuesEqual ( property . GetValue ( ) , property . GetConfig ( ) . m_originalValue ) ;
return ! AtomToolsFramework : : ArePropertyValuesEqual ( property . GetValue ( ) , property . GetConfig ( ) . m_originalValue ) ;
} ) ;
} ) ;
if ( ! savedProperties )
if ( ! savedProperties )
{
{
return false ;
return SaveFailed ( ) ;
}
}
// write sourceData to .material file
// write sourceData to .material file
if ( ! AZ : : RPI : : JsonUtils : : SaveObjectToFile ( normalizedSavePath , sourceData ) )
if ( ! AZ : : RPI : : JsonUtils : : SaveObjectToFile ( m_savePathNormalized , sourceData ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material document could not be saved: '%s'." , normalizedSavePath . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Document could not be saved: '%s'." , m_savePathNormalized . c_str ( ) ) ;
return false ;
return SaveFailed ( ) ;
}
}
// Auto add or checkout saved file
AzToolsFramework : : SourceControlCommandBus : : Broadcast ( & AzToolsFramework : : SourceControlCommandBus : : Events : : RequestEdit ,
normalizedSavePath . c_str ( ) , true , [ ] ( bool , const AzToolsFramework : : SourceControlFileInfo & ) { } ) ;
AZ_TracePrintf ( " MaterialDocument " , " Material document saved: '%s'. \n " , normalizedSavePath . c_str ( ) ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentSaved , m_id ) ;
// If the document is saved to a new file we need to reopen the new document to update assets, paths, property deltas.
// If the document is saved to a new file we need to reopen the new document to update assets, paths, property deltas.
if ( ! Open ( normalizedSavePath ) )
if ( ! Open ( m_savePathNormalized ) )
{
{
return false ;
return SaveFailed ( ) ;
}
}
// Setting flag after reopening becausse it's cleared on open
return SaveSucceeded ( ) ;
m_saveTriggeredInternally = true ;
return true ;
}
bool MaterialDocument : : Close ( )
{
using namespace AZ ;
using namespace RPI ;
if ( ! IsOpen ( ) )
{
AZ_Error ( " MaterialDocument " , false , " Material document is not open. " ) ;
return false ;
}
AZ_TracePrintf ( " MaterialDocument " , " Material document closed: '%s'. \n " , m_absolutePath . c_str ( ) ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentClosed , m_id ) ;
// Clearing after notification so paths are still available
Clear ( ) ;
return true ;
}
}
bool MaterialDocument : : IsOpen ( ) const
bool MaterialDocument : : IsOpen ( ) const
{
{
return ! m_absolutePath . empty ( ) & & ! m_relativePath . empty ( ) & & m_materialAsset . IsReady ( ) & & m_materialInstance ;
return AtomToolsDocument : : IsOpen ( ) & & m_materialAsset . IsReady ( ) & & m_materialInstance ;
}
}
bool MaterialDocument : : IsModified ( ) const
bool MaterialDocument : : IsModified ( ) const
@ -454,44 +309,6 @@ namespace MaterialEditor
return AzFramework : : StringFunc : : Path : : IsExtension ( m_absolutePath . c_str ( ) , AZ : : RPI : : MaterialSourceData : : Extension ) ;
return AzFramework : : StringFunc : : Path : : IsExtension ( m_absolutePath . c_str ( ) , AZ : : RPI : : MaterialSourceData : : Extension ) ;
}
}
bool MaterialDocument : : CanUndo ( ) const
{
// Undo will only be allowed if something has been recorded and we're not at the beginning of history
return IsOpen ( ) & & ! m_undoHistory . empty ( ) & & m_undoHistoryIndex > 0 ;
}
bool MaterialDocument : : CanRedo ( ) const
{
// Redo will only be allowed if something has been recorded and we're not at the end of history
return IsOpen ( ) & & ! m_undoHistory . empty ( ) & & m_undoHistoryIndex < m_undoHistory . size ( ) ;
}
bool MaterialDocument : : Undo ( )
{
if ( CanUndo ( ) )
{
// The history index is one beyond the last executed command. Decrement the index then execute undo.
m_undoHistory [ - - m_undoHistoryIndex ] . first ( ) ;
AZ_TracePrintf ( " MaterialDocument " , " Material document undo: '%s'. \n " , m_absolutePath . c_str ( ) ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentUndoStateChanged , m_id ) ;
return true ;
}
return false ;
}
bool MaterialDocument : : Redo ( )
{
if ( CanRedo ( ) )
{
// Execute the current redo command then move the history index to the next position.
m_undoHistory [ m_undoHistoryIndex + + ] . second ( ) ;
AZ_TracePrintf ( " MaterialDocument " , " Material document redo: '%s'. \n " , m_absolutePath . c_str ( ) ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentUndoStateChanged , m_id ) ;
return true ;
}
return false ;
}
bool MaterialDocument : : BeginEdit ( )
bool MaterialDocument : : BeginEdit ( )
{
{
// Save the current properties as a momento for undo before any changes are applied
// Save the current properties as a momento for undo before any changes are applied
@ -524,17 +341,9 @@ namespace MaterialEditor
if ( ! propertyValuesForUndo . empty ( ) & & ! propertyValuesForRedo . empty ( ) )
if ( ! propertyValuesForUndo . empty ( ) & & ! propertyValuesForRedo . empty ( ) )
{
{
// Wipe any state beyond the current history index
AddUndoRedoHistory (
m_undoHistory . erase ( m_undoHistory . begin ( ) + m_undoHistoryIndex , m_undoHistory . end ( ) ) ;
// Add undo and redo operations using lambdas that will capture property state and restore it when executed
m_undoHistory . emplace_back (
[ this , propertyValuesForUndo ] ( ) { RestorePropertyValues ( propertyValuesForUndo ) ; } ,
[ this , propertyValuesForUndo ] ( ) { RestorePropertyValues ( propertyValuesForUndo ) ; } ,
[ this , propertyValuesForRedo ] ( ) { RestorePropertyValues ( propertyValuesForRedo ) ; } ) ;
[ this , propertyValuesForRedo ] ( ) { RestorePropertyValues ( propertyValuesForRedo ) ; } ) ;
// Assign the index to the end of history
m_undoHistoryIndex = aznumeric_cast < int > ( m_undoHistory . size ( ) ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast ( & AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentUndoStateChanged , m_id ) ;
}
}
m_propertyValuesBeforeEdit . clear ( ) ;
m_propertyValuesBeforeEdit . clear ( ) ;
@ -553,51 +362,25 @@ namespace MaterialEditor
}
}
}
}
void MaterialDocument : : SourceFileChanged ( AZStd : : string relativePath , AZStd : : string scanFolder , [[maybe_unused]] AZ : : Uuid sourceUUID )
{
const auto sourcePath = AZ : : RPI : : AssetUtils : : ResolvePathReference ( scanFolder , relativePath ) ;
if ( m_absolutePath = = sourcePath )
{
// ignore notifications caused by saving the open document
if ( ! m_saveTriggeredInternally )
{
AZ_TracePrintf ( " MaterialDocument " , " Material document changed externally: '%s'. \n " , m_absolutePath . c_str ( ) ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast (
& AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentExternallyModified , m_id ) ;
}
m_saveTriggeredInternally = false ;
}
else if ( m_sourceDependencies . find ( sourcePath ) ! = m_sourceDependencies . end ( ) )
{
AZ_TracePrintf ( " MaterialDocument " , " Material document dependency changed: '%s'. \n " , m_absolutePath . c_str ( ) ) ;
AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Broadcast (
& AtomToolsFramework : : AtomToolsDocumentNotificationBus : : Events : : OnDocumentDependencyModified , m_id ) ;
}
}
bool MaterialDocument : : SavePropertiesToSourceData (
bool MaterialDocument : : SavePropertiesToSourceData (
const AZStd : : string & exportPath , AZ : : RPI : : MaterialSourceData & sourceData , PropertyFilterFunction propertyFilter ) const
const AZStd : : string & exportPath , AZ : : RPI : : MaterialSourceData & sourceData , PropertyFilterFunction propertyFilter ) const
{
{
using namespace AZ ;
using namespace RPI ;
bool result = true ;
bool result = true ;
// populate sourceData with properties that meet the filter
// populate sourceData with properties that meet the filter
m_materialTypeSourceData . EnumerateProperties ( [ & ] ( const AZStd : : string & propertyIdContext , const auto & propertyDefinition ) {
m_materialTypeSourceData . EnumerateProperties ( [ & ] ( const AZStd : : string & propertyIdContext , const auto & propertyDefinition ) {
Name propertyId { propertyIdContext + propertyDefinition - > GetName ( ) } ;
AZ : : Name propertyId { propertyIdContext + propertyDefinition - > GetName ( ) } ;
const auto it = m_properties . find ( propertyId ) ;
const auto it = m_properties . find ( propertyId ) ;
if ( it ! = m_properties . end ( ) & & propertyFilter ( it - > second ) )
if ( it ! = m_properties . end ( ) & & propertyFilter ( it - > second ) )
{
{
MaterialPropertyValue propertyValue = AtomToolsFramework : : ConvertToRuntimeType ( it - > second . GetValue ( ) ) ;
AZ : : RPI : : MaterialPropertyValue propertyValue = AtomToolsFramework : : ConvertToRuntimeType ( it - > second . GetValue ( ) ) ;
if ( propertyValue . IsValid ( ) )
if ( propertyValue . IsValid ( ) )
{
{
if ( ! AtomToolsFramework : : ConvertToExportFormat ( exportPath , propertyId , * propertyDefinition , propertyValue ) )
if ( ! AtomToolsFramework : : ConvertToExportFormat ( exportPath , propertyId , * propertyDefinition , propertyValue ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material d ocument property could not be converted: '%s' in '%s'." , propertyId . GetCStr ( ) , m_absolutePath . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Document property could not be converted: '%s' in '%s'. " , propertyId . GetCStr ( ) , m_absolutePath . c_str ( ) ) ;
result = false ;
result = false ;
return false ;
return false ;
}
}
@ -613,52 +396,21 @@ namespace MaterialEditor
return result ;
return result ;
}
}
bool MaterialDocument : : Open Internal ( AZStd : : string_view loadPath )
bool MaterialDocument : : Open ( AZStd : : string_view loadPath )
{
{
using namespace AZ ;
if ( ! AtomToolsDocument : : Open ( loadPath ) )
using namespace RPI ;
Clear ( ) ;
m_absolutePath = loadPath ;
if ( ! AzFramework : : StringFunc : : Path : : Normalize ( m_absolutePath ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material document path could not be normalized: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
}
if ( AzFramework : : StringFunc : : Path : : IsRelative ( m_absolutePath . c_str ( ) ) )
{
AZ_Error ( " MaterialDocument " , false , " Material document path must be absolute: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
}
bool result = false ;
Data : : AssetInfo sourceAssetInfo ;
AZStd : : string watchFolder ;
AzToolsFramework : : AssetSystemRequestBus : : BroadcastResult ( result , & AzToolsFramework : : AssetSystem : : AssetSystemRequest : : GetSourceInfoBySourcePath ,
m_absolutePath . c_str ( ) , sourceAssetInfo , watchFolder ) ;
if ( ! result )
{
AZ_Error ( " MaterialDocument " , false , " Could not find source material: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
}
m_relativePath = sourceAssetInfo . m_relativePath ;
if ( ! AzFramework : : StringFunc : : Path : : Normalize ( m_relativePath ) )
{
AZ_Error ( " MaterialDocument " , false , " Material document path could not be normalized: '%s'. " , m_relativePath . c_str ( ) ) ;
return false ;
return false ;
}
}
// The material document and inspector are constructed from source data
// The material document and inspector are constructed from source data
if ( AzFramework : : StringFunc : : Path : : IsExtension ( m_absolutePath . c_str ( ) , MaterialSourceData: : Extension ) )
if ( AzFramework : : StringFunc : : Path : : IsExtension ( m_absolutePath . c_str ( ) , AZ : : RPI : : MaterialSourceData : : Extension ) )
{
{
// Load the material source data so that we can check properties and create a material asset from it
// Load the material source data so that we can check properties and create a material asset from it
if ( ! AZ : : RPI : : JsonUtils : : LoadObjectFromFile ( m_absolutePath , m_materialSourceData ) )
if ( ! AZ : : RPI : : JsonUtils : : LoadObjectFromFile ( m_absolutePath , m_materialSourceData ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material source data could not be loaded: '%s'. " , m_absolutePath . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Material source data could not be loaded: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
return OpenFailed ( ) ;
}
}
// We always need the absolute path for the material type and parent material to load source data and resolving
// We always need the absolute path for the material type and parent material to load source data and resolving
@ -666,33 +418,34 @@ namespace MaterialEditor
if ( ! m_materialSourceData . m_parentMaterial . empty ( ) )
if ( ! m_materialSourceData . m_parentMaterial . empty ( ) )
{
{
m_materialSourceData . m_parentMaterial =
m_materialSourceData . m_parentMaterial =
A ssetUtils: : ResolvePathReference ( m_absolutePath , m_materialSourceData . m_parentMaterial ) ;
A Z: : RPI : : A ssetUtils: : ResolvePathReference ( m_absolutePath , m_materialSourceData . m_parentMaterial ) ;
}
}
if ( ! m_materialSourceData . m_materialType . empty ( ) )
if ( ! m_materialSourceData . m_materialType . empty ( ) )
{
{
m_materialSourceData . m_materialType = AssetUtils : : ResolvePathReference ( m_absolutePath , m_materialSourceData . m_materialType ) ;
m_materialSourceData . m_materialType =
AZ : : RPI : : AssetUtils : : ResolvePathReference ( m_absolutePath , m_materialSourceData . m_materialType ) ;
}
}
// Load the material type source data which provides the layout and default values of all of the properties
// Load the material type source data which provides the layout and default values of all of the properties
auto materialTypeOutcome = MaterialUtils: : LoadMaterialTypeSourceData ( m_materialSourceData . m_materialType ) ;
auto materialTypeOutcome = AZ: : RPI : : MaterialUtils: : LoadMaterialTypeSourceData ( m_materialSourceData . m_materialType ) ;
if ( ! materialTypeOutcome . IsSuccess ( ) )
if ( ! materialTypeOutcome . IsSuccess ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material type source data could not be loaded: '%s'. " , m_materialSourceData . m_materialType . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Material type source data could not be loaded: '%s'. " , m_materialSourceData . m_materialType . c_str ( ) ) ;
return false ;
return OpenFailed ( ) ;
}
}
m_materialTypeSourceData = materialTypeOutcome . TakeValue ( ) ;
m_materialTypeSourceData = materialTypeOutcome . TakeValue ( ) ;
}
}
else if ( AzFramework : : StringFunc : : Path : : IsExtension ( m_absolutePath . c_str ( ) , MaterialTypeSourceData: : Extension ) )
else if ( AzFramework : : StringFunc : : Path : : IsExtension ( m_absolutePath . c_str ( ) , AZ: : RPI : : MaterialTypeSourceData: : Extension ) )
{
{
// A material document can be created or loaded from material or material type source data. If we are attempting to load
// A material document can be created or loaded from material or material type source data. If we are attempting to load
// material type source data then the material source data object can be created just by referencing the document path as the
// material type source data then the material source data object can be created just by referencing the document path as the
// material type path.
// material type path.
auto materialTypeOutcome = MaterialUtils: : LoadMaterialTypeSourceData ( m_absolutePath ) ;
auto materialTypeOutcome = AZ: : RPI : : MaterialUtils: : LoadMaterialTypeSourceData ( m_absolutePath ) ;
if ( ! materialTypeOutcome . IsSuccess ( ) )
if ( ! materialTypeOutcome . IsSuccess ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material type source data could not be loaded: '%s'. " , m_absolutePath . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Material type source data could not be loaded: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
return OpenFailed ( ) ;
}
}
m_materialTypeSourceData = materialTypeOutcome . TakeValue ( ) ;
m_materialTypeSourceData = materialTypeOutcome . TakeValue ( ) ;
@ -702,8 +455,8 @@ namespace MaterialEditor
}
}
else
else
{
{
AZ_Error ( " MaterialDocument " , false , " Material d ocument extension not supported: '%s'." , m_absolutePath . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " D ocument extension not supported: '%s'." , m_absolutePath . c_str ( ) ) ;
return false ;
return OpenFailed ( ) ;
}
}
const bool elevateWarnings = false ;
const bool elevateWarnings = false ;
@ -714,44 +467,44 @@ namespace MaterialEditor
// we can create the asset dynamically from the source data.
// we can create the asset dynamically from the source data.
// Long term, the material document should not be concerned with assets at all. The viewport window should be the
// Long term, the material document should not be concerned with assets at all. The viewport window should be the
// only thing concerned with assets or instances.
// only thing concerned with assets or instances.
auto materialAssetResult =
auto materialAssetResult = m_materialSourceData . CreateMaterialAssetFromSourceData (
m_materialSourceData. CreateMaterialAssetFromSourceData ( Uuid : : CreateRandom ( ) , m_absolutePath , elevateWarnings , & m_sourceDependencies ) ;
AZ: : Uuid : : CreateRandom ( ) , m_absolutePath , elevateWarnings , & m_sourceDependencies ) ;
if ( ! materialAssetResult )
if ( ! materialAssetResult )
{
{
AZ_Error ( " MaterialDocument " , false , " Material asset could not be created from source data: '%s'. " , m_absolutePath . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Material asset could not be created from source data: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
return OpenFailed ( ) ;
}
}
m_materialAsset = materialAssetResult . GetValue ( ) ;
m_materialAsset = materialAssetResult . GetValue ( ) ;
if ( ! m_materialAsset . IsReady ( ) )
if ( ! m_materialAsset . IsReady ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material asset is not ready: '%s'. " , m_absolutePath . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Material asset is not ready: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
return OpenFailed ( ) ;
}
}
const auto & materialTypeAsset = m_materialAsset - > GetMaterialTypeAsset ( ) ;
const auto & materialTypeAsset = m_materialAsset - > GetMaterialTypeAsset ( ) ;
if ( ! materialTypeAsset . IsReady ( ) )
if ( ! materialTypeAsset . IsReady ( ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material type asset is not ready: '%s'. " , m_absolutePath . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Material type asset is not ready: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
return OpenFailed ( ) ;
}
}
AZStd : : span < const AZ : : RPI : : MaterialPropertyValue > parentPropertyValues = materialTypeAsset - > GetDefaultPropertyValues ( ) ;
AZStd : : span < const AZ : : RPI : : MaterialPropertyValue > parentPropertyValues = materialTypeAsset - > GetDefaultPropertyValues ( ) ;
AZ : : Data : : Asset < MaterialAsset> parentMaterialAsset ;
AZ : : Data : : Asset < AZ: : RPI : : MaterialAsset> parentMaterialAsset ;
if ( ! m_materialSourceData . m_parentMaterial . empty ( ) )
if ( ! m_materialSourceData . m_parentMaterial . empty ( ) )
{
{
AZ : : RPI : : MaterialSourceData parentMaterialSourceData ;
AZ : : RPI : : MaterialSourceData parentMaterialSourceData ;
if ( ! AZ : : RPI : : JsonUtils : : LoadObjectFromFile ( m_materialSourceData . m_parentMaterial , parentMaterialSourceData ) )
if ( ! AZ : : RPI : : JsonUtils : : LoadObjectFromFile ( m_materialSourceData . m_parentMaterial , parentMaterialSourceData ) )
{
{
AZ_Error ( " MaterialDocument " , false , " Material parent source data could not be loaded for: '%s'. " , m_materialSourceData . m_parentMaterial . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Material parent source data could not be loaded for: '%s'. " , m_materialSourceData . m_parentMaterial . c_str ( ) ) ;
return false ;
return OpenFailed ( ) ;
}
}
const auto parentMaterialAssetIdResult = A ssetUtils: : MakeAssetId ( m_materialSourceData . m_parentMaterial , 0 ) ;
const auto parentMaterialAssetIdResult = A Z: : RPI : : A ssetUtils: : MakeAssetId ( m_materialSourceData . m_parentMaterial , 0 ) ;
if ( ! parentMaterialAssetIdResult )
if ( ! parentMaterialAssetIdResult )
{
{
AZ_Error ( " MaterialDocument " , false , " Material parent asset ID could not be created: '%s'. " , m_materialSourceData . m_parentMaterial . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Material parent asset ID could not be created: '%s'. " , m_materialSourceData . m_parentMaterial . c_str ( ) ) ;
return false ;
return OpenFailed ( ) ;
}
}
auto parentMaterialAssetResult = parentMaterialSourceData . CreateMaterialAssetFromSourceData (
auto parentMaterialAssetResult = parentMaterialSourceData . CreateMaterialAssetFromSourceData (
@ -759,7 +512,7 @@ namespace MaterialEditor
if ( ! parentMaterialAssetResult )
if ( ! parentMaterialAssetResult )
{
{
AZ_Error ( " MaterialDocument " , false , " Material parent asset could not be created from source data: '%s'. " , m_materialSourceData . m_parentMaterial . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Material parent asset could not be created from source data: '%s'. " , m_materialSourceData . m_parentMaterial . c_str ( ) ) ;
return false ;
return OpenFailed ( ) ;
}
}
parentMaterialAsset = parentMaterialAssetResult . GetValue ( ) ;
parentMaterialAsset = parentMaterialAssetResult . GetValue ( ) ;
@ -767,11 +520,11 @@ namespace MaterialEditor
}
}
// Creating a material from a material asset will fail if a texture is referenced but not loaded
// Creating a material from a material asset will fail if a texture is referenced but not loaded
m_materialInstance = Material: : Create ( m_materialAsset ) ;
m_materialInstance = AZ: : RPI : : Material: : Create ( m_materialAsset ) ;
if ( ! m_materialInstance )
if ( ! m_materialInstance )
{
{
AZ_Error ( " MaterialDocument " , false , " Material instance could not be created: '%s'. " , m_absolutePath . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Material instance could not be created: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
return OpenFailed ( ) ;
}
}
// Pipeline State Object changes are always allowed in the material editor because it only runs on developer systems
// Pipeline State Object changes are always allowed in the material editor because it only runs on developer systems
@ -781,7 +534,7 @@ namespace MaterialEditor
// Populate the property map from a combination of source data and assets
// Populate the property map from a combination of source data and assets
// Assets must still be used for now because they contain the final accumulated value after all other materials
// Assets must still be used for now because they contain the final accumulated value after all other materials
// in the hierarchy are applied
// in the hierarchy are applied
m_materialTypeSourceData . EnumeratePropertyGroups ( [ this , & parentPropertyValues ] ( const AZStd : : string & propertyIdContext , const MaterialTypeSourceData: : PropertyGroup * propertyGroup )
m_materialTypeSourceData . EnumeratePropertyGroups ( [ this , & parentPropertyValues ] ( const AZStd : : string & propertyIdContext , const AZ: : RPI : : MaterialTypeSourceData: : PropertyGroup * propertyGroup )
{
{
AtomToolsFramework : : DynamicPropertyConfig propertyConfig ;
AtomToolsFramework : : DynamicPropertyConfig propertyConfig ;
@ -813,7 +566,7 @@ namespace MaterialEditor
// Populate the property group visibility map
// Populate the property group visibility map
// TODO: Support populating the Material Editor with nested property groups, not just the top level.
// TODO: Support populating the Material Editor with nested property groups, not just the top level.
for ( const AZStd : : unique_ptr < MaterialTypeSourceData: : PropertyGroup > & propertyGroup : m_materialTypeSourceData . GetPropertyLayout ( ) . m_propertyGroups )
for ( const AZStd : : unique_ptr < AZ: : RPI : : MaterialTypeSourceData: : PropertyGroup > & propertyGroup : m_materialTypeSourceData . GetPropertyLayout ( ) . m_propertyGroups )
{
{
m_propertyGroupVisibility [ AZ : : Name { propertyGroup - > GetName ( ) } ] = true ;
m_propertyGroupVisibility [ AZ : : Name { propertyGroup - > GetName ( ) } ] = true ;
}
}
@ -856,15 +609,15 @@ namespace MaterialEditor
m_properties [ propertyConfig . m_id ] = AtomToolsFramework : : DynamicProperty ( propertyConfig ) ;
m_properties [ propertyConfig . m_id ] = AtomToolsFramework : : DynamicProperty ( propertyConfig ) ;
//Add UV name customization properties
//Add UV name customization properties
const RPI: : MaterialUvNameMap & uvNameMap = materialTypeAsset - > GetUvNameMap ( ) ;
const AZ: : RPI: : MaterialUvNameMap & uvNameMap = materialTypeAsset - > GetUvNameMap ( ) ;
for ( const RPI: : UvNamePair & uvNamePair : uvNameMap )
for ( const AZ: : RPI: : UvNamePair & uvNamePair : uvNameMap )
{
{
const AZStd : : string shaderInput = uvNamePair . m_shaderInput . ToString ( ) ;
const AZStd : : string shaderInput = uvNamePair . m_shaderInput . ToString ( ) ;
const AZStd : : string uvName = uvNamePair . m_uvName . GetStringView ( ) ;
const AZStd : : string uvName = uvNamePair . m_uvName . GetStringView ( ) ;
propertyConfig = { } ;
propertyConfig = { } ;
propertyConfig . m_dataType = AtomToolsFramework : : DynamicPropertyType : : String ;
propertyConfig . m_dataType = AtomToolsFramework : : DynamicPropertyType : : String ;
propertyConfig . m_id = MaterialPropertyId( UvGroupName , shaderInput ) ;
propertyConfig . m_id = AZ: : RPI : : MaterialPropertyId( UvGroupName , shaderInput ) ;
propertyConfig . m_name = shaderInput ;
propertyConfig . m_name = shaderInput ;
propertyConfig . m_displayName = shaderInput ;
propertyConfig . m_displayName = shaderInput ;
propertyConfig . m_groupName = " UV Sets " ;
propertyConfig . m_groupName = " UV Sets " ;
@ -878,15 +631,15 @@ namespace MaterialEditor
}
}
// Add material functors that are in the top-level functors list.
// Add material functors that are in the top-level functors list.
const MaterialFunctorSourceData: : EditorContext editorContext =
const AZ: : RPI : : MaterialFunctorSourceData: : EditorContext editorContext =
MaterialFunctorSourceData: : EditorContext ( m_materialSourceData . m_materialType , m_materialAsset - > GetMaterialPropertiesLayout ( ) ) ;
AZ: : RPI : : MaterialFunctorSourceData: : EditorContext ( m_materialSourceData . m_materialType , m_materialAsset - > GetMaterialPropertiesLayout ( ) ) ;
for ( Ptr < MaterialFunctorSourceDataHolder> functorData : m_materialTypeSourceData . m_materialFunctorSourceData )
for ( Ptr < AZ: : RPI : : MaterialFunctorSourceDataHolder> functorData : m_materialTypeSourceData . m_materialFunctorSourceData )
{
{
MaterialFunctorSourceData: : FunctorResult result2 = functorData - > CreateFunctor ( editorContext ) ;
AZ: : RPI : : MaterialFunctorSourceData: : FunctorResult result2 = functorData - > CreateFunctor ( editorContext ) ;
if ( result2 . IsSuccess ( ) )
if ( result2 . IsSuccess ( ) )
{
{
Ptr < MaterialFunctor> & functor = result2 . GetValue ( ) ;
Ptr < AZ: : RPI : : MaterialFunctor> & functor = result2 . GetValue ( ) ;
if ( functor ! = nullptr )
if ( functor ! = nullptr )
{
{
m_editorFunctors . push_back ( functor ) ;
m_editorFunctors . push_back ( functor ) ;
@ -895,24 +648,24 @@ namespace MaterialEditor
else
else
{
{
AZ_Error ( " MaterialDocument " , false , " Material functors were not created: '%s'. " , m_absolutePath . c_str ( ) ) ;
AZ_Error ( " MaterialDocument " , false , " Material functors were not created: '%s'. " , m_absolutePath . c_str ( ) ) ;
return false ;
return OpenFailed ( ) ;
}
}
}
}
// Add any material functors that are located inside each property group.
// Add any material functors that are located inside each property group.
bool enumerateResult = m_materialTypeSourceData . EnumeratePropertyGroups (
bool enumerateResult = m_materialTypeSourceData . EnumeratePropertyGroups (
[ this ] ( const AZStd : : string & , const MaterialTypeSourceData: : PropertyGroup * propertyGroup )
[ this ] ( const AZStd : : string & , const AZ: : RPI : : MaterialTypeSourceData: : PropertyGroup * propertyGroup )
{
{
const MaterialFunctorSourceData: : EditorContext editorContext = MaterialFunctorSourceData : : EditorContext (
const AZ: : RPI : : MaterialFunctorSourceData: : EditorContext editorContext = AZ : : RPI : : MaterialFunctorSourceData : : EditorContext (
m_materialSourceData . m_materialType , m_materialAsset - > GetMaterialPropertiesLayout ( ) ) ;
m_materialSourceData . m_materialType , m_materialAsset - > GetMaterialPropertiesLayout ( ) ) ;
for ( Ptr < MaterialFunctorSourceDataHolder> functorData : propertyGroup - > GetFunctors ( ) )
for ( Ptr < AZ: : RPI : : MaterialFunctorSourceDataHolder> functorData : propertyGroup - > GetFunctors ( ) )
{
{
MaterialFunctorSourceData: : FunctorResult result = functorData - > CreateFunctor ( editorContext ) ;
AZ: : RPI : : MaterialFunctorSourceData: : FunctorResult result = functorData - > CreateFunctor ( editorContext ) ;
if ( result . IsSuccess ( ) )
if ( result . IsSuccess ( ) )
{
{
Ptr < MaterialFunctor> & functor = result . GetValue ( ) ;
Ptr < AZ: : RPI : : MaterialFunctor> & functor = result . GetValue ( ) ;
if ( functor ! = nullptr )
if ( functor ! = nullptr )
{
{
m_editorFunctors . push_back ( functor ) ;
m_editorFunctors . push_back ( functor ) ;
@ -930,18 +683,35 @@ namespace MaterialEditor
if ( ! enumerateResult )
if ( ! enumerateResult )
{
{
return false ;
return OpenFailed ( ) ;
}
}
AZ : : RPI : : MaterialPropertyFlags dirtyFlags ;
AZ : : RPI : : MaterialPropertyFlags dirtyFlags ;
dirtyFlags . set ( ) ; // Mark all properties as dirty since we just loaded the material and need to initialize property visibility
dirtyFlags . set ( ) ; // Mark all properties as dirty since we just loaded the material and need to initialize property visibility
RunEditorMaterialFunctors ( dirtyFlags ) ;
RunEditorMaterialFunctors ( dirtyFlags ) ;
// Connecting to bus to monitor external changes
return OpenSucceeded ( ) ;
AzToolsFramework : : AssetSystemBus : : Handler : : BusConnect ( ) ;
}
AZ_TracePrintf ( " MaterialDocument " , " Material document opened: '%s'. \n " , m_absolutePath . c_str ( ) ) ;
bool MaterialDocument : : ReopenRecordState ( )
return true ;
{
m_propertyValuesBeforeReopen . clear ( ) ;
for ( const auto & propertyPair : m_properties )
{
const AtomToolsFramework : : DynamicProperty & property = propertyPair . second ;
if ( ! AtomToolsFramework : : ArePropertyValuesEqual ( property . GetValue ( ) , property . GetConfig ( ) . m_parentValue ) )
{
m_propertyValuesBeforeReopen [ property . GetId ( ) ] = property . GetValue ( ) ;
}
}
return AtomToolsDocument : : ReopenRecordState ( ) ;
}
bool MaterialDocument : : ReopenRestoreState ( )
{
RestorePropertyValues ( m_propertyValuesBeforeReopen ) ;
m_propertyValuesBeforeReopen . clear ( ) ;
return AtomToolsDocument : : ReopenRestoreState ( ) ;
}
}
void MaterialDocument : : Recompile ( )
void MaterialDocument : : Recompile ( )
@ -955,23 +725,18 @@ namespace MaterialEditor
void MaterialDocument : : Clear ( )
void MaterialDocument : : Clear ( )
{
{
AtomToolsFramework : : AtomToolsDocument : : Clear ( ) ;
AZ : : TickBus : : Handler : : BusDisconnect ( ) ;
AZ : : TickBus : : Handler : : BusDisconnect ( ) ;
AzToolsFramework : : AssetSystemBus : : Handler : : BusDisconnect ( ) ;
m_materialAsset = { } ;
m_materialAsset = { } ;
m_materialInstance = { } ;
m_materialInstance = { } ;
m_absolutePath . clear ( ) ;
m_relativePath . clear ( ) ;
m_sourceDependencies . clear ( ) ;
m_saveTriggeredInternally = { } ;
m_compilePending = { } ;
m_compilePending = { } ;
m_properties . clear ( ) ;
m_properties . clear ( ) ;
m_editorFunctors . clear ( ) ;
m_editorFunctors . clear ( ) ;
m_materialTypeSourceData = AZ : : RPI : : MaterialTypeSourceData ( ) ;
m_materialTypeSourceData = AZ : : RPI : : MaterialTypeSourceData ( ) ;
m_materialSourceData = AZ : : RPI : : MaterialSourceData ( ) ;
m_materialSourceData = AZ : : RPI : : MaterialSourceData ( ) ;
m_propertyValuesBeforeEdit . clear ( ) ;
m_propertyValuesBeforeEdit . clear ( ) ;
m_undoHistory . clear ( ) ;
m_undoHistoryIndex = { } ;
}
}
void MaterialDocument : : RestorePropertyValues ( const PropertyValueMap & propertyValues )
void MaterialDocument : : RestorePropertyValues ( const PropertyValueMap & propertyValues )
@ -1040,5 +805,4 @@ namespace MaterialEditor
return result ;
return result ;
}
}
} // namespace MaterialEditor
} // namespace MaterialEditor