Merge pull request #5512 from aws-lumberyard-dev/Atom/guthadam/cherrypick_material_source_data_loading
cherry pick material source data loading from stabilization to development
This commit is contained in:
@@ -31,6 +31,7 @@ namespace AZ
|
|||||||
static constexpr const char UvGroupName[] = "uvSets";
|
static constexpr const char UvGroupName[] = "uvSets";
|
||||||
|
|
||||||
class MaterialAsset;
|
class MaterialAsset;
|
||||||
|
class MaterialAssetCreator;
|
||||||
|
|
||||||
//! This is a simple data structure for serializing in/out material source files.
|
//! This is a simple data structure for serializing in/out material source files.
|
||||||
class MaterialSourceData final
|
class MaterialSourceData final
|
||||||
@@ -78,15 +79,33 @@ namespace AZ
|
|||||||
|
|
||||||
//! Creates a MaterialAsset from the MaterialSourceData content.
|
//! Creates a MaterialAsset from the MaterialSourceData content.
|
||||||
//! @param assetId ID for the MaterialAsset
|
//! @param assetId ID for the MaterialAsset
|
||||||
//! @param materialSourceFilePath Indicates the path of the .material file that the MaterialSourceData represents. Used for resolving file-relative paths.
|
//! @param materialSourceFilePath Indicates the path of the .material file that the MaterialSourceData represents. Used for
|
||||||
|
//! resolving file-relative paths.
|
||||||
//! @param elevateWarnings Indicates whether to treat warnings as errors
|
//! @param elevateWarnings Indicates whether to treat warnings as errors
|
||||||
//! @param includeMaterialPropertyNames Indicates whether to save material property names into the material asset file
|
//! @param includeMaterialPropertyNames Indicates whether to save material property names into the material asset file
|
||||||
Outcome<Data::Asset<MaterialAsset>> CreateMaterialAsset(
|
Outcome<Data::Asset<MaterialAsset>> CreateMaterialAsset(
|
||||||
Data::AssetId assetId,
|
Data::AssetId assetId,
|
||||||
AZStd::string_view materialSourceFilePath = "",
|
AZStd::string_view materialSourceFilePath = "",
|
||||||
bool elevateWarnings = true,
|
bool elevateWarnings = true,
|
||||||
bool includeMaterialPropertyNames = true
|
bool includeMaterialPropertyNames = true) const;
|
||||||
) const;
|
|
||||||
|
//! Creates a MaterialAsset from the MaterialSourceData content.
|
||||||
|
//! @param assetId ID for the MaterialAsset
|
||||||
|
//! @param materialSourceFilePath Indicates the path of the .material file that the MaterialSourceData represents. Used for
|
||||||
|
//! resolving file-relative paths.
|
||||||
|
//! @param elevateWarnings Indicates whether to treat warnings as errors
|
||||||
|
//! @param includeMaterialPropertyNames Indicates whether to save material property names into the material asset file
|
||||||
|
//! @param sourceDependencies if not null, will be populated with a set of all of the loaded material and material type paths
|
||||||
|
Outcome<Data::Asset<MaterialAsset>> CreateMaterialAssetFromSourceData(
|
||||||
|
Data::AssetId assetId,
|
||||||
|
AZStd::string_view materialSourceFilePath = "",
|
||||||
|
bool elevateWarnings = true,
|
||||||
|
bool includeMaterialPropertyNames = true,
|
||||||
|
AZStd::unordered_set<AZStd::string>* sourceDependencies = nullptr) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ApplyPropertiesToAssetCreator(
|
||||||
|
AZ::RPI::MaterialAssetCreator& materialAssetCreator, const AZStd::string_view& materialSourceFilePath) const;
|
||||||
};
|
};
|
||||||
} // namespace RPI
|
} // namespace RPI
|
||||||
} // namespace AZ
|
} // namespace AZ
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ namespace AZ
|
|||||||
|
|
||||||
ResetIssueCounts(); // Because the asset creator can be used multiple times
|
ResetIssueCounts(); // Because the asset creator can be used multiple times
|
||||||
|
|
||||||
m_asset = Data::AssetManager::Instance().CreateAsset<AssetDataT>(assetId, AZ::Data::AssetLoadBehavior::PreLoad);
|
m_asset = Data::Asset<AssetDataT>(assetId, aznew AssetDataT, AZ::Data::AssetLoadBehavior::PreLoad);
|
||||||
m_beginCalled = true;
|
m_beginCalled = true;
|
||||||
|
|
||||||
if (!m_asset)
|
if (!m_asset)
|
||||||
@@ -138,6 +138,7 @@ namespace AZ
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Data::AssetManager::Instance().AssignAssetData(m_asset);
|
||||||
result = AZStd::move(m_asset);
|
result = AZStd::move(m_asset);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,10 @@ namespace AZ
|
|||||||
}
|
}
|
||||||
else if (!m_luaSourceFile.empty())
|
else if (!m_luaSourceFile.empty())
|
||||||
{
|
{
|
||||||
auto loadOutcome = RPI::AssetUtils::LoadAsset<ScriptAsset>(materialTypeSourceFilePath, m_luaSourceFile);
|
// The sub ID for script assets must be explicit.
|
||||||
|
// LUA source files output a compiled as well as an uncompiled asset, sub Ids of 1 and 2.
|
||||||
|
auto loadOutcome =
|
||||||
|
RPI::AssetUtils::LoadAsset<ScriptAsset>(materialTypeSourceFilePath, m_luaSourceFile, ScriptAsset::CompiledAssetSubId);
|
||||||
if (!loadOutcome)
|
if (!loadOutcome)
|
||||||
{
|
{
|
||||||
AZ_Error("LuaMaterialFunctorSourceData", false, "Could not load script file '%s'", m_luaSourceFile.c_str());
|
AZ_Error("LuaMaterialFunctorSourceData", false, "Could not load script file '%s'", m_luaSourceFile.c_str());
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||||
#include <Atom/RPI.Edit/Common/JsonFileLoadContext.h>
|
#include <Atom/RPI.Edit/Common/JsonFileLoadContext.h>
|
||||||
#include <Atom/RPI.Edit/Common/JsonReportingHelper.h>
|
#include <Atom/RPI.Edit/Common/JsonReportingHelper.h>
|
||||||
|
#include <Atom/RPI.Edit/Common/JsonUtils.h>
|
||||||
|
|
||||||
#include <Atom/RPI.Reflect/Material/MaterialAssetCreator.h>
|
#include <Atom/RPI.Reflect/Material/MaterialAssetCreator.h>
|
||||||
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
|
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
|
||||||
@@ -126,7 +127,8 @@ namespace AZ
|
|||||||
return changesWereApplied ? ApplyVersionUpdatesResult::UpdatesApplied : ApplyVersionUpdatesResult::NoUpdates;
|
return changesWereApplied ? ApplyVersionUpdatesResult::UpdatesApplied : ApplyVersionUpdatesResult::NoUpdates;
|
||||||
}
|
}
|
||||||
|
|
||||||
Outcome<Data::Asset<MaterialAsset> > MaterialSourceData::CreateMaterialAsset(Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const
|
Outcome<Data::Asset<MaterialAsset>> MaterialSourceData::CreateMaterialAsset(
|
||||||
|
Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const
|
||||||
{
|
{
|
||||||
MaterialAssetCreator materialAssetCreator;
|
MaterialAssetCreator materialAssetCreator;
|
||||||
materialAssetCreator.SetElevateWarnings(elevateWarnings);
|
materialAssetCreator.SetElevateWarnings(elevateWarnings);
|
||||||
@@ -172,66 +174,7 @@ namespace AZ
|
|||||||
materialAssetCreator.Begin(assetId, *parentMaterialAsset.GetValue().Get(), includeMaterialPropertyNames);
|
materialAssetCreator.Begin(assetId, *parentMaterialAsset.GetValue().Get(), includeMaterialPropertyNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& group : m_properties)
|
ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath);
|
||||||
{
|
|
||||||
for (auto& property : group.second)
|
|
||||||
{
|
|
||||||
MaterialPropertyId propertyId{ group.first, property.first };
|
|
||||||
if (!property.second.m_value.IsValid())
|
|
||||||
{
|
|
||||||
AZ_Warning("Material source data", false, "Source data for material property value is invalid.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MaterialPropertyIndex propertyIndex = materialAssetCreator.m_materialPropertiesLayout->FindPropertyIndex(propertyId.GetFullName());
|
|
||||||
if (propertyIndex.IsValid())
|
|
||||||
{
|
|
||||||
const MaterialPropertyDescriptor* propertyDescriptor = materialAssetCreator.m_materialPropertiesLayout->GetPropertyDescriptor(propertyIndex);
|
|
||||||
switch (propertyDescriptor->GetDataType())
|
|
||||||
{
|
|
||||||
case MaterialPropertyDataType::Image:
|
|
||||||
{
|
|
||||||
Outcome<Data::Asset<ImageAsset>> imageAssetResult = MaterialUtils::GetImageAssetReference(materialSourceFilePath, property.second.m_value.GetValue<AZStd::string>());
|
|
||||||
|
|
||||||
if (imageAssetResult.IsSuccess())
|
|
||||||
{
|
|
||||||
auto& imageAsset = imageAssetResult.GetValue();
|
|
||||||
// Load referenced images when load material
|
|
||||||
imageAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad);
|
|
||||||
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
materialAssetCreator.ReportError("Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(), property.second.m_value.GetValue<AZStd::string>().data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MaterialPropertyDataType::Enum:
|
|
||||||
{
|
|
||||||
AZ::Name enumName = AZ::Name(property.second.m_value.GetValue<AZStd::string>());
|
|
||||||
uint32_t enumValue = propertyDescriptor->GetEnumValue(enumName);
|
|
||||||
if (enumValue == MaterialPropertyDescriptor::InvalidEnumValue)
|
|
||||||
{
|
|
||||||
materialAssetCreator.ReportError("Enum value '%s' couldn't be found in the 'enumValues' list", enumName.GetCStr());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), enumValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), property.second.m_value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
materialAssetCreator.ReportWarning("Can not find property id '%s' in MaterialPropertyLayout", propertyId.GetFullName().GetStringView().data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Data::Asset<MaterialAsset> material;
|
Data::Asset<MaterialAsset> material;
|
||||||
if (materialAssetCreator.End(material))
|
if (materialAssetCreator.End(material))
|
||||||
@@ -244,5 +187,183 @@ namespace AZ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Outcome<Data::Asset<MaterialAsset>> MaterialSourceData::CreateMaterialAssetFromSourceData(
|
||||||
|
Data::AssetId assetId,
|
||||||
|
AZStd::string_view materialSourceFilePath,
|
||||||
|
bool elevateWarnings,
|
||||||
|
bool includeMaterialPropertyNames,
|
||||||
|
AZStd::unordered_set<AZStd::string>* sourceDependencies) const
|
||||||
|
{
|
||||||
|
const auto materialTypeSourcePath = AssetUtils::ResolvePathReference(materialSourceFilePath, m_materialType);
|
||||||
|
const auto materialTypeAssetId = AssetUtils::MakeAssetId(materialTypeSourcePath, 0);
|
||||||
|
if (!materialTypeAssetId.IsSuccess())
|
||||||
|
{
|
||||||
|
AZ_Error("MaterialSourceData", false, "Failed to create material type asset ID: '%s'.", materialTypeSourcePath.c_str());
|
||||||
|
return Failure();
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTypeSourceData materialTypeSourceData;
|
||||||
|
if (!AZ::RPI::JsonUtils::LoadObjectFromFile(materialTypeSourcePath, materialTypeSourceData))
|
||||||
|
{
|
||||||
|
AZ_Error("MaterialSourceData", false, "Failed to load MaterialTypeSourceData: '%s'.", materialTypeSourcePath.c_str());
|
||||||
|
return Failure();
|
||||||
|
}
|
||||||
|
|
||||||
|
materialTypeSourceData.ResolveUvEnums();
|
||||||
|
|
||||||
|
const auto materialTypeAsset =
|
||||||
|
materialTypeSourceData.CreateMaterialTypeAsset(materialTypeAssetId.GetValue(), materialTypeSourcePath, elevateWarnings);
|
||||||
|
if (!materialTypeAsset.IsSuccess())
|
||||||
|
{
|
||||||
|
AZ_Error("MaterialSourceData", false, "Failed to create material type asset from source data: '%s'.", materialTypeSourcePath.c_str());
|
||||||
|
return Failure();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track all of the material and material type assets loaded while trying to create a material asset from source data. This will
|
||||||
|
// be used for evaluating circular dependencies and returned for external monitoring or other use.
|
||||||
|
AZStd::unordered_set<AZStd::string> dependencies;
|
||||||
|
dependencies.insert(materialSourceFilePath);
|
||||||
|
dependencies.insert(materialTypeSourcePath);
|
||||||
|
|
||||||
|
// Load and build a stack of MaterialSourceData from all of the parent materials in the hierarchy. Properties from the source
|
||||||
|
// data will be applied in reverse to the asset creator.
|
||||||
|
AZStd::vector<MaterialSourceData> parentSourceDataStack;
|
||||||
|
|
||||||
|
AZStd::string parentSourceRelPath = m_parentMaterial;
|
||||||
|
AZStd::string parentSourceAbsPath = AssetUtils::ResolvePathReference(materialSourceFilePath, parentSourceRelPath);
|
||||||
|
while (!parentSourceRelPath.empty())
|
||||||
|
{
|
||||||
|
if (!dependencies.insert(parentSourceAbsPath).second)
|
||||||
|
{
|
||||||
|
AZ_Error("MaterialSourceData", false, "Detected circular dependency between materials: '%s' and '%s'.", materialSourceFilePath.data(), parentSourceAbsPath.c_str());
|
||||||
|
return Failure();
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialSourceData parentSourceData;
|
||||||
|
if (!AZ::RPI::JsonUtils::LoadObjectFromFile(parentSourceAbsPath, parentSourceData))
|
||||||
|
{
|
||||||
|
AZ_Error("MaterialSourceData", false, "Failed to load MaterialSourceData for parent material: '%s'.", parentSourceAbsPath.c_str());
|
||||||
|
return Failure();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that all materials in the hierarchy share the same material type
|
||||||
|
const auto parentTypeAssetId = AssetUtils::MakeAssetId(parentSourceAbsPath, parentSourceData.m_materialType, 0);
|
||||||
|
if (!parentTypeAssetId)
|
||||||
|
{
|
||||||
|
AZ_Error("MaterialSourceData", false, "Parent material asset ID wasn't found: '%s'.", parentSourceAbsPath.c_str());
|
||||||
|
return Failure();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentTypeAssetId.GetValue() != materialTypeAssetId.GetValue())
|
||||||
|
{
|
||||||
|
AZ_Error("MaterialSourceData", false, "This material and its parent material do not share the same material type.");
|
||||||
|
return Failure();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the location of the next parent material and push the source data onto the stack
|
||||||
|
parentSourceRelPath = parentSourceData.m_parentMaterial;
|
||||||
|
parentSourceAbsPath = AssetUtils::ResolvePathReference(parentSourceAbsPath, parentSourceRelPath);
|
||||||
|
parentSourceDataStack.emplace_back(AZStd::move(parentSourceData));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the material asset from all the previously loaded source data
|
||||||
|
MaterialAssetCreator materialAssetCreator;
|
||||||
|
materialAssetCreator.SetElevateWarnings(elevateWarnings);
|
||||||
|
materialAssetCreator.Begin(assetId, *materialTypeAsset.GetValue().Get(), includeMaterialPropertyNames);
|
||||||
|
|
||||||
|
while (!parentSourceDataStack.empty())
|
||||||
|
{
|
||||||
|
parentSourceDataStack.back().ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath);
|
||||||
|
parentSourceDataStack.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath);
|
||||||
|
|
||||||
|
Data::Asset<MaterialAsset> material;
|
||||||
|
if (materialAssetCreator.End(material))
|
||||||
|
{
|
||||||
|
if (sourceDependencies)
|
||||||
|
{
|
||||||
|
sourceDependencies->insert(dependencies.begin(), dependencies.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Success(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Failure();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialSourceData::ApplyPropertiesToAssetCreator(
|
||||||
|
AZ::RPI::MaterialAssetCreator& materialAssetCreator, const AZStd::string_view& materialSourceFilePath) const
|
||||||
|
{
|
||||||
|
for (auto& group : m_properties)
|
||||||
|
{
|
||||||
|
for (auto& property : group.second)
|
||||||
|
{
|
||||||
|
MaterialPropertyId propertyId{ group.first, property.first };
|
||||||
|
if (!property.second.m_value.IsValid())
|
||||||
|
{
|
||||||
|
AZ_Warning("Material source data", false, "Source data for material property value is invalid.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MaterialPropertyIndex propertyIndex =
|
||||||
|
materialAssetCreator.m_materialPropertiesLayout->FindPropertyIndex(propertyId.GetFullName());
|
||||||
|
if (propertyIndex.IsValid())
|
||||||
|
{
|
||||||
|
const MaterialPropertyDescriptor* propertyDescriptor =
|
||||||
|
materialAssetCreator.m_materialPropertiesLayout->GetPropertyDescriptor(propertyIndex);
|
||||||
|
switch (propertyDescriptor->GetDataType())
|
||||||
|
{
|
||||||
|
case MaterialPropertyDataType::Image:
|
||||||
|
{
|
||||||
|
Outcome<Data::Asset<ImageAsset>> imageAssetResult = MaterialUtils::GetImageAssetReference(
|
||||||
|
materialSourceFilePath, property.second.m_value.GetValue<AZStd::string>());
|
||||||
|
|
||||||
|
if (imageAssetResult.IsSuccess())
|
||||||
|
{
|
||||||
|
auto& imageAsset = imageAssetResult.GetValue();
|
||||||
|
// Load referenced images when load material
|
||||||
|
imageAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad);
|
||||||
|
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
materialAssetCreator.ReportError(
|
||||||
|
"Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(),
|
||||||
|
property.second.m_value.GetValue<AZStd::string>().data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MaterialPropertyDataType::Enum:
|
||||||
|
{
|
||||||
|
AZ::Name enumName = AZ::Name(property.second.m_value.GetValue<AZStd::string>());
|
||||||
|
uint32_t enumValue = propertyDescriptor->GetEnumValue(enumName);
|
||||||
|
if (enumValue == MaterialPropertyDescriptor::InvalidEnumValue)
|
||||||
|
{
|
||||||
|
materialAssetCreator.ReportError(
|
||||||
|
"Enum value '%s' couldn't be found in the 'enumValues' list", enumName.GetCStr());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), enumValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), property.second.m_value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
materialAssetCreator.ReportWarning(
|
||||||
|
"Can not find property id '%s' in MaterialPropertyLayout", propertyId.GetFullName().GetStringView().data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace RPI
|
} // namespace RPI
|
||||||
} // namespace AZ
|
} // namespace AZ
|
||||||
|
|||||||
@@ -351,11 +351,12 @@ namespace AZ
|
|||||||
for (const ShaderVariantReferenceData& shaderRef : m_shaderCollection)
|
for (const ShaderVariantReferenceData& shaderRef : m_shaderCollection)
|
||||||
{
|
{
|
||||||
const auto& shaderFile = shaderRef.m_shaderFilePath;
|
const auto& shaderFile = shaderRef.m_shaderFilePath;
|
||||||
const auto& shaderAsset = AssetUtils::LoadAsset<ShaderAsset>(materialTypeSourceFilePath, shaderFile, 0);
|
auto shaderAssetResult = AssetUtils::LoadAsset<ShaderAsset>(materialTypeSourceFilePath, shaderFile, 0);
|
||||||
|
|
||||||
if (shaderAsset)
|
if (shaderAssetResult)
|
||||||
{
|
{
|
||||||
auto optionsLayout = shaderAsset.GetValue()->GetShaderOptionGroupLayout();
|
auto shaderAsset = shaderAssetResult.GetValue();
|
||||||
|
auto optionsLayout = shaderAsset->GetShaderOptionGroupLayout();
|
||||||
ShaderOptionGroup options{ optionsLayout };
|
ShaderOptionGroup options{ optionsLayout };
|
||||||
for (auto& iter : shaderRef.m_shaderOptionValues)
|
for (auto& iter : shaderRef.m_shaderOptionValues)
|
||||||
{
|
{
|
||||||
@@ -366,12 +367,11 @@ namespace AZ
|
|||||||
}
|
}
|
||||||
|
|
||||||
materialTypeAssetCreator.AddShader(
|
materialTypeAssetCreator.AddShader(
|
||||||
shaderAsset.GetValue(), options.GetShaderVariantId(),
|
shaderAsset, options.GetShaderVariantId(),
|
||||||
shaderRef.m_shaderTag.IsEmpty() ? Uuid::CreateRandom().ToString<AZ::Name>() : shaderRef.m_shaderTag
|
shaderRef.m_shaderTag.IsEmpty() ? Uuid::CreateRandom().ToString<AZ::Name>() : shaderRef.m_shaderTag);
|
||||||
);
|
|
||||||
|
|
||||||
// Gather UV names
|
// Gather UV names
|
||||||
const ShaderInputContract& shaderInputContract = shaderAsset.GetValue()->GetInputContract();
|
const ShaderInputContract& shaderInputContract = shaderAsset->GetInputContract();
|
||||||
for (const ShaderInputContract::StreamChannelInfo& channel : shaderInputContract.m_streamChannels)
|
for (const ShaderInputContract::StreamChannelInfo& channel : shaderInputContract.m_streamChannels)
|
||||||
{
|
{
|
||||||
const RHI::ShaderSemantic& semantic = channel.m_semantic;
|
const RHI::ShaderSemantic& semantic = channel.m_semantic;
|
||||||
@@ -451,15 +451,19 @@ namespace AZ
|
|||||||
{
|
{
|
||||||
case MaterialPropertyDataType::Image:
|
case MaterialPropertyDataType::Image:
|
||||||
{
|
{
|
||||||
Outcome<Data::Asset<ImageAsset>> imageAssetResult = MaterialUtils::GetImageAssetReference(materialTypeSourceFilePath, property.m_value.GetValue<AZStd::string>());
|
auto imageAssetResult = MaterialUtils::GetImageAssetReference(
|
||||||
|
materialTypeSourceFilePath, property.m_value.GetValue<AZStd::string>());
|
||||||
|
|
||||||
if (imageAssetResult.IsSuccess())
|
if (imageAssetResult)
|
||||||
{
|
{
|
||||||
materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAssetResult.GetValue());
|
auto imageAsset = imageAssetResult.GetValue();
|
||||||
|
materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
materialTypeAssetCreator.ReportError("Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(), property.m_value.GetValue<AZStd::string>().data());
|
materialTypeAssetCreator.ReportError(
|
||||||
|
"Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(),
|
||||||
|
property.m_value.GetValue<AZStd::string>().data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -188,6 +188,10 @@ namespace AZ
|
|||||||
void MaterialTypeAsset::SetReady()
|
void MaterialTypeAsset::SetReady()
|
||||||
{
|
{
|
||||||
m_status = AssetStatus::Ready;
|
m_status = AssetStatus::Ready;
|
||||||
|
|
||||||
|
// If this was created dynamically using MaterialTypeAssetCreator (which is what calls SetReady()),
|
||||||
|
// we need to connect to the AssetBus for reloads.
|
||||||
|
PostLoadInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MaterialTypeAsset::PostLoadInit()
|
bool MaterialTypeAsset::PostLoadInit()
|
||||||
|
|||||||
@@ -126,8 +126,8 @@ namespace AZ
|
|||||||
}
|
}
|
||||||
|
|
||||||
ShaderCollection::Item::Item()
|
ShaderCollection::Item::Item()
|
||||||
|
: m_renderStatesOverlay(RHI::GetInvalidRenderStates())
|
||||||
{
|
{
|
||||||
m_renderStatesOverlay = RHI::GetInvalidRenderStates();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderCollection::Item& ShaderCollection::operator[](size_t i)
|
ShaderCollection::Item& ShaderCollection::operator[](size_t i)
|
||||||
@@ -156,7 +156,8 @@ namespace AZ
|
|||||||
}
|
}
|
||||||
|
|
||||||
ShaderCollection::Item::Item(const Data::Asset<ShaderAsset>& shaderAsset, const AZ::Name& shaderTag, ShaderVariantId variantId)
|
ShaderCollection::Item::Item(const Data::Asset<ShaderAsset>& shaderAsset, const AZ::Name& shaderTag, ShaderVariantId variantId)
|
||||||
: m_shaderAsset(shaderAsset)
|
: m_renderStatesOverlay(RHI::GetInvalidRenderStates())
|
||||||
|
, m_shaderAsset(shaderAsset)
|
||||||
, m_shaderVariantId(variantId)
|
, m_shaderVariantId(variantId)
|
||||||
, m_shaderTag(shaderTag)
|
, m_shaderTag(shaderTag)
|
||||||
, m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId)
|
, m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId)
|
||||||
@@ -164,7 +165,8 @@ namespace AZ
|
|||||||
}
|
}
|
||||||
|
|
||||||
ShaderCollection::Item::Item(Data::Asset<ShaderAsset>&& shaderAsset, const AZ::Name& shaderTag, ShaderVariantId variantId)
|
ShaderCollection::Item::Item(Data::Asset<ShaderAsset>&& shaderAsset, const AZ::Name& shaderTag, ShaderVariantId variantId)
|
||||||
: m_shaderAsset(AZStd::move(shaderAsset))
|
: m_renderStatesOverlay(RHI::GetInvalidRenderStates())
|
||||||
|
, m_shaderAsset(AZStd::move(shaderAsset))
|
||||||
, m_shaderVariantId(variantId)
|
, m_shaderVariantId(variantId)
|
||||||
, m_shaderTag(shaderTag)
|
, m_shaderTag(shaderTag)
|
||||||
, m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId)
|
, m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId)
|
||||||
|
|||||||
+8
-6
@@ -159,7 +159,7 @@ namespace AtomToolsFramework
|
|||||||
|
|
||||||
void AtomToolsDocumentSystemComponent::OnDocumentExternallyModified(const AZ::Uuid& documentId)
|
void AtomToolsDocumentSystemComponent::OnDocumentExternallyModified(const AZ::Uuid& documentId)
|
||||||
{
|
{
|
||||||
m_documentIdsToReopen.insert(documentId);
|
m_documentIdsWithExternalChanges.insert(documentId);
|
||||||
if (!AZ::TickBus::Handler::BusIsConnected())
|
if (!AZ::TickBus::Handler::BusIsConnected())
|
||||||
{
|
{
|
||||||
AZ::TickBus::Handler::BusConnect();
|
AZ::TickBus::Handler::BusConnect();
|
||||||
@@ -168,7 +168,7 @@ namespace AtomToolsFramework
|
|||||||
|
|
||||||
void AtomToolsDocumentSystemComponent::OnDocumentDependencyModified(const AZ::Uuid& documentId)
|
void AtomToolsDocumentSystemComponent::OnDocumentDependencyModified(const AZ::Uuid& documentId)
|
||||||
{
|
{
|
||||||
m_documentIdsToReopen.insert(documentId);
|
m_documentIdsWithDependencyChanges.insert(documentId);
|
||||||
if (!AZ::TickBus::Handler::BusIsConnected())
|
if (!AZ::TickBus::Handler::BusIsConnected())
|
||||||
{
|
{
|
||||||
AZ::TickBus::Handler::BusConnect();
|
AZ::TickBus::Handler::BusConnect();
|
||||||
@@ -177,7 +177,7 @@ namespace AtomToolsFramework
|
|||||||
|
|
||||||
void AtomToolsDocumentSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
void AtomToolsDocumentSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||||
{
|
{
|
||||||
for (const AZ::Uuid& documentId : m_documentIdsToReopen)
|
for (const AZ::Uuid& documentId : m_documentIdsWithExternalChanges)
|
||||||
{
|
{
|
||||||
AZStd::string documentPath;
|
AZStd::string documentPath;
|
||||||
AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
|
AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
|
||||||
@@ -191,6 +191,8 @@ namespace AtomToolsFramework
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_documentIdsWithDependencyChanges.erase(documentId);
|
||||||
|
|
||||||
AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
|
AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
|
||||||
|
|
||||||
bool openResult = false;
|
bool openResult = false;
|
||||||
@@ -204,7 +206,7 @@ namespace AtomToolsFramework
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const AZ::Uuid& documentId : m_documentIdsToReopen)
|
for (const AZ::Uuid& documentId : m_documentIdsWithDependencyChanges)
|
||||||
{
|
{
|
||||||
AZStd::string documentPath;
|
AZStd::string documentPath;
|
||||||
AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
|
AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
|
||||||
@@ -231,8 +233,8 @@ namespace AtomToolsFramework
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_documentIdsToReopen.clear();
|
m_documentIdsWithDependencyChanges.clear();
|
||||||
m_documentIdsToReopen.clear();
|
m_documentIdsWithExternalChanges.clear();
|
||||||
AZ::TickBus::Handler::BusDisconnect();
|
AZ::TickBus::Handler::BusDisconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -85,8 +85,8 @@ namespace AtomToolsFramework
|
|||||||
AZStd::intrusive_ptr<AtomToolsDocumentSystemSettings> m_settings;
|
AZStd::intrusive_ptr<AtomToolsDocumentSystemSettings> m_settings;
|
||||||
AZStd::function<AtomToolsDocument*()> m_documentCreator;
|
AZStd::function<AtomToolsDocument*()> m_documentCreator;
|
||||||
AZStd::unordered_map<AZ::Uuid, AZStd::shared_ptr<AtomToolsDocument>> m_documentMap;
|
AZStd::unordered_map<AZ::Uuid, AZStd::shared_ptr<AtomToolsDocument>> m_documentMap;
|
||||||
AZStd::unordered_set<AZ::Uuid> m_documentIdsToRebuild;
|
AZStd::unordered_set<AZ::Uuid> m_documentIdsWithExternalChanges;
|
||||||
AZStd::unordered_set<AZ::Uuid> m_documentIdsToReopen;
|
AZStd::unordered_set<AZ::Uuid> m_documentIdsWithDependencyChanges;
|
||||||
const size_t m_maxMessageBoxLineCount = 15;
|
const size_t m_maxMessageBoxLineCount = 15;
|
||||||
};
|
};
|
||||||
} // namespace AtomToolsFramework
|
} // namespace AtomToolsFramework
|
||||||
|
|||||||
@@ -552,26 +552,26 @@ namespace MaterialEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialDocument::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID)
|
void MaterialDocument::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, [[maybe_unused]] AZ::Uuid sourceUUID)
|
||||||
{
|
{
|
||||||
if (m_sourceAssetId.m_guid == sourceUUID)
|
auto sourcePath = AZ::RPI::AssetUtils::ResolvePathReference(scanFolder, relativePath);
|
||||||
|
|
||||||
|
if (m_absolutePath == sourcePath)
|
||||||
{
|
{
|
||||||
// ignore notifications caused by saving the open document
|
// ignore notifications caused by saving the open document
|
||||||
if (!m_saveTriggeredInternally)
|
if (!m_saveTriggeredInternally)
|
||||||
{
|
{
|
||||||
AZ_TracePrintf("MaterialDocument", "Material document changed externally: '%s'.\n", m_absolutePath.c_str());
|
AZ_TracePrintf("MaterialDocument", "Material document changed externally: '%s'.\n", m_absolutePath.c_str());
|
||||||
AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentExternallyModified, m_id);
|
AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
|
||||||
|
&AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentExternallyModified, m_id);
|
||||||
}
|
}
|
||||||
m_saveTriggeredInternally = false;
|
m_saveTriggeredInternally = false;
|
||||||
}
|
}
|
||||||
}
|
else if (m_sourceDependencies.find(sourcePath) != m_sourceDependencies.end())
|
||||||
|
|
||||||
void MaterialDocument::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
|
|
||||||
{
|
|
||||||
if (m_dependentAssetIds.find(asset->GetId()) != m_dependentAssetIds.end())
|
|
||||||
{
|
{
|
||||||
AZ_TracePrintf("MaterialDocument", "Material document dependency changed: '%s'.\n", m_absolutePath.c_str());
|
AZ_TracePrintf("MaterialDocument", "Material document dependency changed: '%s'.\n", m_absolutePath.c_str());
|
||||||
AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentDependencyModified, m_id);
|
AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
|
||||||
|
&AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentDependencyModified, m_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,7 +641,6 @@ namespace MaterialEditor
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sourceAssetId = sourceAssetInfo.m_assetId;
|
|
||||||
m_relativePath = sourceAssetInfo.m_relativePath;
|
m_relativePath = sourceAssetInfo.m_relativePath;
|
||||||
if (!AzFramework::StringFunc::Path::Normalize(m_relativePath))
|
if (!AzFramework::StringFunc::Path::Normalize(m_relativePath))
|
||||||
{
|
{
|
||||||
@@ -716,14 +715,15 @@ 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 createResult = m_materialSourceData.CreateMaterialAsset(Uuid::CreateRandom(), m_absolutePath, true);
|
auto materialAssetResult =
|
||||||
if (!createResult)
|
m_materialSourceData.CreateMaterialAssetFromSourceData(Uuid::CreateRandom(), m_absolutePath, true, true, &m_sourceDependencies);
|
||||||
|
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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_materialAsset = createResult.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());
|
||||||
@@ -737,28 +737,34 @@ namespace MaterialEditor
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// track material type asset to notify when dependencies change
|
|
||||||
m_dependentAssetIds.insert(materialTypeAsset->GetId());
|
|
||||||
AZ::Data::AssetBus::MultiHandler::BusConnect(materialTypeAsset->GetId());
|
|
||||||
|
|
||||||
AZStd::array_view<AZ::RPI::MaterialPropertyValue> parentPropertyValues = materialTypeAsset->GetDefaultPropertyValues();
|
AZStd::array_view<AZ::RPI::MaterialPropertyValue> parentPropertyValues = materialTypeAsset->GetDefaultPropertyValues();
|
||||||
AZ::Data::Asset<MaterialAsset> parentMaterialAsset;
|
AZ::Data::Asset<MaterialAsset> parentMaterialAsset;
|
||||||
if (!m_materialSourceData.m_parentMaterial.empty())
|
if (!m_materialSourceData.m_parentMaterial.empty())
|
||||||
{
|
{
|
||||||
// There is a parent for this material
|
AZ::RPI::MaterialSourceData parentMaterialSourceData;
|
||||||
auto parentMaterialResult = AssetUtils::LoadAsset<MaterialAsset>(m_absolutePath, m_materialSourceData.m_parentMaterial);
|
if (!AZ::RPI::JsonUtils::LoadObjectFromFile(m_materialSourceData.m_parentMaterial, parentMaterialSourceData))
|
||||||
if (!parentMaterialResult)
|
|
||||||
{
|
{
|
||||||
AZ_Error("MaterialDocument", false, "Parent material asset could not be loaded: '%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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
parentMaterialAsset = parentMaterialResult.GetValue();
|
const auto parentMaterialAssetIdResult = AssetUtils::MakeAssetId(m_materialSourceData.m_parentMaterial, 0);
|
||||||
parentPropertyValues = parentMaterialAsset->GetPropertyValues();
|
if (!parentMaterialAssetIdResult)
|
||||||
|
{
|
||||||
|
AZ_Error("MaterialDocument", false, "Material parent asset ID could not be created: '%s'.", m_materialSourceData.m_parentMaterial.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// track parent material asset to notify when dependencies change
|
auto parentMaterialAssetResult = parentMaterialSourceData.CreateMaterialAssetFromSourceData(
|
||||||
m_dependentAssetIds.insert(parentMaterialAsset->GetId());
|
parentMaterialAssetIdResult.GetValue(), m_materialSourceData.m_parentMaterial, true, true);
|
||||||
AZ::Data::AssetBus::MultiHandler::BusConnect(parentMaterialAsset->GetId());
|
if (!parentMaterialAssetResult)
|
||||||
|
{
|
||||||
|
AZ_Error("MaterialDocument", false, "Material parent asset could not be created from source data: '%s'.", m_materialSourceData.m_parentMaterial.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
parentMaterialAsset = parentMaterialAssetResult.GetValue();
|
||||||
|
parentPropertyValues = parentMaterialAsset->GetPropertyValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@@ -908,15 +914,13 @@ namespace MaterialEditor
|
|||||||
void MaterialDocument::Clear()
|
void MaterialDocument::Clear()
|
||||||
{
|
{
|
||||||
AZ::TickBus::Handler::BusDisconnect();
|
AZ::TickBus::Handler::BusDisconnect();
|
||||||
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
|
|
||||||
AzToolsFramework::AssetSystemBus::Handler::BusDisconnect();
|
AzToolsFramework::AssetSystemBus::Handler::BusDisconnect();
|
||||||
|
|
||||||
m_materialAsset = {};
|
m_materialAsset = {};
|
||||||
m_materialInstance = {};
|
m_materialInstance = {};
|
||||||
m_absolutePath.clear();
|
m_absolutePath.clear();
|
||||||
m_relativePath.clear();
|
m_relativePath.clear();
|
||||||
m_sourceAssetId = {};
|
m_sourceDependencies.clear();
|
||||||
m_dependentAssetIds.clear();
|
|
||||||
m_saveTriggeredInternally = {};
|
m_saveTriggeredInternally = {};
|
||||||
m_compilePending = {};
|
m_compilePending = {};
|
||||||
m_properties.clear();
|
m_properties.clear();
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ namespace MaterialEditor
|
|||||||
: public AtomToolsFramework::AtomToolsDocument
|
: public AtomToolsFramework::AtomToolsDocument
|
||||||
, public MaterialDocumentRequestBus::Handler
|
, public MaterialDocumentRequestBus::Handler
|
||||||
, private AZ::TickBus::Handler
|
, private AZ::TickBus::Handler
|
||||||
, private AZ::Data::AssetBus::MultiHandler
|
|
||||||
, private AzToolsFramework::AssetSystemBus::Handler
|
, private AzToolsFramework::AssetSystemBus::Handler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -105,11 +104,6 @@ namespace MaterialEditor
|
|||||||
void SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID) override;
|
void SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID) override;
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// AZ::Data::AssetBus::Router overrides...
|
|
||||||
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool SavePropertiesToSourceData(
|
bool SavePropertiesToSourceData(
|
||||||
const AZStd::string& exportPath, AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const;
|
const AZStd::string& exportPath, AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const;
|
||||||
|
|
||||||
@@ -138,11 +132,8 @@ namespace MaterialEditor
|
|||||||
// Material instance being edited
|
// Material instance being edited
|
||||||
AZ::Data::Instance<AZ::RPI::Material> m_materialInstance;
|
AZ::Data::Instance<AZ::RPI::Material> m_materialInstance;
|
||||||
|
|
||||||
// Asset used to open document
|
|
||||||
AZ::Data::AssetId m_sourceAssetId;
|
|
||||||
|
|
||||||
// Set of assets that can trigger a document reload
|
// Set of assets that can trigger a document reload
|
||||||
AZStd::unordered_set<AZ::Data::AssetId> m_dependentAssetIds;
|
AZStd::unordered_set<AZStd::string> m_sourceDependencies;
|
||||||
|
|
||||||
// Track if document saved itself last to skip external modification notification
|
// Track if document saved itself last to skip external modification notification
|
||||||
bool m_saveTriggeredInternally = false;
|
bool m_saveTriggeredInternally = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user