You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil...

167 lines
9.1 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Material/EditorMaterialComponentUtil.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Edit/Common/JsonUtils.h>
#include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
#include <Atom/RPI.Edit/Material/MaterialSourceData.h>
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
#include <Atom/RPI.Edit/Material/MaterialUtils.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
#include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialNameContext.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
namespace AZ
{
namespace Render
{
namespace EditorMaterialComponentUtil
{
bool LoadMaterialEditDataFromAssetId(const AZ::Data::AssetId& assetId, MaterialEditData& editData)
{
editData = MaterialEditData();
if (!assetId.IsValid())
{
AZ_Warning("AZ::Render::EditorMaterialComponentUtil", false, "Attempted to load material data for invalid asset id.");
return false;
}
editData.m_materialAssetId = assetId;
// Load the originating product asset from which the new source has set will be generated
auto materialAssetOutcome = AZ::RPI::AssetUtils::LoadAsset<AZ::RPI::MaterialAsset>(editData.m_materialAssetId);
if (!materialAssetOutcome)
{
AZ_Error("AZ::Render::EditorMaterialComponentUtil", false, "Failed to load material asset: %s", editData.m_materialAssetId.ToString<AZStd::string>().c_str());
return false;
}
editData.m_materialAsset = materialAssetOutcome.GetValue();
editData.m_materialTypeAsset = editData.m_materialAsset->GetMaterialTypeAsset();
editData.m_materialParentAsset = {};
editData.m_materialSourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(editData.m_materialAsset.GetId());
if (AzFramework::StringFunc::Path::IsExtension(
editData.m_materialSourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension))
{
if (!AZ::RPI::JsonUtils::LoadObjectFromFile(editData.m_materialSourcePath, editData.m_materialSourceData))
{
AZ_Error("AZ::Render::EditorMaterialComponentUtil", false, "Material source data could not be loaded: '%s'.", editData.m_materialSourcePath.c_str());
return false;
}
}
if (!editData.m_materialSourceData.m_parentMaterial.empty())
{
// There is a parent for this material
auto parentMaterialResult = AZ::RPI::AssetUtils::LoadAsset<AZ::RPI::MaterialAsset>(editData.m_materialSourcePath, editData.m_materialSourceData.m_parentMaterial);
if (!parentMaterialResult)
{
AZ_Error("AZ::Render::EditorMaterialComponentUtil", false, "Parent material asset could not be loaded: '%s'.", editData.m_materialSourceData.m_parentMaterial.c_str());
return false;
}
editData.m_materialParentAsset = parentMaterialResult.GetValue();
editData.m_materialParentSourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(editData.m_materialParentAsset.GetId());
}
// We need a valid path to the material type source data to get the property layout and assign to the new material
editData.m_materialTypeSourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(editData.m_materialTypeAsset.GetId());
if (editData.m_materialTypeSourcePath.empty())
{
AZ_Error("AZ::Render::EditorMaterialComponentUtil", false, "Failed to locate source material type asset: %s", editData.m_materialAssetId.ToString<AZStd::string>().c_str());
return false;
}
// Load the material type source data
auto materialTypeOutcome = AZ::RPI::MaterialUtils::LoadMaterialTypeSourceData(editData.m_materialTypeSourcePath);
if (!materialTypeOutcome.IsSuccess())
{
AZ_Error("AZ::Render::EditorMaterialComponentUtil", false, "Failed to load material type source data: %s", editData.m_materialTypeSourcePath.c_str());
return false;
}
editData.m_materialTypeSourceData = materialTypeOutcome.TakeValue();
return true;
}
bool SaveSourceMaterialFromEditData(const AZStd::string& path, const MaterialEditData& editData)
{
if (path.empty() || !editData.m_materialAsset.IsReady() || !editData.m_materialTypeAsset.IsReady() ||
editData.m_materialTypeSourcePath.empty())
{
AZ_Error("AZ::Render::EditorMaterialComponentUtil", false, "Can not export: %s", path.c_str());
return false;
}
// Construct the material source data object that will be exported
AZ::RPI::MaterialSourceData exportData;
exportData.m_materialTypeVersion = editData.m_materialTypeAsset->GetVersion();
exportData.m_materialType = AtomToolsFramework::GetExteralReferencePath(path, editData.m_materialTypeSourcePath);
exportData.m_parentMaterial = AtomToolsFramework::GetExteralReferencePath(path, editData.m_materialParentSourcePath);
// Copy all of the properties from the material asset to the source data that will be exported
bool result = true;
editData.m_materialTypeSourceData.EnumerateProperties([&](const AZ::RPI::MaterialTypeSourceData::PropertyDefinition* propertyDefinition, const AZ::RPI::MaterialNameContext& nameContext)
{
AZ::Name propertyId{propertyDefinition->GetName()};
nameContext.ContextualizeProperty(propertyId);
const AZ::RPI::MaterialPropertyIndex propertyIndex =
editData.m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyId);
AZ::RPI::MaterialPropertyValue propertyValue =
editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()];
AZ::RPI::MaterialPropertyValue propertyValueDefault = propertyDefinition->m_value;
if (editData.m_materialParentAsset.IsReady())
{
propertyValueDefault = editData.m_materialParentAsset->GetPropertyValues()[propertyIndex.GetIndex()];
}
// Check for and apply any property overrides before saving property values
auto propertyOverrideItr = editData.m_materialPropertyOverrideMap.find(propertyId);
if (propertyOverrideItr != editData.m_materialPropertyOverrideMap.end())
{
propertyValue = AZ::RPI::MaterialPropertyValue::FromAny(propertyOverrideItr->second);
}
if (!AtomToolsFramework::ConvertToExportFormat(path, propertyId, *propertyDefinition, propertyValue))
{
AZ_Error("AZ::Render::EditorMaterialComponentUtil", false, "Failed to export: %s", path.c_str());
result = false;
return false;
}
// Don't export values if they are the same as the material type or parent
if (propertyValueDefault == propertyValue)
{
return true;
}
// TODO: Support populating the Material Editor with nested property groups, not just the top level.
const AZStd::string groupName = propertyId.GetStringView().substr(0, propertyId.GetStringView().size() - propertyDefinition->GetName().size() - 1);
exportData.m_properties[groupName][propertyDefinition->GetName()].m_value = propertyValue;
return true;
});
return result && AZ::RPI::JsonUtils::SaveObjectToFile(path, exportData);
}
} // namespace EditorMaterialComponentUtil
} // namespace Render
} // namespace AZ
//#include <AtomLyIntegration/CommonFeatures/moc_EditorMaterialComponentUtil.cpp>