Created function to get relative paths to referenced files that will fall back to asset folder relative paths under certain conditions

Signed-off-by: Guthrie Adams <guthadam@amazon.com>
monroegm-disable-blank-issue-2
Guthrie Adams 4 years ago
parent f18f838da6
commit ed4f7965da

@ -45,10 +45,22 @@ namespace AtomToolsFramework
//! Convert the property value into the format that will be stored in the source data //! Convert the property value into the format that will be stored in the source data
//! This is primarily needed to support conversions of special types like enums and images //! This is primarily needed to support conversions of special types like enums and images
bool ConvertToExportFormat( bool ConvertToExportFormat(
const AZ::IO::BasicPath<AZStd::string>& exportFolder, const AZStd::string& exportPath,
const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition, const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition,
AZ::RPI::MaterialPropertyValue& propertyValue); AZ::RPI::MaterialPropertyValue& propertyValue);
//! Generate a file path from the exported file to the external reference.
//! This function is to support copying or moving a folder containing materials, models, and textures without modifying the files. The
//! general case returns a relative path from the export file to the reference file. If the reference path is too different or distant
//! from the export path then it might be more difficult to work with than an asset folder relative path. For example, material types
//! that Atom provides live in a folder that should be accessible from anywhere. When materials are created in arbitrary gems and
//! project folders, a relative path to the material type would need to be updated whenever the materials are copied or moved. The same
//! thing will happen with parent materials or textures if their paths cant be resolved. To alleviate some of this, we use the asset
//! folder relative path if the export folder relative path is too complex. An alternate solution would be to only use export folder
//! relative paths if the referenced path is in the same folder or a sub folder the assets are not generally packaged like that.
AZStd::string GetExteralReferencePath(
const AZStd::string& exportPath, const AZStd::string& referencePath, const uint32_t maxPathDepth = 2);
//! Traverse up the instance data node hierarchy to find the containing dynamic property object //! Traverse up the instance data node hierarchy to find the containing dynamic property object
const AtomToolsFramework::DynamicProperty* FindDynamicPropertyForInstanceDataNode(const AzToolsFramework::InstanceDataNode* pNode); const AtomToolsFramework::DynamicProperty* FindDynamicPropertyForInstanceDataNode(const AzToolsFramework::InstanceDataNode* pNode);
} // namespace AtomToolsFramework } // namespace AtomToolsFramework

