|
|
|
@ -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 document 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 document property could not be found: '%s'.", propertyId.GetCStr());
|
|
|
|
AZ_Error("MaterialDocument", false, "Document 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 document is not open.");
|
|
|
|
AZ_Error("MaterialDocument", false, "Document 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 document property could not be found: '%s'.", propertyId.GetCStr());
|
|
|
|
AZ_Error("MaterialDocument", false, "Document 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 document is not open.");
|
|
|
|
AZ_Error("MaterialDocument", false, "Document 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 document property group could not be found: '%s'.", propertyGroupFullName.GetCStr());
|
|
|
|
AZ_Error("MaterialDocument", false, "Document 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 document 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 document property could not be found: '%s'.", propertyId.GetCStr());
|
|
|
|
AZ_Error("MaterialDocument", false, "Document 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,108 +135,53 @@ 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());
|
|
|
|
// SaveFailed has already been called so just forward the result without additional notifications.
|
|
|
|
|
|
|
|
// TODO Replace bool return value with enum for open and save states.
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!IsSavable())
|
|
|
|
// populate sourceData with modified or overridden properties and save object
|
|
|
|
{
|
|
|
|
AZ::RPI::MaterialSourceData sourceData;
|
|
|
|
AZ_Error("MaterialDocument", false, "Material types can only be saved as a child: '%s'.", m_absolutePath.c_str());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// create source data from properties
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
auto propertyFilter = [](const AtomToolsFramework::DynamicProperty& property) {
|
|
|
|
// populate sourceData with modified or overwritten properties
|
|
|
|
|
|
|
|
const bool savedProperties = SavePropertiesToSourceData(m_absolutePath, 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 (!SaveSourceData(sourceData, propertyFilter))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
return SaveFailed();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// write sourceData to .material file
|
|
|
|
|
|
|
|
if (!AZ::RPI::JsonUtils::SaveObjectToFile(m_absolutePath, sourceData))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
AZ_Error("MaterialDocument", false, "Material document could not be saved: '%s'.", m_absolutePath.c_str());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// after saving, reset to a clean state
|
|
|
|
// after saving, reset to a clean state
|
|
|
|
@ -262,181 +193,82 @@ 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());
|
|
|
|
// SaveFailed has already been called so just forward the result without additional notifications.
|
|
|
|
|
|
|
|
// TODO Replace bool return value with enum for open and save states.
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!IsSavable())
|
|
|
|
// populate sourceData with modified or overridden properties and save object
|
|
|
|
{
|
|
|
|
AZ::RPI::MaterialSourceData sourceData;
|
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// create source data from properties
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
auto propertyFilter = [](const AtomToolsFramework::DynamicProperty& property) {
|
|
|
|
// populate sourceData with modified or overwritten properties
|
|
|
|
|
|
|
|
const bool savedProperties = SavePropertiesToSourceData(normalizedSavePath, 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)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// write sourceData to .material file
|
|
|
|
if (!SaveSourceData(sourceData, propertyFilter))
|
|
|
|
if (!AZ::RPI::JsonUtils::SaveObjectToFile(normalizedSavePath, sourceData))
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
AZ_Error("MaterialDocument", false, "Material document could not be saved: '%s'.", normalizedSavePath.c_str());
|
|
|
|
return SaveFailed();
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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());
|
|
|
|
// SaveFailed has already been called so just forward the result without additional notifications.
|
|
|
|
|
|
|
|
// TODO Replace bool return value with enum for open and save states.
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AZStd::string normalizedSavePath = savePath;
|
|
|
|
// populate sourceData with modified or overridden properties and save object
|
|
|
|
if (!AzFramework::StringFunc::Path::Normalize(normalizedSavePath))
|
|
|
|
AZ::RPI::MaterialSourceData sourceData;
|
|
|
|
{
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// create source data from properties
|
|
|
|
|
|
|
|
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_relativePath.c_str(), MaterialSourceData::Extension))
|
|
|
|
if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.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
|
|
|
|
auto propertyFilter = [](const AtomToolsFramework::DynamicProperty& property) {
|
|
|
|
const bool savedProperties = SavePropertiesToSourceData(normalizedSavePath, 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)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// write sourceData to .material file
|
|
|
|
if (!SaveSourceData(sourceData, propertyFilter))
|
|
|
|
if (!AZ::RPI::JsonUtils::SaveObjectToFile(normalizedSavePath, sourceData))
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
AZ_Error("MaterialDocument", false, "Material document could not be saved: '%s'.", normalizedSavePath.c_str());
|
|
|
|
return SaveFailed();
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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 +286,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 +318,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,52 +339,25 @@ namespace MaterialEditor
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MaterialDocument::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, [[maybe_unused]] AZ::Uuid sourceUUID)
|
|
|
|
bool MaterialDocument::SaveSourceData(AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const
|
|
|
|
{
|
|
|
|
|
|
|
|
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(
|
|
|
|
|
|
|
|
const AZStd::string& exportPath, AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
using namespace AZ;
|
|
|
|
bool addPropertiesResult = true;
|
|
|
|
using namespace RPI;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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([&, this](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(m_savePathNormalized, propertyId, *propertyDefinition, propertyValue))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
AZ_Error("MaterialDocument", false, "Material document 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;
|
|
|
|
addPropertiesResult = false;
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -610,55 +369,36 @@ namespace MaterialEditor
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
if (!addPropertiesResult)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool MaterialDocument::OpenInternal(AZStd::string_view loadPath)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
using namespace AZ;
|
|
|
|
|
|
|
|
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());
|
|
|
|
AZ_Error("MaterialDocument", false, "Document properties could not be saved: '%s'.", m_savePathNormalized.c_str());
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (AzFramework::StringFunc::Path::IsRelative(m_absolutePath.c_str()))
|
|
|
|
if (!AZ::RPI::JsonUtils::SaveObjectToFile(m_savePathNormalized, sourceData))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
AZ_Error("MaterialDocument", false, "Material document path must be absolute: '%s'.", m_absolutePath.c_str());
|
|
|
|
AZ_Error("MaterialDocument", false, "Document could not be saved: '%s'.", m_savePathNormalized.c_str());
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool result = false;
|
|
|
|
return true;
|
|
|
|
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;
|
|
|
|
bool MaterialDocument::Open(AZStd::string_view loadPath)
|
|
|
|
if (!AzFramework::StringFunc::Path::Normalize(m_relativePath))
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!AtomToolsDocument::Open(loadPath))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
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 +406,34 @@ namespace MaterialEditor
|
|
|
|
if (!m_materialSourceData.m_parentMaterial.empty())
|
|
|
|
if (!m_materialSourceData.m_parentMaterial.empty())
|
|
|
|
{
|
|
|
|
{
|
|
|
|
m_materialSourceData.m_parentMaterial =
|
|
|
|
m_materialSourceData.m_parentMaterial =
|
|
|
|
AssetUtils::ResolvePathReference(m_absolutePath, m_materialSourceData.m_parentMaterial);
|
|
|
|
AZ::RPI::AssetUtils::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 +443,8 @@ namespace MaterialEditor
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
AZ_Error("MaterialDocument", false, "Material document extension not supported: '%s'.", m_absolutePath.c_str());
|
|
|
|
AZ_Error("MaterialDocument", false, "Document extension not supported: '%s'.", m_absolutePath.c_str());
|
|
|
|
return false;
|
|
|
|
return OpenFailed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const bool elevateWarnings = false;
|
|
|
|
const bool elevateWarnings = false;
|
|
|
|
@ -714,44 +455,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 = AssetUtils::MakeAssetId(m_materialSourceData.m_parentMaterial, 0);
|
|
|
|
const auto parentMaterialAssetIdResult = AZ::RPI::AssetUtils::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 +500,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 +508,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 +522,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 +554,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 +597,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 +619,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 (AZ::RPI::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();
|
|
|
|
AZ::RPI::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 +636,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 (AZ::RPI::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();
|
|
|
|
AZ::RPI::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 +671,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 +713,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 +793,4 @@ namespace MaterialEditor
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace MaterialEditor
|
|
|
|
} // namespace MaterialEditor
|
|
|
|
|