merging dev
Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com>
This commit is contained in:
@@ -24,6 +24,13 @@ namespace AZ
|
||||
AZ_RTTI(AZ::RPI::JsonMaterialPropertyValueSerializer, "{A52B1ED8-C849-4269-9AA7-9D0814D2EC59}", BaseJsonSerializer);
|
||||
AZ_CLASS_ALLOCATOR_DECL;
|
||||
|
||||
//! A LoadContext object must be passed down to the serializer via JsonDeserializerContext::GetMetadata().Add(...)
|
||||
struct LoadContext
|
||||
{
|
||||
AZ_TYPE_INFO(JsonMaterialPropertyValueSerializer::LoadContext, "{5E0A891A-27F6-4AD7-88A5-B9EA50F88B45}");
|
||||
uint32_t m_materialTypeVersion; //!< The version number from the .materialtype file
|
||||
};
|
||||
|
||||
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
|
||||
JsonDeserializerContext& context) override;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace AZ
|
||||
|
||||
AZStd::string m_parentMaterial; //!< The immediate parent of this material
|
||||
|
||||
uint32_t m_propertyLayoutVersion = 0; //!< The version of the property layout, defined in the material type, which was used to configure this material
|
||||
uint32_t m_materialTypeVersion = 0; //!< The version of the material type that was used to configure this material
|
||||
|
||||
struct Property
|
||||
{
|
||||
@@ -64,6 +64,18 @@ namespace AZ
|
||||
|
||||
PropertyGroupMap m_properties;
|
||||
|
||||
enum class ApplyVersionUpdatesResult
|
||||
{
|
||||
Failed,
|
||||
NoUpdates,
|
||||
UpdatesApplied
|
||||
};
|
||||
|
||||
//! Checks the material type version and potentially applies a series of property changes (most common are simple property renames)
|
||||
//! based on the MaterialTypeAsset's version update procedure.
|
||||
//! @param materialSourceFilePath Indicates the path of the .material file that the MaterialSourceData represents. Used for resolving file-relative paths.
|
||||
ApplyVersionUpdatesResult ApplyVersionUpdates(AZStd::string_view materialSourceFilePath = "");
|
||||
|
||||
//! 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.
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <Atom/RPI.Reflect/Base.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h>
|
||||
#include <Atom/RPI.Edit/Material/MaterialFunctorSourceData.h>
|
||||
#include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -119,12 +120,36 @@ namespace AZ
|
||||
|
||||
using PropertyList = AZStd::vector<PropertyDefinition>;
|
||||
|
||||
struct VersionUpdatesRenameOperationDefinition
|
||||
{
|
||||
AZ_TYPE_INFO(AZ::RPI::MaterialTypeSourceData::VersionUpdatesRenameOperationDefinition, "{F2295489-E15A-46CC-929F-8D42DEDBCF14}");
|
||||
|
||||
AZStd::string m_operation;
|
||||
|
||||
AZStd::string m_renameFrom;
|
||||
AZStd::string m_renameTo;
|
||||
};
|
||||
|
||||
// TODO: Support script operations--At that point, we'll likely need to replace VersionUpdatesRenameOperationDefinition with a more generic
|
||||
// data structure that has a custom JSON serialize. We will only be supporting rename for now.
|
||||
using VersionUpdateActions = AZStd::vector<VersionUpdatesRenameOperationDefinition>;
|
||||
|
||||
struct VersionUpdateDefinition
|
||||
{
|
||||
AZ_TYPE_INFO(AZ::RPI::MaterialTypeSourceData::VersionUpdateDefinition, "{2C9D3B91-0585-4BC9-91D2-4CF0C71BC4B7}");
|
||||
|
||||
uint32_t m_toVersion;
|
||||
VersionUpdateActions m_actions;
|
||||
};
|
||||
|
||||
using VersionUpdates = AZStd::vector<VersionUpdateDefinition>;
|
||||
|
||||
struct PropertyLayout
|
||||
{
|
||||
AZ_TYPE_INFO(AZ::RPI::MaterialTypeSourceData::PropertyLayout, "{AE53CF3F-5C3B-44F5-B2FB-306F0EB06393}");
|
||||
|
||||
//! Indicates the version of the set of available properties. Can be used to detect materials that might need to be updated.
|
||||
uint32_t m_version = 0;
|
||||
|
||||
//! This field is unused, and has been replaced by MaterialTypeSourceData::m_version below. It is kept for legacy file compatibility to suppress warnings and errors.
|
||||
uint32_t m_versionOld = 0;
|
||||
|
||||
//! List of groups that will contain the available properties
|
||||
AZStd::vector<GroupDefinition> m_groups;
|
||||
@@ -135,6 +160,11 @@ namespace AZ
|
||||
|
||||
AZStd::string m_description;
|
||||
|
||||
//! Version 1 is the default and should not contain any version update.
|
||||
uint32_t m_version = 1;
|
||||
|
||||
VersionUpdates m_versionUpdates;
|
||||
|
||||
PropertyLayout m_propertyLayout;
|
||||
|
||||
//! A list of shader variants that are always used at runtime; they cannot be turned off
|
||||
@@ -153,7 +183,12 @@ namespace AZ
|
||||
|
||||
const GroupDefinition* FindGroup(AZStd::string_view groupName) const;
|
||||
|
||||
const PropertyDefinition* FindProperty(AZStd::string_view groupName, AZStd::string_view propertyName) const;
|
||||
//! Searches for a specific property.
|
||||
//! Note this function can find properties using old versions of the property name; in that case,
|
||||
//! the name in the returned PropertyDefinition* will not match the @propertyName that was searched for.
|
||||
//! @param materialTypeVersion indicates the version number of the property name being passed in. Only renames above this version number will be applied.
|
||||
//! @return the requested property, or null if it could not be found
|
||||
const PropertyDefinition* FindProperty(AZStd::string_view groupName, AZStd::string_view propertyName, uint32_t materialTypeVersion = 0) const;
|
||||
|
||||
//! Construct a complete list of group definitions, including implicit groups, arranged in the same order as the source data
|
||||
//! Groups with the same name will be consolidated into a single entry
|
||||
@@ -179,6 +214,11 @@ namespace AZ
|
||||
bool ConvertPropertyValueToSourceDataFormat(const PropertyDefinition& propertyDefinition, MaterialPropertyValue& propertyValue) const;
|
||||
|
||||
Outcome<Data::Asset<MaterialTypeAsset>> CreateMaterialTypeAsset(Data::AssetId assetId, AZStd::string_view materialTypeSourceFilePath = "", bool elevateWarnings = true) const;
|
||||
|
||||
//! Possibly renames @propertyId based on the material version update steps.
|
||||
//! @param materialTypeVersion indicates the version number of the property name being passed in. Only renames above this version number will be applied.
|
||||
//! @return true if the property was renamed
|
||||
bool ApplyPropertyRenames(MaterialPropertyId& propertyId, uint32_t materialTypeVersion = 0) const;
|
||||
};
|
||||
|
||||
//! The wrapper class for derived material functors.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
namespace UnitTest
|
||||
{
|
||||
class MaterialTests;
|
||||
class MaterialAssetTests;
|
||||
}
|
||||
|
||||
namespace AZ
|
||||
@@ -42,10 +43,12 @@ namespace AZ
|
||||
, public MaterialReloadNotificationBus::Handler
|
||||
, public AssetInitBus::Handler
|
||||
{
|
||||
friend class MaterialVersionUpdate;
|
||||
friend class MaterialAssetCreator;
|
||||
friend class MaterialAssetHandler;
|
||||
friend class MaterialAssetCreatorCommon;
|
||||
friend class UnitTest::MaterialTests;
|
||||
friend class UnitTest::MaterialAssetTests;
|
||||
|
||||
public:
|
||||
AZ_RTTI(MaterialAsset, "{522C7BE0-501D-463E-92C6-15184A2B7AD8}", AZ::Data::AssetData);
|
||||
@@ -119,6 +122,10 @@ namespace AZ
|
||||
//! from m_materialTypeAsset.
|
||||
void RealignPropertyValuesAndNames();
|
||||
|
||||
//! Checks the material type version and potentially applies a series of property changes (most common are simple property renames)
|
||||
//! based on the MaterialTypeAsset's version update procedure.
|
||||
void ApplyVersionUpdates();
|
||||
|
||||
//! Called by asset creators to assign the asset to a ready state.
|
||||
void SetReady();
|
||||
|
||||
@@ -143,6 +150,10 @@ namespace AZ
|
||||
//! If empty, this implies that m_propertyValues is aligned with the entries in m_materialPropertiesLayout.
|
||||
AZStd::vector<AZ::Name> m_propertyNames;
|
||||
|
||||
//! The materialTypeVersion this materialAsset was based of. If the versions do not match at runtime when a
|
||||
//! materialTypeAsset is loaded, an update will be performed on m_propertyNames if populated.
|
||||
uint32_t m_materialTypeVersion = 1;
|
||||
|
||||
//! A flag to determine if m_propertyValues needs to be aligned with MaterialPropertiesLayout. Set to true whenever
|
||||
//! m_materialTypeAsset is reinitializing.
|
||||
bool m_isDirty = true;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <Atom/RPI.Reflect/Material/ShaderCollection.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialVersionUpdate.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -123,6 +124,11 @@ namespace AZ
|
||||
//! Returns a map from the UV shader inputs to a custom name.
|
||||
MaterialUvNameMap GetUvNameMap() const;
|
||||
|
||||
//! Returns the version of the MaterialTypeAsset.
|
||||
uint32_t GetVersion() const;
|
||||
|
||||
const AZStd::vector<MaterialVersionUpdate>& GetMaterialVersionUpdateList() const { return m_materialVersionUpdates; }
|
||||
|
||||
private:
|
||||
bool PostLoadInit() override;
|
||||
|
||||
@@ -162,6 +168,12 @@ namespace AZ
|
||||
//! Index in @m_shaderCollection of the shader asset that contains the ObjectSrg.
|
||||
uint32_t m_objectSrgShaderIndex = InvalidShaderIndex;
|
||||
|
||||
//! The version of this MaterialTypeAsset. If the version is greater than 1, actions performed
|
||||
//! to update this MaterialTypeAsset will be in m_materialVersionUpdateMap
|
||||
uint32_t m_version = 1;
|
||||
|
||||
//! Contains actions to perform for each material update version.
|
||||
AZStd::vector<MaterialVersionUpdate> m_materialVersionUpdates;
|
||||
};
|
||||
|
||||
class MaterialTypeAssetHandler : public AssetHandler<MaterialTypeAsset>
|
||||
|
||||
@@ -38,6 +38,11 @@ namespace AZ
|
||||
void AddShader(const AZ::Data::Asset<ShaderAsset>& shaderAsset, const ShaderVariantId& shaderVaraintId = ShaderVariantId{}, const AZ::Name& shaderTag = Uuid::CreateRandom().ToString<AZ::Name>());
|
||||
void AddShader(const AZ::Data::Asset<ShaderAsset>& shaderAsset, const AZ::Name& shaderTag);
|
||||
|
||||
//! Sets the version of the MaterialTypeAsset
|
||||
void SetVersion(uint32_t version);
|
||||
//! Adds a version update object into the MaterialTypeAsset
|
||||
void AddVersionUpdate(const MaterialVersionUpdate& materialVersionUpdate);
|
||||
|
||||
//! Indicates that this MaterialType will own the specified shader option.
|
||||
//! Material-owned shader options can be connected to material properties (either directly or through functors).
|
||||
//! They cannot be accessed externally (for example, through the Material::SetSystemShaderOption() function).
|
||||
@@ -112,6 +117,7 @@ namespace AZ
|
||||
//! Saves the per-material SRG layout in m_shaderResourceGroupLayout for easier access
|
||||
void CacheMaterialSrgLayout();
|
||||
|
||||
bool ValidateMaterialVersion();
|
||||
bool ValidateBeginMaterialProperty();
|
||||
bool ValidateEndMaterialProperty();
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/containers/map.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
class MaterialAsset;
|
||||
|
||||
// This class contains a toVersion and a list of actions to specify what operations were performed to upgrade a materialType.
|
||||
class MaterialVersionUpdate
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(AZ::RPI::MaterialVersionUpdate, "{B36E7712-AED8-46AA-AFE0-01F8F884C44A}");
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
// At this time, the only supported operation is rename. If/when we add more actions in the future,
|
||||
// we'll need to improve this, possibly with some virtual interface or union data.
|
||||
struct RenamePropertyAction
|
||||
{
|
||||
AZ_TYPE_INFO(AZ::RPI::MaterialVersionUpdate::RenameAction, "{A1FBEB19-EA05-40F0-9700-57D048DF572B}");
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
AZ::Name m_fromPropertyId;
|
||||
AZ::Name m_toPropertyId;
|
||||
};
|
||||
|
||||
explicit MaterialVersionUpdate() = default;
|
||||
explicit MaterialVersionUpdate(uint32_t toVersion);
|
||||
|
||||
uint32_t GetVersion() const;
|
||||
void SetVersion(uint32_t toVersion);
|
||||
|
||||
//! Apply version updates to the given material asset.
|
||||
//! @return true if any changes were made
|
||||
bool ApplyVersionUpdates(MaterialAsset& materialAsset) const;
|
||||
|
||||
using Actions = AZStd::vector<RenamePropertyAction>;
|
||||
const Actions& GetActions() const;
|
||||
void AddAction(const RenamePropertyAction& action);
|
||||
|
||||
private:
|
||||
uint32_t m_toVersion;
|
||||
Actions m_actions;
|
||||
};
|
||||
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
@@ -47,7 +47,7 @@ namespace AZ
|
||||
{
|
||||
AssetBuilderSDK::AssetBuilderDesc materialBuilderDescriptor;
|
||||
materialBuilderDescriptor.m_name = JobKey;
|
||||
materialBuilderDescriptor.m_version = 109; // Changed "id" to "name" in serialization
|
||||
materialBuilderDescriptor.m_version = 110; // Material version auto update feature
|
||||
materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.material", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.materialtype", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
materialBuilderDescriptor.m_busId = azrtti_typeid<MaterialBuilder>();
|
||||
@@ -287,6 +287,11 @@ namespace AZ
|
||||
return {};
|
||||
}
|
||||
|
||||
if (MaterialSourceData::ApplyVersionUpdatesResult::Failed == material.GetValue().ApplyVersionUpdates(materialSourceFilePath))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto materialAssetOutcome = material.GetValue().CreateMaterialAsset(Uuid::CreateRandom(), materialSourceFilePath, true);
|
||||
if (!materialAssetOutcome.IsSuccess())
|
||||
{
|
||||
|
||||
@@ -62,6 +62,8 @@ namespace AZ
|
||||
return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::Catastrophic, "Material type reference not found.");
|
||||
}
|
||||
|
||||
const JsonMaterialPropertyValueSerializer::LoadContext* loadContext = context.GetMetadata().Find<JsonMaterialPropertyValueSerializer::LoadContext>();
|
||||
|
||||
// Construct the full property name (groupName.propertyName) by parsing it from the JSON path string.
|
||||
size_t startPropertyName = context.GetPath().Get().rfind('/');
|
||||
size_t startGroupName = context.GetPath().Get().rfind('/', startPropertyName-1);
|
||||
@@ -70,7 +72,7 @@ namespace AZ
|
||||
|
||||
JSR::ResultCode result(JSR::Tasks::ReadField);
|
||||
|
||||
auto propertyDefinition = materialType->FindProperty(groupName, propertyName);
|
||||
auto propertyDefinition = materialType->FindProperty(groupName, propertyName, loadContext->m_materialTypeVersion);
|
||||
if (!propertyDefinition)
|
||||
{
|
||||
AZStd::string message = AZStd::string::format("Property '%.*s.%.*s' not found in material type.", AZ_STRING_ARG(groupName), AZ_STRING_ARG(propertyName));
|
||||
|
||||
@@ -72,6 +72,62 @@ namespace AZ
|
||||
materialAssetCreator.SetPropertyValue(propertyId, entry.second);
|
||||
}
|
||||
}
|
||||
|
||||
MaterialSourceData::ApplyVersionUpdatesResult MaterialSourceData::ApplyVersionUpdates(AZStd::string_view materialSourceFilePath)
|
||||
{
|
||||
AZStd::string materialTypeFullPath = AssetUtils::ResolvePathReference(materialSourceFilePath, m_materialType);
|
||||
auto materialTypeSourceDataOutcome = MaterialUtils::LoadMaterialTypeSourceData(materialTypeFullPath);
|
||||
if (!materialTypeSourceDataOutcome.IsSuccess())
|
||||
{
|
||||
return ApplyVersionUpdatesResult::Failed;
|
||||
}
|
||||
|
||||
MaterialTypeSourceData materialTypeSourceData = materialTypeSourceDataOutcome.TakeValue();
|
||||
|
||||
if (m_materialTypeVersion == materialTypeSourceData.m_version)
|
||||
{
|
||||
return ApplyVersionUpdatesResult::NoUpdates;
|
||||
}
|
||||
|
||||
bool changesWereApplied = false;
|
||||
|
||||
// Note that the only kind of property update currently supported is rename...
|
||||
|
||||
for (auto& groupPair : m_properties)
|
||||
{
|
||||
PropertyMap& propertyMap = groupPair.second;
|
||||
|
||||
PropertyMap newPropertyMap;
|
||||
|
||||
for (auto& propertyPair : propertyMap)
|
||||
{
|
||||
MaterialPropertyId propertyId{groupPair.first, propertyPair.first};
|
||||
if (materialTypeSourceData.ApplyPropertyRenames(propertyId, m_materialTypeVersion))
|
||||
{
|
||||
newPropertyMap[propertyId.GetPropertyName().GetStringView()] = propertyPair.second;
|
||||
changesWereApplied = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
newPropertyMap[propertyPair.first] = propertyPair.second;
|
||||
}
|
||||
}
|
||||
|
||||
propertyMap = newPropertyMap;
|
||||
}
|
||||
|
||||
if (changesWereApplied)
|
||||
{
|
||||
AZ_Warning("MaterialSourceData", false,
|
||||
"This material is based on version '%u' of '%s', but the material type is now at version '%u'. "
|
||||
"Automatic updates are available. Consider updating the .material source file.",
|
||||
m_materialTypeVersion, m_materialType.c_str(), materialTypeSourceData.m_version);
|
||||
}
|
||||
|
||||
m_materialTypeVersion = materialTypeSourceData.m_version;
|
||||
|
||||
return changesWereApplied ? ApplyVersionUpdatesResult::UpdatesApplied : ApplyVersionUpdatesResult::NoUpdates;
|
||||
}
|
||||
|
||||
Outcome<Data::Asset<MaterialAsset> > MaterialSourceData::CreateMaterialAsset(Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <Atom/RPI.Edit/Material/MaterialSourceDataSerializer.h>
|
||||
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
|
||||
#include <Atom/RPI.Edit/Material/MaterialPropertyValueSerializer.h>
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RPI.Edit/Common/JsonFileLoadContext.h>
|
||||
#include <Atom/RPI.Edit/Common/JsonUtils.h>
|
||||
@@ -45,9 +46,9 @@ namespace AZ
|
||||
}
|
||||
|
||||
result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_description, azrtti_typeid<AZStd::string>(), inputValue, "description", context));
|
||||
result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_materialType, azrtti_typeid<AZStd::string>(), inputValue, "materialType", context));
|
||||
result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_parentMaterial, azrtti_typeid<AZStd::string>(), inputValue, "parentMaterial", context));
|
||||
result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_propertyLayoutVersion, azrtti_typeid<uint32_t>(), inputValue, "propertyLayoutVersion", context));
|
||||
result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_materialType, azrtti_typeid<AZStd::string>(), inputValue, "materialType", context));
|
||||
result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_materialTypeVersion, azrtti_typeid<uint32_t>(), inputValue, "materialTypeVersion", context));
|
||||
|
||||
if (materialSourceData->m_materialType.empty())
|
||||
{
|
||||
@@ -118,6 +119,10 @@ namespace AZ
|
||||
|
||||
context.GetMetadata().Add(AZStd::move(materialTypeData));
|
||||
|
||||
JsonMaterialPropertyValueSerializer::LoadContext materialPropertyValueLoadContext;
|
||||
materialPropertyValueLoadContext.m_materialTypeVersion = materialSourceData->m_materialTypeVersion;
|
||||
context.GetMetadata().Add(materialPropertyValueLoadContext);
|
||||
|
||||
result.Combine(ContinueLoadingFromJsonObjectField(&materialSourceData->m_properties, azrtti_typeid<MaterialSourceData::PropertyGroupMap>(), inputValue, "properties", context));
|
||||
|
||||
if (result.GetProcessing() == JsonSerializationResult::Processing::Completed)
|
||||
@@ -146,9 +151,9 @@ namespace AZ
|
||||
|
||||
JSR::ResultCode resultCode(JSR::Tasks::ReadField);
|
||||
resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "description", &materialSourceData->m_description, nullptr, azrtti_typeid<AZStd::string>(), context));
|
||||
resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "materialType", &materialSourceData->m_materialType, nullptr, azrtti_typeid<AZStd::string>(), context));
|
||||
resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "parentMaterial", &materialSourceData->m_parentMaterial, nullptr, azrtti_typeid<AZStd::string>(), context));
|
||||
resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "propertyLayoutVersion", &materialSourceData->m_propertyLayoutVersion, nullptr, azrtti_typeid<uint32_t>(), context));
|
||||
resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "materialType", &materialSourceData->m_materialType, nullptr, azrtti_typeid<AZStd::string>(), context));
|
||||
resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "materialTypeVersion", &materialSourceData->m_materialTypeVersion, nullptr, azrtti_typeid<uint32_t>(), context));
|
||||
resultCode.Combine(ContinueStoringToJsonObjectField(outputValue, "properties", &materialSourceData->m_properties, nullptr, azrtti_typeid<MaterialSourceData::PropertyGroupMap>(), context));
|
||||
|
||||
return context.Report(resultCode, "Processed material.");
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialVersionUpdate.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
@@ -59,6 +60,23 @@ namespace AZ
|
||||
|
||||
serializeContext->RegisterGenericType<PropertyConnectionList>();
|
||||
|
||||
serializeContext->Class<VersionUpdatesRenameOperationDefinition>()
|
||||
->Version(1)
|
||||
->Field("op", &VersionUpdatesRenameOperationDefinition::m_operation)
|
||||
->Field("from", &VersionUpdatesRenameOperationDefinition::m_renameFrom)
|
||||
->Field("to", &VersionUpdatesRenameOperationDefinition::m_renameTo)
|
||||
;
|
||||
|
||||
serializeContext->RegisterGenericType<VersionUpdateActions>();
|
||||
|
||||
serializeContext->Class<VersionUpdateDefinition>()
|
||||
->Version(1)
|
||||
->Field("toVersion", &VersionUpdateDefinition::m_toVersion)
|
||||
->Field("actions", &VersionUpdateDefinition::m_actions)
|
||||
;
|
||||
|
||||
serializeContext->RegisterGenericType<VersionUpdates>();
|
||||
|
||||
serializeContext->Class<ShaderVariantReferenceData>()
|
||||
->Version(2)
|
||||
->Field("file", &ShaderVariantReferenceData::m_shaderFilePath)
|
||||
@@ -67,8 +85,8 @@ namespace AZ
|
||||
;
|
||||
|
||||
serializeContext->Class<PropertyLayout>()
|
||||
->Version(1)
|
||||
->Field("version", &PropertyLayout::m_version)
|
||||
->Version(2) // Material Version Update
|
||||
->Field("version", &PropertyLayout::m_versionOld)
|
||||
->Field("groups", &PropertyLayout::m_groups)
|
||||
->Field("properties", &PropertyLayout::m_properties)
|
||||
;
|
||||
@@ -76,8 +94,10 @@ namespace AZ
|
||||
serializeContext->RegisterGenericType<UvNameMap>();
|
||||
|
||||
serializeContext->Class<MaterialTypeSourceData>()
|
||||
->Version(3)
|
||||
->Version(4) // Material Version Update
|
||||
->Field("description", &MaterialTypeSourceData::m_description)
|
||||
->Field("version", &MaterialTypeSourceData::m_version)
|
||||
->Field("versionUpdates", &MaterialTypeSourceData::m_versionUpdates)
|
||||
->Field("propertyLayout", &MaterialTypeSourceData::m_propertyLayout)
|
||||
->Field("shaders", &MaterialTypeSourceData::m_shaderCollection)
|
||||
->Field("functors", &MaterialTypeSourceData::m_materialFunctorSourceData)
|
||||
@@ -110,7 +130,38 @@ namespace AZ
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const MaterialTypeSourceData::PropertyDefinition* MaterialTypeSourceData::FindProperty(AZStd::string_view groupName, AZStd::string_view propertyName) const
|
||||
bool MaterialTypeSourceData::ApplyPropertyRenames(MaterialPropertyId& propertyId, uint32_t materialTypeVersion) const
|
||||
{
|
||||
bool renamed = false;
|
||||
|
||||
for (const VersionUpdateDefinition& versionUpdate : m_versionUpdates)
|
||||
{
|
||||
if (materialTypeVersion >= versionUpdate.m_toVersion)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const VersionUpdatesRenameOperationDefinition& action : versionUpdate.m_actions)
|
||||
{
|
||||
if (action.m_operation == "rename")
|
||||
{
|
||||
if (action.m_renameFrom == propertyId.GetFullName().GetStringView())
|
||||
{
|
||||
propertyId = MaterialPropertyId::Parse(action.m_renameTo);
|
||||
renamed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("Material source data", false, "Unsupported material version update operation '%s'", action.m_operation.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return renamed;
|
||||
}
|
||||
|
||||
const MaterialTypeSourceData::PropertyDefinition* MaterialTypeSourceData::FindProperty(AZStd::string_view groupName, AZStd::string_view propertyName, uint32_t materialTypeVersion) const
|
||||
{
|
||||
auto groupIter = m_propertyLayout.m_properties.find(groupName);
|
||||
if (groupIter == m_propertyLayout.m_properties.end())
|
||||
@@ -126,6 +177,27 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
// Property has not been found, try looking for renames in the version history
|
||||
|
||||
MaterialPropertyId propertyId = MaterialPropertyId{groupName, propertyName};
|
||||
ApplyPropertyRenames(propertyId, materialTypeVersion);
|
||||
|
||||
// Do the search again with the new names
|
||||
|
||||
groupIter = m_propertyLayout.m_properties.find(propertyId.GetGroupName().GetStringView());
|
||||
if (groupIter == m_propertyLayout.m_properties.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const PropertyDefinition& property : groupIter->second)
|
||||
{
|
||||
if (property.m_name == propertyId.GetPropertyName().GetStringView())
|
||||
{
|
||||
return &property;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -280,6 +352,41 @@ namespace AZ
|
||||
materialTypeAssetCreator.SetElevateWarnings(elevateWarnings);
|
||||
materialTypeAssetCreator.Begin(assetId);
|
||||
|
||||
if (m_propertyLayout.m_versionOld != 0)
|
||||
{
|
||||
materialTypeAssetCreator.ReportError(
|
||||
"The field '/propertyLayout/version' is deprecated and moved to '/version'. "
|
||||
"Please edit this material type source file and move the '\"version\": %u' setting up one level.",
|
||||
m_propertyLayout.m_versionOld);
|
||||
return Failure();
|
||||
}
|
||||
|
||||
// Set materialtype version and add each version update object into MaterialTypeAsset.
|
||||
materialTypeAssetCreator.SetVersion(m_version);
|
||||
{
|
||||
const AZ::Name rename = AZ::Name{ "rename" };
|
||||
|
||||
for (const auto& versionUpdate : m_versionUpdates)
|
||||
{
|
||||
MaterialVersionUpdate materialVersionUpdate{versionUpdate.m_toVersion};
|
||||
for (const auto& action : versionUpdate.m_actions)
|
||||
{
|
||||
if (action.m_operation == rename.GetStringView())
|
||||
{
|
||||
materialVersionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction{
|
||||
AZ::Name{ action.m_renameFrom },
|
||||
AZ::Name{ action.m_renameTo }
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
materialTypeAssetCreator.ReportWarning("Unsupported material version update operation '%s'", action.m_operation.c_str());
|
||||
}
|
||||
}
|
||||
materialTypeAssetCreator.AddVersionUpdate(materialVersionUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
// Used to gather all the UV streams used in this material type from its shaders in alphabetical order.
|
||||
auto semanticComp = [](const RHI::ShaderSemantic& lhs, const RHI::ShaderSemantic& rhs) -> bool
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialVersionUpdate.h>
|
||||
#include <Atom/RPI.Reflect/Asset/AssetHandler.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
|
||||
|
||||
@@ -32,8 +33,9 @@ namespace AZ
|
||||
if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<MaterialAsset, AZ::Data::AssetData>()
|
||||
->Version(10)
|
||||
->Version(11) // Material version update
|
||||
->Field("materialTypeAsset", &MaterialAsset::m_materialTypeAsset)
|
||||
->Field("materialTypeVersion", &MaterialAsset::m_materialTypeVersion)
|
||||
->Field("propertyValues", &MaterialAsset::m_propertyValues)
|
||||
->Field("propertyNames", &MaterialAsset::m_propertyNames)
|
||||
;
|
||||
@@ -103,9 +105,25 @@ namespace AZ
|
||||
|
||||
AZStd::array_view<MaterialPropertyValue> MaterialAsset::GetPropertyValues() const
|
||||
{
|
||||
if (!m_propertyNames.empty() && m_isDirty)
|
||||
// If property names are included, they are used to re-arrange the property value list to align with the
|
||||
// MaterialPropertiesLayout. This realignment would be necessary if the material type is updated with
|
||||
// a new property layout, and a corresponding material is not reprocessed by the AP and continues using the
|
||||
// old property layout.
|
||||
if (!m_propertyNames.empty())
|
||||
{
|
||||
const_cast<MaterialAsset*>(this)->RealignPropertyValuesAndNames();
|
||||
const uint32_t materialTypeVersion = m_materialTypeAsset->GetVersion();
|
||||
if (m_materialTypeVersion < materialTypeVersion)
|
||||
{
|
||||
// It is possible that the material type has had some properties renamed. If that's the case, and this material
|
||||
// is still referencing the old property layout, we need to apply any auto updates to rename those properties
|
||||
// before using them to realign the property values.
|
||||
const_cast<MaterialAsset*>(this)->ApplyVersionUpdates();
|
||||
}
|
||||
|
||||
if (m_isDirty)
|
||||
{
|
||||
const_cast<MaterialAsset*>(this)->RealignPropertyValuesAndNames();
|
||||
}
|
||||
}
|
||||
|
||||
return m_propertyValues;
|
||||
@@ -183,6 +201,40 @@ namespace AZ
|
||||
m_isDirty = false;
|
||||
}
|
||||
|
||||
void MaterialAsset::ApplyVersionUpdates()
|
||||
{
|
||||
if (m_materialTypeVersion == m_materialTypeAsset->GetVersion())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t originalVersion = m_materialTypeVersion;
|
||||
|
||||
bool changesWereApplied = false;
|
||||
|
||||
for (const MaterialVersionUpdate& versionUpdate : m_materialTypeAsset->GetMaterialVersionUpdateList())
|
||||
{
|
||||
if (m_materialTypeVersion < versionUpdate.GetVersion())
|
||||
{
|
||||
if (versionUpdate.ApplyVersionUpdates(*this))
|
||||
{
|
||||
changesWereApplied = true;
|
||||
m_materialTypeVersion = versionUpdate.GetVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changesWereApplied)
|
||||
{
|
||||
AZ_Warning("MaterialAsset", false,
|
||||
"This material is based on version '%u' of %s, but the material type is now at version '%u'. "
|
||||
"Automatic updates are available. Consider updating the .material source file.",
|
||||
originalVersion, m_materialTypeAsset.ToString<AZStd::string>().c_str(), m_materialTypeAsset->GetVersion());
|
||||
}
|
||||
|
||||
m_materialTypeVersion = m_materialTypeAsset->GetVersion();
|
||||
}
|
||||
|
||||
void MaterialAsset::ReinitializeMaterialTypeAsset(Data::Asset<Data::AssetData> asset)
|
||||
{
|
||||
Data::Asset<MaterialTypeAsset> newMaterialTypeAsset = { asset.GetAs<MaterialTypeAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace AZ
|
||||
if (ValidateIsReady())
|
||||
{
|
||||
m_asset->m_materialTypeAsset = parentMaterial.m_materialTypeAsset;
|
||||
m_asset->m_materialTypeVersion = m_asset->m_materialTypeAsset->GetVersion();
|
||||
|
||||
if (!m_asset->m_materialTypeAsset)
|
||||
{
|
||||
@@ -69,6 +70,7 @@ namespace AZ
|
||||
ReportError("MaterialTypeAsset is null");
|
||||
return;
|
||||
}
|
||||
m_asset->m_materialTypeVersion = m_asset->m_materialTypeAsset->GetVersion();
|
||||
|
||||
m_materialPropertiesLayout = m_asset->GetMaterialPropertiesLayout();
|
||||
if (includeMaterialPropertyNames)
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace AZ
|
||||
|
||||
void MaterialTypeAsset::Reflect(ReflectContext* context)
|
||||
{
|
||||
MaterialVersionUpdate::Reflect(context);
|
||||
UvNamePair::Reflect(context);
|
||||
|
||||
if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
@@ -44,7 +45,9 @@ namespace AZ
|
||||
serializeContext->RegisterGenericType<MaterialUvNameMap>();
|
||||
|
||||
serializeContext->Class<MaterialTypeAsset, AZ::Data::AssetData>()
|
||||
->Version(4) // ATOM-15472
|
||||
->Version(5) // Material version update
|
||||
->Field("Version", &MaterialTypeAsset::m_version)
|
||||
->Field("VersionUpdates", &MaterialTypeAsset::m_materialVersionUpdates)
|
||||
->Field("ShaderCollection", &MaterialTypeAsset::m_shaderCollection)
|
||||
->Field("MaterialFunctors", &MaterialTypeAsset::m_materialFunctors)
|
||||
->Field("MaterialSrgShaderIndex", &MaterialTypeAsset::m_materialSrgShaderIndex)
|
||||
@@ -161,6 +164,11 @@ namespace AZ
|
||||
return m_uvNameMap;
|
||||
}
|
||||
|
||||
uint32_t MaterialTypeAsset::GetVersion() const
|
||||
{
|
||||
return m_version;
|
||||
}
|
||||
|
||||
void MaterialTypeAsset::SetReady()
|
||||
{
|
||||
m_status = AssetStatus::Ready;
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace AZ
|
||||
|
||||
bool MaterialTypeAssetCreator::End(Data::Asset<MaterialTypeAsset>& result)
|
||||
{
|
||||
if (!ValidateIsReady() || !ValidateEndMaterialProperty())
|
||||
if (!ValidateIsReady() || !ValidateEndMaterialProperty() || !ValidateMaterialVersion())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -100,6 +100,48 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
bool MaterialTypeAssetCreator::ValidateMaterialVersion()
|
||||
{
|
||||
if (m_asset->m_materialVersionUpdates.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t prevVersion = 0;
|
||||
for(const MaterialVersionUpdate& versionUpdate : m_asset->m_materialVersionUpdates)
|
||||
{
|
||||
if (versionUpdate.GetVersion() <= prevVersion)
|
||||
{
|
||||
ReportError("Version updates are not sequential. See version update '%u'.", versionUpdate.GetVersion());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (versionUpdate.GetVersion() > m_asset->m_version)
|
||||
{
|
||||
ReportError("Version updates go beyond the current material type version. See version update '%u'.", versionUpdate.GetVersion());
|
||||
return false;
|
||||
}
|
||||
|
||||
prevVersion = versionUpdate.GetVersion();
|
||||
}
|
||||
|
||||
const auto& lastMaterialVersionUpdate = m_asset->m_materialVersionUpdates.back();
|
||||
for (const auto& action : lastMaterialVersionUpdate.GetActions())
|
||||
{
|
||||
const auto propertyIndex = m_asset->m_materialPropertiesLayout->FindPropertyIndex(AZ::Name{ action.m_toPropertyId });
|
||||
if (!propertyIndex.IsValid())
|
||||
{
|
||||
ReportError("Renamed property '%s' not found in material property layout. Check that the property name has been "
|
||||
"upgraded to the correct version",
|
||||
action.m_toPropertyId.GetCStr());
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MaterialTypeAssetCreator::AddShader(const AZ::Data::Asset<ShaderAsset>& shaderAsset, const ShaderVariantId& shaderVaraintId, const AZ::Name& shaderTag)
|
||||
{
|
||||
if (ValidateIsReady() && ValidateNotNull(shaderAsset, "ShaderAsset"))
|
||||
@@ -123,6 +165,16 @@ namespace AZ
|
||||
AddShader(shaderAsset, ShaderVariantId{}, shaderTag);
|
||||
}
|
||||
|
||||
void MaterialTypeAssetCreator::SetVersion(uint32_t version)
|
||||
{
|
||||
m_asset->m_version = version;
|
||||
}
|
||||
|
||||
void MaterialTypeAssetCreator::AddVersionUpdate(const MaterialVersionUpdate& materialVersionUpdate)
|
||||
{
|
||||
m_asset->m_materialVersionUpdates.push_back(materialVersionUpdate);
|
||||
}
|
||||
|
||||
void MaterialTypeAssetCreator::ClaimShaderOptionOwnership(const Name& shaderOptionName)
|
||||
{
|
||||
bool optionFound = false;
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 <Atom/RPI.Reflect/Material/MaterialVersionUpdate.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
void MaterialVersionUpdate::RenamePropertyAction::Reflect(ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<RenamePropertyAction>()
|
||||
->Version(1)
|
||||
->Field("From", &RenamePropertyAction::m_fromPropertyId)
|
||||
->Field("To", &RenamePropertyAction::m_toPropertyId)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialVersionUpdate::Reflect(ReflectContext* context)
|
||||
{
|
||||
MaterialVersionUpdate::RenamePropertyAction::Reflect(context);
|
||||
|
||||
if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->RegisterGenericType<MaterialVersionUpdate::Actions>();
|
||||
|
||||
serializeContext->Class<MaterialVersionUpdate>()
|
||||
->Version(1)
|
||||
->Field("ToVersion", &MaterialVersionUpdate::m_toVersion)
|
||||
->Field("Actions", &MaterialVersionUpdate::m_actions)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
MaterialVersionUpdate::MaterialVersionUpdate(uint32_t toVersion)
|
||||
: m_toVersion(toVersion)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t MaterialVersionUpdate::GetVersion() const
|
||||
{
|
||||
return m_toVersion;
|
||||
}
|
||||
|
||||
void MaterialVersionUpdate::SetVersion(uint32_t toVersion)
|
||||
{
|
||||
m_toVersion = toVersion;
|
||||
}
|
||||
|
||||
bool MaterialVersionUpdate::ApplyVersionUpdates(MaterialAsset& materialAsset) const
|
||||
{
|
||||
bool changesWereApplied = false;
|
||||
|
||||
for (auto& propertyName : materialAsset.m_propertyNames)
|
||||
{
|
||||
for (const auto& action : m_actions)
|
||||
{
|
||||
if (propertyName == action.m_fromPropertyId)
|
||||
{
|
||||
propertyName = action.m_toPropertyId;
|
||||
changesWereApplied = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changesWereApplied;
|
||||
}
|
||||
|
||||
const AZ::RPI::MaterialVersionUpdate::Actions& MaterialVersionUpdate::GetActions() const
|
||||
{
|
||||
return m_actions;
|
||||
}
|
||||
|
||||
void MaterialVersionUpdate::AddAction(const RenamePropertyAction& action)
|
||||
{
|
||||
m_actions.push_back(action);
|
||||
}
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <Common/AssetSystemStub.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
@@ -36,12 +37,17 @@ namespace UnitTest
|
||||
// Because GetSourceInfoBySourcePath should always return 0 for the sub-id, since it's about the source file not product file.
|
||||
sourceInfo.m_assetInfo.m_assetId.m_subId = 0;
|
||||
|
||||
m_sourceInfoMap.emplace(sourcePath, sourceInfo);
|
||||
AZStd::string normalizedSourcePath = sourcePath;
|
||||
AzFramework::StringFunc::Path::Normalize(normalizedSourcePath);
|
||||
m_sourceInfoMap.emplace(normalizedSourcePath, sourceInfo);
|
||||
}
|
||||
|
||||
bool AssetSystemStub::GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder)
|
||||
{
|
||||
auto iter = m_sourceInfoMap.find(sourcePath);
|
||||
AZStd::string normalizedSourcePath = sourcePath;
|
||||
AzFramework::StringFunc::Path::Normalize(normalizedSourcePath);
|
||||
|
||||
auto iter = m_sourceInfoMap.find(normalizedSourcePath);
|
||||
|
||||
if (iter != m_sourceInfoMap.end())
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <Common/RPITestFixture.h>
|
||||
#include <Common/SerializeTester.h>
|
||||
#include <Common/ShaderAssetTestUtils.h>
|
||||
#include <Common/ErrorMessageFinder.h>
|
||||
#include <Material/MaterialAssetTestUtils.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
@@ -63,6 +64,11 @@ namespace UnitTest
|
||||
|
||||
RPITestFixture::TearDown();
|
||||
}
|
||||
|
||||
void ReplaceMaterialType(Data::Asset<MaterialAsset> materialAsset, Data::Asset<MaterialTypeAsset> upgradedMaterialTypeAsset)
|
||||
{
|
||||
materialAsset->m_materialTypeAsset = upgradedMaterialTypeAsset;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(MaterialAssetTests, Basic)
|
||||
@@ -202,6 +208,81 @@ namespace UnitTest
|
||||
EXPECT_EQ(serializedAsset->GetPropertyValues()[8].GetValue<Data::Asset<ImageAsset>>(), streamingImageAsset);
|
||||
}
|
||||
|
||||
TEST_F(MaterialAssetTests, UpgradeMaterialAsset)
|
||||
{
|
||||
// Here we test the main way that a material asset upgrade would be applied at runtime: A material type is updated to
|
||||
// both rename a property *and* change the order in which properties appear in the layout. In this case, the new name
|
||||
// must be identified and then that new name is used to find the appropriate index in the property layout.
|
||||
|
||||
auto materialSrgLayout = CreateCommonTestMaterialSrgLayout();
|
||||
|
||||
auto shaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), materialSrgLayout);
|
||||
|
||||
Data::Asset<MaterialTypeAsset> testMaterialTypeAssetV1;
|
||||
MaterialTypeAssetCreator materialTypeCreator;
|
||||
materialTypeCreator.Begin(Uuid::CreateRandom());
|
||||
materialTypeCreator.AddShader(shaderAsset);
|
||||
AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyInt" }, MaterialPropertyDataType::Int, Name{ "m_int" });
|
||||
AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyUInt" }, MaterialPropertyDataType::UInt, Name{ "m_uint" });
|
||||
AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyFloat" }, MaterialPropertyDataType::Float, Name{ "m_float" });
|
||||
EXPECT_TRUE(materialTypeCreator.End(testMaterialTypeAssetV1));
|
||||
|
||||
// Construct the material asset with materialTypeAsset version 1
|
||||
Data::AssetId assetId(Uuid::CreateRandom());
|
||||
|
||||
MaterialAssetCreator creator;
|
||||
const bool includePropertyNames = true;
|
||||
creator.Begin(assetId, *testMaterialTypeAssetV1, includePropertyNames);
|
||||
creator.SetPropertyValue(Name{ "MyInt" }, 7);
|
||||
creator.SetPropertyValue(Name{ "MyUInt" }, 8u);
|
||||
creator.SetPropertyValue(Name{ "MyFloat" }, 9.0f);
|
||||
Data::Asset<MaterialAsset> materialAsset;
|
||||
EXPECT_TRUE(creator.End(materialAsset));
|
||||
|
||||
// Prepare material type asset version 2 with the update actions
|
||||
MaterialVersionUpdate versionUpdate(2);
|
||||
versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction(
|
||||
{
|
||||
Name{ "MyInt" },
|
||||
Name{ "MyIntRenamed" }
|
||||
}));
|
||||
|
||||
Data::Asset<MaterialTypeAsset> testMaterialTypeAssetV2;
|
||||
materialTypeCreator.Begin(Uuid::CreateRandom());
|
||||
materialTypeCreator.SetVersion(versionUpdate.GetVersion());
|
||||
materialTypeCreator.AddVersionUpdate(versionUpdate);
|
||||
materialTypeCreator.AddShader(shaderAsset);
|
||||
// Now we add the properties in a different order from before, and use the new name for MyInt.
|
||||
AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyUInt" }, MaterialPropertyDataType::UInt, Name{ "m_uint" });
|
||||
AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyFloat" }, MaterialPropertyDataType::Float, Name{ "m_float" });
|
||||
AddMaterialPropertyForSrg(materialTypeCreator, Name{ "MyIntRenamed" }, MaterialPropertyDataType::Int, Name{ "m_int" });
|
||||
EXPECT_TRUE(materialTypeCreator.End(testMaterialTypeAssetV2));
|
||||
|
||||
// This is our way of faking the idea that an old version of the MaterialAsset could be loaded with a new version of the MaterialTypeAsset.
|
||||
ReplaceMaterialType(materialAsset, testMaterialTypeAssetV2);
|
||||
|
||||
// This can find errors and warnings, we are looking for a warning when the version update is applied
|
||||
ErrorMessageFinder warningFinder;
|
||||
warningFinder.AddExpectedErrorMessage("Automatic updates are available. Consider updating the .material source file");
|
||||
warningFinder.AddExpectedErrorMessage("This material is based on version '1'");
|
||||
warningFinder.AddExpectedErrorMessage("material type is now at version '2'");
|
||||
|
||||
// Even though this material was created using the old version of the material type, it's property values should get automatically
|
||||
// updated to align with the new property layout in the latest MaterialTypeAsset.
|
||||
MaterialPropertyIndex myIntIndex = materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(Name{"MyIntRenamed"});
|
||||
EXPECT_EQ(2, myIntIndex.GetIndex());
|
||||
EXPECT_EQ(7, materialAsset->GetPropertyValues()[myIntIndex.GetIndex()].GetValue<int32_t>());
|
||||
|
||||
warningFinder.CheckExpectedErrorsFound();
|
||||
|
||||
// Since the MaterialAsset has already been updated, and the warning reported once, we should not see the "consider updating"
|
||||
// warning reported again on subsequent property accesses.
|
||||
warningFinder.Reset();
|
||||
myIntIndex = materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(Name{"MyIntRenamed"});
|
||||
EXPECT_EQ(2, myIntIndex.GetIndex());
|
||||
EXPECT_EQ(7, materialAsset->GetPropertyValues()[myIntIndex.GetIndex()].GetValue<int32_t>());
|
||||
}
|
||||
|
||||
TEST_F(MaterialAssetTests, Error_NoBegin)
|
||||
{
|
||||
Data::AssetId assetId(Uuid::CreateRandom());
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <Common/RPITestFixture.h>
|
||||
#include <Common/JsonTestUtils.h>
|
||||
#include <Common/ShaderAssetTestUtils.h>
|
||||
#include <Common/ErrorMessageFinder.h>
|
||||
#include <Material/MaterialAssetTestUtils.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
@@ -17,6 +18,7 @@
|
||||
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
|
||||
#include <Atom/RPI.Edit/Material/MaterialUtils.h>
|
||||
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
@@ -60,22 +62,72 @@ namespace UnitTest
|
||||
localFileIO->SetAlias("@exefolder@", rootPath);
|
||||
|
||||
m_testMaterialSrgLayout = CreateCommonTestMaterialSrgLayout();
|
||||
|
||||
m_testShaderAsset = CreateTestShaderAsset(Uuid::CreateRandom(), m_testMaterialSrgLayout);
|
||||
m_assetSystemStub.RegisterSourceInfo("@exefolder@/Temp/test.shader", m_testShaderAsset.GetId());
|
||||
|
||||
MaterialTypeAssetCreator materialTypeCreator;
|
||||
materialTypeCreator.Begin(Uuid::CreateRandom());
|
||||
materialTypeCreator.AddShader(m_testShaderAsset);
|
||||
AddCommonTestMaterialProperties(materialTypeCreator, "general.");
|
||||
materialTypeCreator.End(m_testMaterialTypeAsset);
|
||||
// The MaterialSourceData relies on both MaterialTypeSourceData and MaterialTypeAsset. We have to make sure the
|
||||
// .materialtype file is present on disk, and that the MaterialTypeAsset is available through the asset database stub...
|
||||
|
||||
const char* materialTypeJson = R"(
|
||||
{
|
||||
"version": 10,
|
||||
"propertyLayout": {
|
||||
"properties": {
|
||||
"general": [
|
||||
{"name": "MyBool", "type": "bool"},
|
||||
{"name": "MyInt", "type": "Int"},
|
||||
{"name": "MyUInt", "type": "UInt"},
|
||||
{"name": "MyFloat", "type": "Float"},
|
||||
{"name": "MyFloat2", "type": "Vector2"},
|
||||
{"name": "MyFloat3", "type": "Vector3"},
|
||||
{"name": "MyFloat4", "type": "Vector4"},
|
||||
{"name": "MyColor", "type": "Color"},
|
||||
{"name": "MyImage", "type": "Image"},
|
||||
{"name": "MyEnum", "type": "Enum", "enumValues": ["Enum0", "Enum1", "Enum2"], "defaultValue": "Enum0"}
|
||||
]
|
||||
}
|
||||
},
|
||||
"shaders": [
|
||||
{
|
||||
"file": "@exefolder@/Temp/test.shader"
|
||||
}
|
||||
],
|
||||
"versionUpdates": [
|
||||
{
|
||||
"toVersion": 2,
|
||||
"actions": [
|
||||
{"op": "rename", "from": "general.testColorNameA", "to": "general.testColorNameB"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"toVersion": 4,
|
||||
"actions": [
|
||||
{"op": "rename", "from": "general.testColorNameB", "to": "general.testColorNameC"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"toVersion": 10,
|
||||
"actions": [
|
||||
{"op": "rename", "from": "general.testColorNameC", "to": "general.MyColor"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
)";
|
||||
|
||||
AZ::Utils::WriteFile(materialTypeJson, "@exefolder@/Temp/test.materialtype");
|
||||
|
||||
MaterialTypeSourceData materialTypeSourceData;
|
||||
LoadTestDataFromJson(materialTypeSourceData, materialTypeJson);
|
||||
m_testMaterialTypeAsset = materialTypeSourceData.CreateMaterialTypeAsset(Uuid::CreateRandom()).TakeValue();
|
||||
|
||||
// Since this test doesn't actually instantiate a Material, it won't need to instantiate this ImageAsset, so all we
|
||||
// need is an asset reference with a valid ID.
|
||||
m_testImageAsset = Data::Asset<ImageAsset>{ Data::AssetId{Uuid::CreateRandom(), StreamingImageAsset::GetImageAssetSubId()}, azrtti_typeid<StreamingImageAsset>() };
|
||||
|
||||
// Register the test assets with the AssetSystemStub so CreateMaterialAsset() can use AssetUtils.
|
||||
m_assetSystemStub.RegisterSourceInfo("test.materialtype", m_testMaterialTypeAsset.GetId());
|
||||
m_assetSystemStub.RegisterSourceInfo("test.streamingimage", m_testImageAsset.GetId());
|
||||
m_assetSystemStub.RegisterSourceInfo("@exefolder@/Temp/test.materialtype", m_testMaterialTypeAsset.GetId());
|
||||
m_assetSystemStub.RegisterSourceInfo("@exefolder@/Temp/test.streamingimage", m_testImageAsset.GetId());
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
@@ -88,12 +140,12 @@ namespace UnitTest
|
||||
RPITestFixture::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void AddPropertyGroup(MaterialSourceData& material, AZStd::string_view groupName)
|
||||
{
|
||||
material.m_properties.insert(groupName);
|
||||
}
|
||||
|
||||
|
||||
void AddProperty(MaterialSourceData& material, AZStd::string_view groupName, AZStd::string_view propertyName, const MaterialPropertyValue& anyValue)
|
||||
{
|
||||
material.m_properties[groupName][propertyName].m_value = anyValue;
|
||||
@@ -103,7 +155,7 @@ namespace UnitTest
|
||||
{
|
||||
MaterialSourceData sourceData;
|
||||
|
||||
sourceData.m_materialType = "test.materialtype";
|
||||
sourceData.m_materialType = "@exefolder@/Temp/test.materialtype";
|
||||
AddPropertyGroup(sourceData, "general");
|
||||
AddProperty(sourceData, "general", "MyBool", true);
|
||||
AddProperty(sourceData, "general", "MyInt", -10);
|
||||
@@ -113,7 +165,7 @@ namespace UnitTest
|
||||
AddProperty(sourceData, "general", "MyFloat2", AZ::Vector2(2.1f, 2.2f));
|
||||
AddProperty(sourceData, "general", "MyFloat3", AZ::Vector3(3.1f, 3.2f, 3.3f));
|
||||
AddProperty(sourceData, "general", "MyFloat4", AZ::Vector4(4.1f, 4.2f, 4.3f, 4.4f));
|
||||
AddProperty(sourceData, "general", "MyImage", AZStd::string("test.streamingimage"));
|
||||
AddProperty(sourceData, "general", "MyImage", AZStd::string("@exefolder@/Temp/test.streamingimage"));
|
||||
AddProperty(sourceData, "general", "MyEnum", AZStd::string("Enum1"));
|
||||
|
||||
auto materialAssetOutcome = sourceData.CreateMaterialAsset(Uuid::CreateRandom(), "", true);
|
||||
@@ -139,7 +191,7 @@ namespace UnitTest
|
||||
EXPECT_STREQ(a.m_materialType.data(), b.m_materialType.data());
|
||||
EXPECT_STREQ(a.m_description.data(), b.m_description.data());
|
||||
EXPECT_STREQ(a.m_parentMaterial.data(), b.m_parentMaterial.data());
|
||||
EXPECT_EQ(a.m_propertyLayoutVersion, b.m_propertyLayoutVersion);
|
||||
EXPECT_EQ(a.m_materialTypeVersion, b.m_materialTypeVersion);
|
||||
|
||||
EXPECT_EQ(a.m_properties.size(), b.m_properties.size());
|
||||
for (auto& groupA : a.m_properties)
|
||||
@@ -170,7 +222,7 @@ namespace UnitTest
|
||||
|
||||
auto& propertyA = propertyIterA.second;
|
||||
auto& propertyB = propertyIterB->second;
|
||||
|
||||
|
||||
bool typesMatch = propertyA.m_value.GetTypeId() == propertyB.m_value.GetTypeId();
|
||||
EXPECT_TRUE(typesMatch);
|
||||
if (typesMatch)
|
||||
@@ -229,8 +281,8 @@ namespace UnitTest
|
||||
" } \n"
|
||||
"} \n";
|
||||
|
||||
const char* materialTypeFilePath = "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/roundTripTest.materialtype";
|
||||
|
||||
const char* materialTypeFilePath = "@exefolder@/Temp/roundTripTest.materialtype";
|
||||
|
||||
AZ::IO::FileIOStream file;
|
||||
EXPECT_TRUE(file.Open(materialTypeFilePath, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath));
|
||||
file.Write(strlen(materialTypeJson), materialTypeJson);
|
||||
@@ -240,7 +292,7 @@ namespace UnitTest
|
||||
sourceDataOriginal.m_materialType = materialTypeFilePath;
|
||||
sourceDataOriginal.m_parentMaterial = materialTypeFilePath;
|
||||
sourceDataOriginal.m_description = "This is a description";
|
||||
sourceDataOriginal.m_propertyLayoutVersion = 7;
|
||||
sourceDataOriginal.m_materialTypeVersion = 7;
|
||||
AddPropertyGroup(sourceDataOriginal, "groupA");
|
||||
AddProperty(sourceDataOriginal, "groupA", "MyBool", true);
|
||||
AddProperty(sourceDataOriginal, "groupA", "MyInt", -10);
|
||||
@@ -252,14 +304,14 @@ namespace UnitTest
|
||||
AddPropertyGroup(sourceDataOriginal, "groupC");
|
||||
AddProperty(sourceDataOriginal, "groupC", "MyFloat4", AZ::Vector4(4.1f, 4.2f, 4.3f, 4.4f));
|
||||
AddProperty(sourceDataOriginal, "groupC", "MyColor", AZ::Color{0.1f, 0.2f, 0.3f, 0.4f});
|
||||
AddProperty(sourceDataOriginal, "groupC", "MyImage", AZStd::string("test.streamingimage"));
|
||||
AddProperty(sourceDataOriginal, "groupC", "MyImage", AZStd::string("@exefolder@/Temp/test.streamingimage"));
|
||||
|
||||
AZStd::string sourceDataSerialized;
|
||||
JsonTestResult storeResult = StoreTestDataToJson(sourceDataOriginal, sourceDataSerialized);
|
||||
|
||||
MaterialSourceData sourceDataCopy;
|
||||
JsonTestResult loadResult = LoadTestDataFromJson(sourceDataCopy, sourceDataSerialized);
|
||||
|
||||
|
||||
CheckEqual(sourceDataOriginal, sourceDataCopy);
|
||||
}
|
||||
|
||||
@@ -277,10 +329,10 @@ namespace UnitTest
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
const char* materialTypeFilePath = "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/simpleMaterialType.materialtype";
|
||||
const char* materialTypeFilePath = "@exefolder@/Temp/simpleMaterialType.materialtype";
|
||||
|
||||
AZ::IO::FileIOStream file;
|
||||
EXPECT_TRUE(file.Open(materialTypeFilePath, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath));
|
||||
@@ -296,7 +348,7 @@ namespace UnitTest
|
||||
"testColor": [0.1,0.2,0.3]
|
||||
}
|
||||
},
|
||||
"materialType": "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/simpleMaterialType.materialtype"
|
||||
"materialType": "@exefolder@/Temp/simpleMaterialType.materialtype"
|
||||
}
|
||||
)";
|
||||
|
||||
@@ -330,7 +382,7 @@ namespace UnitTest
|
||||
{
|
||||
const AZStd::string inputJson = R"(
|
||||
{
|
||||
"propertyLayoutVersion": 1,
|
||||
"materialTypeVersion": 1,
|
||||
"properties": {
|
||||
"baseColor": {
|
||||
"color": [1.0,1.0,1.0]
|
||||
@@ -354,7 +406,7 @@ namespace UnitTest
|
||||
const AZStd::string inputJson = R"(
|
||||
{
|
||||
"materialType": "DoesNotExist.materialtype",
|
||||
"propertyLayoutVersion": 1,
|
||||
"materialTypeVersion": 1,
|
||||
"properties": {
|
||||
"baseColor": {
|
||||
"color": [1.0,1.0,1.0]
|
||||
@@ -387,10 +439,10 @@ namespace UnitTest
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
const char* materialTypeFilePath = "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/simpleMaterialType.materialtype";
|
||||
const char* materialTypeFilePath = "@exefolder@/Temp/simpleMaterialType.materialtype";
|
||||
|
||||
AZ::IO::FileIOStream file;
|
||||
EXPECT_TRUE(file.Open(materialTypeFilePath, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath));
|
||||
@@ -399,8 +451,8 @@ namespace UnitTest
|
||||
|
||||
const AZStd::string inputJson = R"(
|
||||
{
|
||||
"materialType": "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/simpleMaterialType.materialtype",
|
||||
"propertyLayoutVersion": 1,
|
||||
"materialType": "@exefolder@/Temp/simpleMaterialType.materialtype",
|
||||
"materialTypeVersion": 1,
|
||||
"properties": {
|
||||
"general": {
|
||||
"testColor": [1.0,1.0,1.0]
|
||||
@@ -433,10 +485,10 @@ namespace UnitTest
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
const char* materialTypeFilePath = "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/simpleMaterialType.materialtype";
|
||||
const char* materialTypeFilePath = "@exefolder@/Temp/simpleMaterialType.materialtype";
|
||||
|
||||
AZ::IO::FileIOStream file;
|
||||
EXPECT_TRUE(file.Open(materialTypeFilePath, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath));
|
||||
@@ -445,8 +497,8 @@ namespace UnitTest
|
||||
|
||||
const AZStd::string inputJson = R"(
|
||||
{
|
||||
"materialType": "@exefolder@/Gems/Atom/RPI/Code/Tests/Material/Temp/simpleMaterialType.materialtype",
|
||||
"propertyLayoutVersion": 1,
|
||||
"materialType": "@exefolder@/Temp/simpleMaterialType.materialtype",
|
||||
"materialTypeVersion": 1,
|
||||
"properties": {
|
||||
"general": {
|
||||
"doesNotExist": [1.0,1.0,1.0]
|
||||
@@ -467,20 +519,20 @@ namespace UnitTest
|
||||
TEST_F(MaterialSourceDataTests, CreateMaterialAsset_MultiLevelDataInheritance)
|
||||
{
|
||||
MaterialSourceData sourceDataLevel1;
|
||||
sourceDataLevel1.m_materialType = "test.materialtype";
|
||||
sourceDataLevel1.m_materialType = "@exefolder@/Temp/test.materialtype";
|
||||
AddPropertyGroup(sourceDataLevel1, "general");
|
||||
AddProperty(sourceDataLevel1, "general", "MyFloat", 1.5f);
|
||||
AddProperty(sourceDataLevel1, "general", "MyColor", AZ::Color{0.1f, 0.2f, 0.3f, 0.4f});
|
||||
|
||||
MaterialSourceData sourceDataLevel2;
|
||||
sourceDataLevel2.m_materialType = "test.materialtype";
|
||||
sourceDataLevel2.m_materialType = "@exefolder@/Temp/test.materialtype";
|
||||
sourceDataLevel2.m_parentMaterial = "level1.material";
|
||||
AddPropertyGroup(sourceDataLevel2, "general");
|
||||
AddProperty(sourceDataLevel2, "general", "MyColor", AZ::Color{0.15f, 0.25f, 0.35f, 0.45f});
|
||||
AddProperty(sourceDataLevel2, "general", "MyFloat2", AZ::Vector2{4.1f, 4.2f});
|
||||
|
||||
MaterialSourceData sourceDataLevel3;
|
||||
sourceDataLevel3.m_materialType = "test.materialtype";
|
||||
sourceDataLevel3.m_materialType = "@exefolder@/Temp/test.materialtype";
|
||||
sourceDataLevel3.m_parentMaterial = "level2.material";
|
||||
AddPropertyGroup(sourceDataLevel3, "general");
|
||||
AddProperty(sourceDataLevel3, "general", "MyFloat", 3.5f);
|
||||
@@ -497,7 +549,7 @@ namespace UnitTest
|
||||
|
||||
auto materialAssetLevel3 = sourceDataLevel3.CreateMaterialAsset(Uuid::CreateRandom(), "", true);
|
||||
EXPECT_TRUE(materialAssetLevel3.IsSuccess());
|
||||
|
||||
|
||||
auto layout = m_testMaterialTypeAsset->GetMaterialPropertiesLayout();
|
||||
MaterialPropertyIndex myFloat = layout->FindPropertyIndex(Name("general.MyFloat"));
|
||||
MaterialPropertyIndex myFloat2 = layout->FindPropertyIndex(Name("general.MyFloat2"));
|
||||
@@ -535,14 +587,14 @@ namespace UnitTest
|
||||
m_assetSystemStub.RegisterSourceInfo("otherBase.materialtype", otherMaterialType.GetId());
|
||||
|
||||
MaterialSourceData sourceDataLevel1;
|
||||
sourceDataLevel1.m_materialType = "test.materialtype";
|
||||
sourceDataLevel1.m_materialType = "@exefolder@/Temp/test.materialtype";
|
||||
|
||||
MaterialSourceData sourceDataLevel2;
|
||||
sourceDataLevel2.m_materialType = "test.materialtype";
|
||||
sourceDataLevel2.m_materialType = "@exefolder@/Temp/test.materialtype";
|
||||
sourceDataLevel2.m_parentMaterial = "level1.material";
|
||||
|
||||
MaterialSourceData sourceDataLevel3;
|
||||
sourceDataLevel3.m_materialType = "otherBase.materialtype";
|
||||
sourceDataLevel3.m_materialType = "@exefolder@/Temp/otherBase.materialtype";
|
||||
sourceDataLevel3.m_parentMaterial = "level2.material";
|
||||
|
||||
auto materialAssetLevel1 = sourceDataLevel1.CreateMaterialAsset(Uuid::CreateRandom(), "", true);
|
||||
@@ -570,7 +622,7 @@ namespace UnitTest
|
||||
{
|
||||
MaterialSourceData sourceData;
|
||||
|
||||
sourceData.m_materialType = "test.materialtype";
|
||||
sourceData.m_materialType = "@exefolder@/Temp/test.materialtype";
|
||||
|
||||
AddPropertyGroup(sourceData, "general");
|
||||
|
||||
@@ -587,7 +639,7 @@ namespace UnitTest
|
||||
{
|
||||
MaterialSourceData sourceData;
|
||||
|
||||
sourceData.m_materialType = "test.materialtype";
|
||||
sourceData.m_materialType = "@exefolder@/Temp/test.materialtype";
|
||||
|
||||
AddPropertyGroup(sourceData, "general");
|
||||
|
||||
@@ -629,7 +681,7 @@ namespace UnitTest
|
||||
|
||||
expectWarning([](MaterialSourceData& materialSourceData)
|
||||
{
|
||||
AddProperty(materialSourceData, "general", "DoesNotExist", AZStd::string("test.streamingimage"));
|
||||
AddProperty(materialSourceData, "general", "DoesNotExist", AZStd::string("@exefolder@/Temp/test.streamingimage"));
|
||||
});
|
||||
|
||||
// Missing image reference
|
||||
@@ -638,6 +690,124 @@ namespace UnitTest
|
||||
AddProperty(materialSourceData, "general", "MyImage", AZStd::string("doesNotExist.streamingimage"));
|
||||
}, 3); // Expect a 3rd error because AssetUtils reports its own assertion failure
|
||||
}
|
||||
|
||||
|
||||
TEST_F(MaterialSourceDataTests, Load_MaterialTypeVersionUpdate)
|
||||
{
|
||||
const AZStd::string inputJson = R"(
|
||||
{
|
||||
"materialType": "@exefolder@/Temp/test.materialtype",
|
||||
"materialTypeVersion": 1,
|
||||
"properties": {
|
||||
"general": {
|
||||
"testColorNameA": [0.1, 0.2, 0.3]
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
MaterialSourceData material;
|
||||
JsonTestResult loadResult = LoadTestDataFromJson(material, inputJson);
|
||||
|
||||
EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
|
||||
EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
|
||||
|
||||
// Initially, the loaded material data will match the .material file exactly. This gives us the accurate representation of
|
||||
// what's actually saved on disk.
|
||||
|
||||
EXPECT_NE(material.m_properties["general"].find("testColorNameA"), material.m_properties["general"].end());
|
||||
EXPECT_EQ(material.m_properties["general"].find("testColorNameB"), material.m_properties["general"].end());
|
||||
EXPECT_EQ(material.m_properties["general"].find("testColorNameC"), material.m_properties["general"].end());
|
||||
EXPECT_EQ(material.m_properties["general"].find("MyColor"), material.m_properties["general"].end());
|
||||
|
||||
AZ::Color testColor = material.m_properties["general"]["testColorNameA"].m_value.GetValue<AZ::Color>();
|
||||
EXPECT_TRUE(AZ::Color(0.1f, 0.2f, 0.3f, 1.0f).IsClose(testColor, 0.01));
|
||||
|
||||
EXPECT_EQ(1, material.m_materialTypeVersion);
|
||||
|
||||
// Then we force the material data to update to the latest material type version specification
|
||||
ErrorMessageFinder warningFinder; // Note this finds errors and warnings, and we're looking for a warning.
|
||||
warningFinder.AddExpectedErrorMessage("Automatic updates are available. Consider updating the .material source file");
|
||||
warningFinder.AddExpectedErrorMessage("This material is based on version '1'");
|
||||
warningFinder.AddExpectedErrorMessage("material type is now at version '10'");
|
||||
material.ApplyVersionUpdates();
|
||||
warningFinder.CheckExpectedErrorsFound();
|
||||
|
||||
// Now the material data should match the latest material type.
|
||||
// Look for the property under the latest name in the material type, not the name used in the .material file.
|
||||
|
||||
EXPECT_EQ(material.m_properties["general"].find("testColorNameA"), material.m_properties["general"].end());
|
||||
EXPECT_EQ(material.m_properties["general"].find("testColorNameB"), material.m_properties["general"].end());
|
||||
EXPECT_EQ(material.m_properties["general"].find("testColorNameC"), material.m_properties["general"].end());
|
||||
EXPECT_NE(material.m_properties["general"].find("MyColor"), material.m_properties["general"].end());
|
||||
|
||||
testColor = material.m_properties["general"]["MyColor"].m_value.GetValue<AZ::Color>();
|
||||
EXPECT_TRUE(AZ::Color(0.1f, 0.2f, 0.3f, 1.0f).IsClose(testColor, 0.01));
|
||||
|
||||
EXPECT_EQ(10, material.m_materialTypeVersion);
|
||||
|
||||
// Calling ApplyVersionUpdates() again should not report the warning again, since the material has already been updated.
|
||||
warningFinder.Reset();
|
||||
material.ApplyVersionUpdates();
|
||||
}
|
||||
|
||||
TEST_F(MaterialSourceDataTests, Load_MaterialTypeVersionPartialUpdate)
|
||||
{
|
||||
// This case is similar to Load_MaterialTypeVersionUpdate but we start at a later
|
||||
// version so only some of the version updates are applied.
|
||||
|
||||
const AZStd::string inputJson = R"(
|
||||
{
|
||||
"materialType": "@exefolder@/Temp/test.materialtype",
|
||||
"materialTypeVersion": 3,
|
||||
"properties": {
|
||||
"general": {
|
||||
"testColorNameB": [0.1, 0.2, 0.3]
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
MaterialSourceData material;
|
||||
JsonTestResult loadResult = LoadTestDataFromJson(material, inputJson);
|
||||
|
||||
EXPECT_EQ(AZ::JsonSerializationResult::Tasks::ReadField, loadResult.m_jsonResultCode.GetTask());
|
||||
EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, loadResult.m_jsonResultCode.GetProcessing());
|
||||
|
||||
material.ApplyVersionUpdates();
|
||||
|
||||
AZ::Color testColor = material.m_properties["general"]["MyColor"].m_value.GetValue<AZ::Color>();
|
||||
EXPECT_TRUE(AZ::Color(0.1f, 0.2f, 0.3f, 1.0f).IsClose(testColor, 0.01));
|
||||
|
||||
EXPECT_EQ(10, material.m_materialTypeVersion);
|
||||
}
|
||||
|
||||
TEST_F(MaterialSourceDataTests, Load_Error_MaterialTypeVersionUpdateWithMismatchedVersion)
|
||||
{
|
||||
const AZStd::string inputJson = R"(
|
||||
{
|
||||
"materialType": "@exefolder@/Temp/test.materialtype",
|
||||
"materialTypeVersion": 3, // At this version, the property should be testColorNameB not testColorNameA
|
||||
"properties": {
|
||||
"general": {
|
||||
"testColorNameA": [0.1, 0.2, 0.3]
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
MaterialSourceData material;
|
||||
JsonTestResult loadResult = LoadTestDataFromJson(material, inputJson);
|
||||
|
||||
loadResult.ContainsMessage("/properties/general/testColorNameA", "Property 'general.testColorNameA' not found in material type.");
|
||||
|
||||
EXPECT_FALSE(material.m_properties["general"]["testColorNameA"].m_value.IsValid());
|
||||
|
||||
material.ApplyVersionUpdates();
|
||||
|
||||
EXPECT_FALSE(material.m_properties["general"]["MyColor"].m_value.IsValid());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <Material/MaterialAssetTestUtils.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialVersionUpdate.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
||||
@@ -153,6 +154,16 @@ namespace UnitTest
|
||||
MaterialTypeAssetCreator materialTypeCreator;
|
||||
materialTypeCreator.Begin(assetId);
|
||||
|
||||
// Version updates
|
||||
MaterialVersionUpdate versionUpdate(2);
|
||||
versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction(
|
||||
{
|
||||
Name{ "EnableSpecialPassPrev" },
|
||||
Name{ "EnableSpecialPass" }
|
||||
}));
|
||||
materialTypeCreator.SetVersion(versionUpdate.GetVersion());
|
||||
materialTypeCreator.AddVersionUpdate(versionUpdate);
|
||||
|
||||
// Built-in shader
|
||||
|
||||
materialTypeCreator.AddShader(m_testShaderAsset);
|
||||
@@ -198,7 +209,7 @@ namespace UnitTest
|
||||
{
|
||||
EXPECT_EQ(m_testMaterialSrgLayout, materialTypeAsset->GetMaterialSrgLayout());
|
||||
EXPECT_EQ(5, materialTypeAsset->GetMaterialPropertiesLayout()->GetPropertyCount());
|
||||
|
||||
EXPECT_EQ(2, materialTypeAsset->GetVersion());
|
||||
// Check aliased properties
|
||||
|
||||
const MaterialPropertyIndex colorIndex = materialTypeAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(Name{ "MyColor" });
|
||||
@@ -490,6 +501,106 @@ namespace UnitTest
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(MaterialTypeAssetTests, Error_InvalidMaterialVersionUpdate_WrongName)
|
||||
{
|
||||
Data::Asset<MaterialTypeAsset> materialTypeAsset;
|
||||
|
||||
Data::AssetId assetId(Uuid::CreateRandom());
|
||||
|
||||
MaterialTypeAssetCreator materialTypeCreator;
|
||||
materialTypeCreator.Begin(assetId);
|
||||
|
||||
// Invalid version updates
|
||||
MaterialVersionUpdate versionUpdate(2);
|
||||
versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction(
|
||||
{
|
||||
Name{ "EnableSpecialPassPrev" },
|
||||
Name{ "InvalidPropertyName" }
|
||||
}));
|
||||
materialTypeCreator.SetVersion(versionUpdate.GetVersion());
|
||||
materialTypeCreator.AddVersionUpdate(versionUpdate);
|
||||
materialTypeCreator.AddShader(m_testShaderAsset);
|
||||
|
||||
materialTypeCreator.BeginMaterialProperty(Name{ "EnableSpecialPass" }, MaterialPropertyDataType::Bool);
|
||||
materialTypeCreator.EndMaterialProperty();
|
||||
|
||||
AZ_TEST_START_ASSERTTEST;
|
||||
EXPECT_FALSE(materialTypeCreator.End(materialTypeAsset));
|
||||
AZ_TEST_STOP_ASSERTTEST(1);
|
||||
EXPECT_EQ(1, materialTypeCreator.GetErrorCount());
|
||||
}
|
||||
|
||||
TEST_F(MaterialTypeAssetTests, Error_InvalidMaterialVersionUpdate_WrongOrder)
|
||||
{
|
||||
MaterialTypeAssetCreator materialTypeCreator;
|
||||
materialTypeCreator.Begin(Uuid::CreateRandom());
|
||||
|
||||
materialTypeCreator.SetVersion(4);
|
||||
materialTypeCreator.AddShader(m_testShaderAsset);
|
||||
materialTypeCreator.BeginMaterialProperty(Name{ "d" }, MaterialPropertyDataType::Bool);
|
||||
materialTypeCreator.EndMaterialProperty();
|
||||
|
||||
ErrorMessageFinder errorMessageFinder;
|
||||
errorMessageFinder.AddExpectedErrorMessage("Version updates are not sequential. See version update '3'");
|
||||
|
||||
{
|
||||
MaterialVersionUpdate versionUpdate(2);
|
||||
versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction({Name{ "a" },Name{ "b" }}));
|
||||
materialTypeCreator.AddVersionUpdate(versionUpdate);
|
||||
}
|
||||
|
||||
{
|
||||
MaterialVersionUpdate versionUpdate(4);
|
||||
versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction({Name{ "b" },Name{ "c" }}));
|
||||
materialTypeCreator.AddVersionUpdate(versionUpdate);
|
||||
}
|
||||
|
||||
{
|
||||
MaterialVersionUpdate versionUpdate(3);
|
||||
versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction({Name{ "c" },Name{ "d" }}));
|
||||
materialTypeCreator.AddVersionUpdate(versionUpdate);
|
||||
}
|
||||
|
||||
Data::Asset<MaterialTypeAsset> materialTypeAsset;
|
||||
EXPECT_FALSE(materialTypeCreator.End(materialTypeAsset));
|
||||
|
||||
errorMessageFinder.CheckExpectedErrorsFound();
|
||||
|
||||
EXPECT_EQ(1, materialTypeCreator.GetErrorCount());
|
||||
}
|
||||
|
||||
TEST_F(MaterialTypeAssetTests, Error_InvalidMaterialVersionUpdate_GoesTooFar)
|
||||
{
|
||||
MaterialTypeAssetCreator materialTypeCreator;
|
||||
materialTypeCreator.Begin(Uuid::CreateRandom());
|
||||
|
||||
materialTypeCreator.SetVersion(3);
|
||||
materialTypeCreator.AddShader(m_testShaderAsset);
|
||||
materialTypeCreator.BeginMaterialProperty(Name{ "d" }, MaterialPropertyDataType::Bool);
|
||||
materialTypeCreator.EndMaterialProperty();
|
||||
|
||||
ErrorMessageFinder errorMessageFinder;
|
||||
errorMessageFinder.AddExpectedErrorMessage("Version updates go beyond the current material type version. See version update '4'");
|
||||
|
||||
{
|
||||
MaterialVersionUpdate versionUpdate(2);
|
||||
versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction({Name{ "a" },Name{ "b" }}));
|
||||
materialTypeCreator.AddVersionUpdate(versionUpdate);
|
||||
}
|
||||
|
||||
{
|
||||
MaterialVersionUpdate versionUpdate(4);
|
||||
versionUpdate.AddAction(MaterialVersionUpdate::RenamePropertyAction({Name{ "b" },Name{ "c" }}));
|
||||
materialTypeCreator.AddVersionUpdate(versionUpdate);
|
||||
}
|
||||
|
||||
Data::Asset<MaterialTypeAsset> materialTypeAsset;
|
||||
EXPECT_FALSE(materialTypeCreator.End(materialTypeAsset));
|
||||
|
||||
errorMessageFinder.CheckExpectedErrorsFound();
|
||||
|
||||
EXPECT_EQ(1, materialTypeCreator.GetErrorCount());
|
||||
}
|
||||
|
||||
TEST_F(MaterialTypeAssetTests, MaterialTypeWithNoSRGOrProperties)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <Common/RPITestFixture.h>
|
||||
#include <Common/JsonTestUtils.h>
|
||||
#include <Common/ShaderAssetTestUtils.h>
|
||||
#include <Common/ErrorMessageFinder.h>
|
||||
#include <Material/MaterialAssetTestUtils.h>
|
||||
|
||||
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
|
||||
@@ -978,8 +979,16 @@ namespace UnitTest
|
||||
const AZStd::string inputJson = R"(
|
||||
{
|
||||
"description": "This is a general description about the material",
|
||||
"version": 2,
|
||||
"versionUpdates": [
|
||||
{
|
||||
"toVersion": 2,
|
||||
"actions": [
|
||||
{ "op": "rename", "from": "groupA.fooPrev", "to": "groupA.foo" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"propertyLayout": {
|
||||
"version": 2,
|
||||
"groups": [
|
||||
{
|
||||
"name": "groupA",
|
||||
@@ -1062,7 +1071,12 @@ namespace UnitTest
|
||||
|
||||
EXPECT_EQ(material.m_description, "This is a general description about the material");
|
||||
|
||||
EXPECT_EQ(material.m_propertyLayout.m_version, 2);
|
||||
EXPECT_EQ(material.m_version, 2);
|
||||
EXPECT_EQ(material.m_versionUpdates.size(), 1);
|
||||
EXPECT_EQ(material.m_versionUpdates[0].m_toVersion, 2);
|
||||
EXPECT_EQ(material.m_versionUpdates[0].m_actions[0].m_operation, "rename");
|
||||
EXPECT_EQ(material.m_versionUpdates[0].m_actions[0].m_renameFrom, "groupA.fooPrev");
|
||||
EXPECT_EQ(material.m_versionUpdates[0].m_actions[0].m_renameTo, "groupA.foo");
|
||||
|
||||
EXPECT_EQ(material.m_propertyLayout.m_groups.size(), 2);
|
||||
EXPECT_TRUE(material.FindGroup("groupA") != nullptr);
|
||||
@@ -1208,8 +1222,6 @@ namespace UnitTest
|
||||
|
||||
EXPECT_EQ(material.m_description, "This is a general description about the material");
|
||||
|
||||
EXPECT_EQ(material.m_propertyLayout.m_version, 2);
|
||||
|
||||
EXPECT_EQ(material.m_propertyLayout.m_groups.size(), 2);
|
||||
EXPECT_TRUE(material.FindGroup("groupA") != nullptr);
|
||||
EXPECT_TRUE(material.FindGroup("groupB") != nullptr);
|
||||
@@ -1266,7 +1278,6 @@ namespace UnitTest
|
||||
{
|
||||
"description": "",
|
||||
"propertyLayout": {
|
||||
"version": 2,
|
||||
"groups": [
|
||||
{
|
||||
"name": "general",
|
||||
@@ -1305,4 +1316,191 @@ namespace UnitTest
|
||||
CheckPropertyValue<Data::Asset<ImageAsset>>(materialTypeAsset, Name{ "general.absolute" }, m_testImageAsset2);
|
||||
CheckPropertyValue<Data::Asset<ImageAsset>>(materialTypeAsset, Name{ "general.relative" }, m_testImageAsset2);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(MaterialTypeSourceDataTests, FindPropertyUsingOldName)
|
||||
{
|
||||
const AZStd::string inputJson = R"(
|
||||
{
|
||||
"version": 10,
|
||||
"versionUpdates": [
|
||||
{
|
||||
"toVersion": 2,
|
||||
"actions": [
|
||||
{ "op": "rename", "from": "general.fooA", "to": "general.fooB" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"toVersion": 4,
|
||||
"actions": [
|
||||
{ "op": "rename", "from": "general.barA", "to": "general.barB" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"toVersion": 6,
|
||||
"actions": [
|
||||
{ "op": "rename", "from": "general.fooB", "to": "general.fooC" },
|
||||
{ "op": "rename", "from": "general.barB", "to": "general.barC" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"toVersion": 7,
|
||||
"actions": [
|
||||
{ "op": "rename", "from": "general.bazA", "to": "otherGroup.bazB" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"propertyLayout": {
|
||||
"properties": {
|
||||
"general": [
|
||||
{
|
||||
"name": "fooC",
|
||||
"type": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "barC",
|
||||
"type": "Float"
|
||||
}
|
||||
],
|
||||
"otherGroup": [
|
||||
{
|
||||
"name": "dontMindMe",
|
||||
"type": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "bazB",
|
||||
"type": "Float"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
MaterialTypeSourceData materialType;
|
||||
JsonTestResult loadResult = LoadTestDataFromJson(materialType, inputJson);
|
||||
|
||||
EXPECT_EQ(materialType.m_version, 10);
|
||||
|
||||
// First find the properties using their correct current names
|
||||
const MaterialTypeSourceData::PropertyDefinition* foo = materialType.FindProperty("general", "fooC");
|
||||
const MaterialTypeSourceData::PropertyDefinition* bar = materialType.FindProperty("general", "barC");
|
||||
const MaterialTypeSourceData::PropertyDefinition* baz = materialType.FindProperty("otherGroup", "bazB");
|
||||
|
||||
EXPECT_TRUE(foo);
|
||||
EXPECT_TRUE(bar);
|
||||
EXPECT_TRUE(baz);
|
||||
EXPECT_EQ(foo->m_name, "fooC");
|
||||
EXPECT_EQ(bar->m_name, "barC");
|
||||
EXPECT_EQ(baz->m_name, "bazB");
|
||||
|
||||
// Now try doing the property lookup using old versions of the name and make sure the same property can be found
|
||||
|
||||
EXPECT_EQ(foo, materialType.FindProperty("general", "fooA"));
|
||||
EXPECT_EQ(foo, materialType.FindProperty("general", "fooB"));
|
||||
EXPECT_EQ(bar, materialType.FindProperty("general", "barA"));
|
||||
EXPECT_EQ(bar, materialType.FindProperty("general", "barB"));
|
||||
EXPECT_EQ(baz, materialType.FindProperty("general", "bazA"));
|
||||
|
||||
EXPECT_EQ(nullptr, materialType.FindProperty("general", "fooX"));
|
||||
EXPECT_EQ(nullptr, materialType.FindProperty("general", "barX"));
|
||||
EXPECT_EQ(nullptr, materialType.FindProperty("general", "bazX"));
|
||||
EXPECT_EQ(nullptr, materialType.FindProperty("general", "bazB"));
|
||||
EXPECT_EQ(nullptr, materialType.FindProperty("otherGroup", "bazA"));
|
||||
}
|
||||
|
||||
TEST_F(MaterialTypeSourceDataTests, FindPropertyUsingOldName_Error_UnsupportedVersionUpdate)
|
||||
{
|
||||
const AZStd::string inputJson = R"(
|
||||
{
|
||||
"version": 10,
|
||||
"versionUpdates": [
|
||||
{
|
||||
"toVersion": 2,
|
||||
"actions": [
|
||||
{ "op": "notRename", "from": "general.fooA", "to": "general.fooB" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"propertyLayout": {
|
||||
"properties": {
|
||||
"general": [
|
||||
{
|
||||
"name": "fooB",
|
||||
"type": "Bool"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
MaterialTypeSourceData materialType;
|
||||
JsonTestResult loadResult = LoadTestDataFromJson(materialType, inputJson);
|
||||
|
||||
ErrorMessageFinder errorMessageFinder;
|
||||
errorMessageFinder.AddExpectedErrorMessage("Unsupported material version update operation 'notRename'");
|
||||
|
||||
|
||||
const MaterialTypeSourceData::PropertyDefinition* foo = materialType.FindProperty("general", "fooA");
|
||||
|
||||
EXPECT_EQ(nullptr, foo);
|
||||
|
||||
errorMessageFinder.CheckExpectedErrorsFound();
|
||||
}
|
||||
|
||||
TEST_F(MaterialTypeSourceDataTests, CreateMaterialTypeAsset_Error_UnsupportedVersionUpdate)
|
||||
{
|
||||
MaterialTypeSourceData sourceData;
|
||||
|
||||
MaterialTypeSourceData::PropertyDefinition propertySource;
|
||||
propertySource.m_name = "a";
|
||||
propertySource.m_dataType = MaterialPropertyDataType::Int;
|
||||
propertySource.m_value = 0;
|
||||
sourceData.m_propertyLayout.m_properties["general"].push_back(propertySource);
|
||||
|
||||
sourceData.m_version = 2;
|
||||
|
||||
MaterialTypeSourceData::VersionUpdateDefinition versionUpdate;
|
||||
versionUpdate.m_toVersion = 2;
|
||||
MaterialTypeSourceData::VersionUpdatesRenameOperationDefinition updateAction;
|
||||
updateAction.m_operation = "operationNotKnown";
|
||||
versionUpdate.m_actions.push_back(updateAction);
|
||||
sourceData.m_versionUpdates.push_back(versionUpdate);
|
||||
|
||||
ErrorMessageFinder errorMessageFinder;
|
||||
errorMessageFinder.AddExpectedErrorMessage("Unsupported material version update operation 'operationNotKnown'");
|
||||
errorMessageFinder.AddIgnoredErrorMessage("Failed to build MaterialTypeAsset", true);
|
||||
|
||||
auto materialTypeOutcome = sourceData.CreateMaterialTypeAsset(Uuid::CreateRandom());
|
||||
EXPECT_FALSE(materialTypeOutcome.IsSuccess());
|
||||
|
||||
errorMessageFinder.CheckExpectedErrorsFound();
|
||||
}
|
||||
|
||||
TEST_F(MaterialTypeSourceDataTests, CreateMaterialTypeAsset_Error_VersionInWrongLocation)
|
||||
{
|
||||
// The version field used to be under the propertyLayout section, but it has been moved up to the top level.
|
||||
// If any users have their own custom .materialtype with an older format that has the version in the wrong place
|
||||
// then we will report an error with instructions to move it to the correct location.
|
||||
|
||||
ErrorMessageFinder errorMessageFinder;
|
||||
errorMessageFinder.AddExpectedErrorMessage("The field '/propertyLayout/version' is deprecated and moved to '/version'. Please edit this material type source file and move the '\"version\": 4' setting up one level");
|
||||
|
||||
const AZStd::string inputJson = R"(
|
||||
{
|
||||
"propertyLayout": {
|
||||
"version": 4
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
MaterialTypeSourceData materialType;
|
||||
JsonTestResult loadResult = LoadTestDataFromJson(materialType, inputJson);
|
||||
|
||||
auto materialTypeOutcome = materialType.CreateMaterialTypeAsset(Uuid::CreateRandom());
|
||||
EXPECT_FALSE(materialTypeOutcome.IsSuccess());
|
||||
|
||||
errorMessageFinder.CheckExpectedErrorsFound();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ set(FILES
|
||||
Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h
|
||||
Include/Atom/RPI.Reflect/Material/ShaderCollection.h
|
||||
Include/Atom/RPI.Reflect/Material/MaterialFunctor.h
|
||||
Include/Atom/RPI.Reflect/Material/MaterialVersionUpdate.h
|
||||
Include/Atom/RPI.Reflect/Pass/ComputePassData.h
|
||||
Include/Atom/RPI.Reflect/Pass/CopyPassData.h
|
||||
Include/Atom/RPI.Reflect/Pass/DownsampleMipChainPassData.h
|
||||
@@ -141,6 +142,7 @@ set(FILES
|
||||
Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp
|
||||
Source/RPI.Reflect/Material/ShaderCollection.cpp
|
||||
Source/RPI.Reflect/Material/MaterialFunctor.cpp
|
||||
Source/RPI.Reflect/Material/MaterialVersionUpdate.cpp
|
||||
Source/RPI.Reflect/Pass/PassAsset.cpp
|
||||
Source/RPI.Reflect/Pass/PassAttachmentReflect.cpp
|
||||
Source/RPI.Reflect/Pass/PassRequest.cpp
|
||||
|
||||
Reference in New Issue
Block a user