@ -166,10 +166,13 @@ namespace AtomToolsFramework
} }
bool ConvertToExportFormat( bool ConvertToExportFormat(
const AZ::IO::BasicPath<AZStd::string>& exportFolder, const AZStd::string& exportPath,
const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition, const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition,
AZ::RPI::MaterialPropertyValue& propertyValue) AZ::RPI::MaterialPropertyValue& propertyValue)
{ {
AZ::IO::BasicPath<AZStd::string> exportFolder(exportPath);
exportFolder.RemoveFilename();
if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Enum && propertyValue.Is<uint32_t>()) if (propertyDefinition.m_dataType == AZ::RPI::MaterialPropertyDataType::Enum && propertyValue.Is<uint32_t>())
{ {
const uint32_t index = propertyValue.GetValue<uint32_t>(); const uint32_t index = propertyValue.GetValue<uint32_t>();
@ -206,6 +209,47 @@ namespace AtomToolsFramework
return true; return true;
} }
AZStd::string GetExteralReferencePath(const AZStd::string& exportPath, const AZStd::string& referencePath, const uint32_t maxPathDepth)
{
if (referencePath.empty())
{
return {};
}
AZ::IO::BasicPath<AZStd::string> exportFolder(exportPath);
exportFolder.RemoveFilename();
const AZStd::string relativePath = AZ::IO::PathView(referencePath).LexicallyRelative(exportFolder).StringAsPosix();
// Count the difference in depth between the export file path and the referenced file path.
uint32_t parentFolderCount = 0;
AZStd::string::size_type pos = 0;
const AZStd::string parentFolderToken = "..";
while ((pos = relativePath.find(parentFolderToken, pos)) != AZStd::string::npos)
{
parentFolderCount++;
pos += parentFolderToken.length();
}
// If the difference in depth is too great then revert to using the asset folder relative path.
// We could change this to only use relative paths for references in subfolders.
if (parentFolderCount > maxPathDepth)
{
AZStd::string watchFolder;
AZ::Data::AssetInfo assetInfo;
bool sourceInfoFound = false;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, referencePath.c_str(),
assetInfo, watchFolder);
if (sourceInfoFound)
{
return assetInfo.m_relativePath;
}
}
return relativePath;
}
const AtomToolsFramework::DynamicProperty* FindDynamicPropertyForInstanceDataNode(const AzToolsFramework::InstanceDataNode* pNode) const AtomToolsFramework::DynamicProperty* FindDynamicPropertyForInstanceDataNode(const AzToolsFramework::InstanceDataNode* pNode)
{ {
// Traverse up the hierarchy from the input node to search for an instance corresponding to material inspector property // Traverse up the hierarchy from the input node to search for an instance corresponding to material inspector property

@ -228,18 +228,15 @@ namespace MaterialEditor
return false; return false;
} }
AZ::IO::BasicPath<AZStd::string> exportFolder(m_absolutePath);
exportFolder.RemoveFilename();
// create source data from properties // create source data from properties
MaterialSourceData sourceData; MaterialSourceData sourceData;
sourceData.m_propertyLayoutVersion = m_materialTypeSourceData.m_propertyLayout.m_version; sourceData.m_materialTypeVersion = m_materialAsset->GetMaterialTypeAsset()->GetVersion();
sourceData.m_materialType = AZ::IO::PathView(m_materialSourceData.m_materialType).LexicallyRelative(exportFolder).StringAsPosix(); sourceData.m_materialType = AtomToolsFramework::GetExteralReferencePath(m_absolutePath, m_materialSourceData.m_materialType);
sourceData.m_parentMaterial = AZ::IO::PathView(m_materialSourceData.m_parentMaterial).LexicallyRelative(exportFolder).StringAsPosix(); sourceData.m_parentMaterial = AtomToolsFramework::GetExteralReferencePath(m_absolutePath, m_materialSourceData.m_parentMaterial);
// populate sourceData with modified or overwritten properties // populate sourceData with modified or overwritten properties
const bool savedProperties = SavePropertiesToSourceData( const bool savedProperties = SavePropertiesToSourceData(
exportFolder, sourceData, m_absolutePath, sourceData,
[](const AtomToolsFramework::DynamicProperty& property) [](const AtomToolsFramework::DynamicProperty& property)
{ {
return !AtomToolsFramework::ArePropertyValuesEqual(property.GetValue(), property.GetConfig().m_parentValue); return !AtomToolsFramework::ArePropertyValuesEqual(property.GetValue(), property.GetConfig().m_parentValue);
@ -302,18 +299,16 @@ namespace MaterialEditor
return false; return false;
} }
AZ::IO::BasicPath<AZStd::string> exportFolder(normalizedSavePath);
exportFolder.RemoveFilename();
// create source data from properties // create source data from properties
MaterialSourceData sourceData; MaterialSourceData sourceData;
sourceData.m_propertyLayoutVersion = m_materialTypeSourceData.m_propertyLayout.m_version; sourceData.m_materialTypeVersion = m_materialAsset->GetMaterialTypeAsset()->GetVersion();
sourceData.m_materialType = AZ::IO::PathView(m_materialSourceData.m_materialType).LexicallyRelative(exportFolder).StringAsPosix(); sourceData.m_materialType = AtomToolsFramework::GetExteralReferencePath(normalizedSavePath, m_materialSourceData.m_materialType);
sourceData.m_parentMaterial = AZ::IO::PathView(m_materialSourceData.m_parentMaterial).LexicallyRelative(exportFolder).StringAsPosix(); sourceData.m_parentMaterial =
AtomToolsFramework::GetExteralReferencePath(normalizedSavePath, m_materialSourceData.m_parentMaterial);
// populate sourceData with modified or overwritten properties // populate sourceData with modified or overwritten properties
const bool savedProperties = SavePropertiesToSourceData( const bool savedProperties = SavePropertiesToSourceData(
exportFolder, sourceData, normalizedSavePath, sourceData,
[](const AtomToolsFramework::DynamicProperty& property) [](const AtomToolsFramework::DynamicProperty& property)
{ {
return !AtomToolsFramework::ArePropertyValuesEqual(property.GetValue(), property.GetConfig().m_parentValue); return !AtomToolsFramework::ArePropertyValuesEqual(property.GetValue(), property.GetConfig().m_parentValue);
@ -375,27 +370,21 @@ namespace MaterialEditor
return false; return false;
} }
AZ::IO::BasicPath<AZStd::string> exportFolder(normalizedSavePath);
exportFolder.RemoveFilename();
// create source data from properties // create source data from properties
MaterialSourceData sourceData; MaterialSourceData sourceData;
sourceData.m_propertyLayoutVersion = m_materialTypeSourceData.m_propertyLayout.m_version; sourceData.m_materialTypeVersion = m_materialAsset->GetMaterialTypeAsset()->GetVersion();
sourceData.m_materialType = AZ::IO::PathView(m_materialSourceData.m_materialType).LexicallyRelative(exportFolder).StringAsPosix(); sourceData.m_materialType = AtomToolsFramework::GetExteralReferencePath(normalizedSavePath, 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_relativePath.c_str(), MaterialSourceData::Extension))
{ {
sourceData.m_parentMaterial = AZ::IO::PathView(m_absolutePath).LexicallyRelative(exportFolder).StringAsPosix(); sourceData.m_parentMaterial =
AtomToolsFramework::GetExteralReferencePath(normalizedSavePath, m_materialSourceData.m_parentMaterial);
} }
// Force save data to store forward slashes
AzFramework::StringFunc::Replace(sourceData.m_materialType, "\\", "/");
AzFramework::StringFunc::Replace(sourceData.m_parentMaterial, "\\", "/");
// populate sourceData with modified properties // populate sourceData with modified properties
const bool savedProperties = SavePropertiesToSourceData( const bool savedProperties = SavePropertiesToSourceData(
exportFolder, sourceData, normalizedSavePath, sourceData,
[](const AtomToolsFramework::DynamicProperty& property) [](const AtomToolsFramework::DynamicProperty& property)
{ {
return !AtomToolsFramework::ArePropertyValuesEqual(property.GetValue(), property.GetConfig().m_originalValue); return !AtomToolsFramework::ArePropertyValuesEqual(property.GetValue(), property.GetConfig().m_originalValue);
@ -595,9 +584,7 @@ namespace MaterialEditor
} }
bool MaterialDocument::SavePropertiesToSourceData( bool MaterialDocument::SavePropertiesToSourceData(
const AZ::IO::BasicPath<AZStd::string>& exportFolder, const AZStd::string& exportPath, AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const
AZ::RPI::MaterialSourceData& sourceData,
PropertyFilterFunction propertyFilter) const
{ {
using namespace AZ; using namespace AZ;
using namespace RPI; using namespace RPI;
@ -615,7 +602,7 @@ namespace MaterialEditor
MaterialPropertyValue propertyValue = AtomToolsFramework::ConvertToRuntimeType(it->second.GetValue()); MaterialPropertyValue propertyValue = AtomToolsFramework::ConvertToRuntimeType(it->second.GetValue());
if (propertyValue.IsValid()) if (propertyValue.IsValid())
{ {
if (!AtomToolsFramework::ConvertToExportFormat(exportFolder, propertyDefinition, propertyValue)) if (!AtomToolsFramework::ConvertToExportFormat(exportPath, propertyDefinition, propertyValue))
{ {
AZ_Error("MaterialDocument", false, "Material document property could not be converted: '%s' in '%s'.", propertyId.GetFullName().GetCStr(), m_absolutePath.c_str()); AZ_Error("MaterialDocument", false, "Material document property could not be converted: '%s' in '%s'.", propertyId.GetFullName().GetCStr(), m_absolutePath.c_str());
result = false; result = false;
@ -699,6 +686,12 @@ namespace MaterialEditor
return false; return false;
} }
m_materialTypeSourceData = materialTypeOutcome.GetValue(); m_materialTypeSourceData = materialTypeOutcome.GetValue();
if (MaterialSourceData::ApplyVersionUpdatesResult::Failed == m_materialSourceData.ApplyVersionUpdates(m_absolutePath))
{
AZ_Error("MaterialDocument", false, "Material source data could not be auto updated to the latest version of the material type: '%s'.", m_materialSourceData.m_materialType.c_str());
return false;
}
} }
else if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), MaterialTypeSourceData::Extension)) else if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), MaterialTypeSourceData::Extension))
{ {

@ -105,9 +105,7 @@ namespace MaterialEditor
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
bool SavePropertiesToSourceData( bool SavePropertiesToSourceData(
const AZ::IO::BasicPath<AZStd::string>& exportFolder, const AZStd::string& exportPath, AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const;
AZ::RPI::MaterialSourceData& sourceData,
PropertyFilterFunction propertyFilter) const;
bool OpenInternal(AZStd::string_view loadPath); bool OpenInternal(AZStd::string_view loadPath);

@ -98,9 +98,6 @@ namespace AZ
bool SaveSourceMaterialFromEditData(const AZStd::string& path, const MaterialEditData& editData) bool SaveSourceMaterialFromEditData(const AZStd::string& path, const MaterialEditData& editData)
{ {
AZ::IO::BasicPath<AZStd::string> exportFolder(path);
exportFolder.RemoveFilename();
// Construct the material source data object that will be exported // Construct the material source data object that will be exported
AZ::RPI::MaterialSourceData exportData; AZ::RPI::MaterialSourceData exportData;
@ -121,8 +118,7 @@ namespace AZ
return false; return false;
} }
exportData.m_materialType = exportData.m_materialType = AtomToolsFramework::GetExteralReferencePath(path, editData.m_materialTypeSourcePath);
AZ::IO::PathView(editData.m_materialTypeSourcePath).LexicallyRelative(exportFolder).StringAsPosix();
} }
if (!editData.m_materialParentSourcePath.empty()) if (!editData.m_materialParentSourcePath.empty())
@ -141,8 +137,7 @@ namespace AZ
return false; return false;
} }
exportData.m_parentMaterial = exportData.m_parentMaterial = AtomToolsFramework::GetExteralReferencePath(path, editData.m_materialParentSourcePath);
AZ::IO::PathView(editData.m_materialParentSourcePath).LexicallyRelative(exportFolder).StringAsPosix();
} }
// Copy all of the properties from the material asset to the source data that will be exported // Copy all of the properties from the material asset to the source data that will be exported
@ -168,7 +163,7 @@ namespace AZ
propertyValue = AZ::RPI::MaterialPropertyValue::FromAny(propertyOverrideItr->second); propertyValue = AZ::RPI::MaterialPropertyValue::FromAny(propertyOverrideItr->second);
} }
if (!AtomToolsFramework::ConvertToExportFormat(exportFolder, propertyDefinition, propertyValue)) if (!AtomToolsFramework::ConvertToExportFormat(path, propertyDefinition, propertyValue))
{ {
AZ_Error("AZ::Render::EditorMaterialComponentUtil", false, "Failed to export: %s", path.c_str()); AZ_Error("AZ::Render::EditorMaterialComponentUtil", false, "Failed to export: %s", path.c_str());
result = false; result = false;

Loading…
Cancel
Save