ATOM-15223 updating material assignment ID to be portable to other models

The bug was reported that copy and paste did not work with the material component.
Copy and paste to take the worked fine.
All of the material assignments/overrides get mapped using the LOD and asset ID of materials provided with the model.
The asset IDs of materials exported by atom builders, using the scene API, are the combination of the same UUID as the model asset ID and the unique sub ID that is now hashed from the material name provided by the DCC tool.
If we map material assignments using the entire asset ID that was generated in the model builder then the mapping will only work with that specific model.
This change updates the material assignment ID equality operators and hash function to only use the sub ID portion of the asset ID.
As long as the sub IDs are generated consistently the material assignment mappings will be portable to models with the same material names.

Also moved material assignment structures to atom common features static library so this was to be moved to cpp files
This commit is contained in:
guthadam
2021-05-12 13:47:47 -05:00
parent 33f7da764e
commit 931a127b7b
7 changed files with 232 additions and 168 deletions
@@ -1,21 +1,21 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Atom/RPI.Public/Material/Material.h>
#include <AzCore/std/containers/unordered_map.h>
#include <Atom/RPI.Public/Model/Model.h>
#include <Atom/Feature/Material/MaterialAssignmentId.h>
#include <Atom/RPI.Public/Material/Material.h>
#include <Atom/RPI.Public/Model/Model.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/std/containers/unordered_map.h>
namespace AZ
{
@@ -31,39 +31,19 @@ namespace AZ
MaterialAssignment() = default;
MaterialAssignment(const AZ::Data::AssetId& materialAssetId)
: m_materialInstance()
{
m_materialAsset.Create(materialAssetId);
}
MaterialAssignment(const AZ::Data::AssetId& materialAssetId);
MaterialAssignment(const Data::Asset<RPI::MaterialAsset>& asset)
: m_materialAsset(asset)
, m_materialInstance()
{
}
MaterialAssignment(const Data::Asset<RPI::MaterialAsset>& asset);
MaterialAssignment(const Data::Asset<RPI::MaterialAsset>& asset, const Data::Instance<RPI::Material>& instance)
: m_materialAsset(asset)
, m_materialInstance(instance)
{
}
MaterialAssignment(const Data::Asset<RPI::MaterialAsset>& asset, const Data::Instance<RPI::Material>& instance);
void RebuildInstance()
{
if (m_materialAsset.IsReady())
{
m_materialInstance = m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_materialAsset) : RPI::Material::Create(m_materialAsset);
AZ_Error("MaterialAssignment", m_materialInstance, "Material instance not initialized");
}
}
//! Recreates the material instance from the asset if it has been loaded.
//! If amy property overrides have been specified then a unique instance will be created.
//! Otherwise an attempt will be made to find or create a shared instance.
void RebuildInstance();
AZStd::string ToString() const
{
AZStd::string assetPathString;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetPathString, &AZ::Data::AssetCatalogRequests::GetAssetPathById, m_materialAsset.GetId());
return assetPathString;
}
//! Returns a string composed of the asset path.
AZStd::string ToString() const;
Data::Asset<RPI::MaterialAsset> m_materialAsset;
Data::Instance<RPI::Material> m_materialInstance;
@@ -77,64 +57,15 @@ namespace AZ
static const MaterialAssignmentMap DefaultMaterialAssignmentMap;
//! Utility function for retrieving a material entry from a MaterialAssignmentMap
AZ_INLINE const MaterialAssignment& GetMaterialAssignmentFromMap(const MaterialAssignmentMap& materials, const MaterialAssignmentId& id)
{
const auto& materialItr = materials.find(id);
return materialItr != materials.end() ? materialItr->second : DefaultMaterialAssignment;
}
const MaterialAssignment& GetMaterialAssignmentFromMap(const MaterialAssignmentMap& materials, const MaterialAssignmentId& id);
//! Utility function for retrieving a material entry from a MaterialAssignmentMap, falling back to defaults for a particular asset or the entire model
AZ_INLINE const MaterialAssignment& GetMaterialAssignmentFromMapWithFallback(const MaterialAssignmentMap& materials, const MaterialAssignmentId& id)
{
const MaterialAssignment& lodAssignment = GetMaterialAssignmentFromMap(materials, id);
if (lodAssignment.m_materialInstance.get())
{
return lodAssignment;
}
const MaterialAssignment& assetAssignment = GetMaterialAssignmentFromMap(materials, MaterialAssignmentId::CreateFromAssetOnly(id.m_materialAssetId));
if (assetAssignment.m_materialInstance.get())
{
return assetAssignment;
}
const MaterialAssignment& defaultAssignment = GetMaterialAssignmentFromMap(materials, DefaultMaterialAssignmentId);
if (defaultAssignment.m_materialInstance.get())
{
return defaultAssignment;
}
return DefaultMaterialAssignment;
}
//! Utility function for retrieving a material entry from a MaterialAssignmentMap, falling back to defaults for a particular asset
//! or the entire model
const MaterialAssignment& GetMaterialAssignmentFromMapWithFallback(
const MaterialAssignmentMap& materials, const MaterialAssignmentId& id);
//! Utility function for generating a set of available material assignments in a model
AZ_INLINE MaterialAssignmentMap GetMaterialAssignmentsFromModel(Data::Instance<AZ::RPI::Model> model)
{
MaterialAssignmentMap materials;
materials[DefaultMaterialAssignmentId] = MaterialAssignment();
if (model)
{
size_t lodIndex = 0;
for (const Data::Instance<AZ::RPI::ModelLod>& lod : model->GetLods())
{
for (const AZ::RPI::ModelLod::Mesh& mesh : lod->GetMeshes())
{
if (mesh.m_material)
{
const MaterialAssignmentId generalId = MaterialAssignmentId::CreateFromAssetOnly(mesh.m_material->GetAssetId());
materials[generalId] = MaterialAssignment(mesh.m_material->GetAsset(), mesh.m_material);
const MaterialAssignmentId specificId = MaterialAssignmentId::CreateFromLodAndAsset(lodIndex, mesh.m_material->GetAssetId());
materials[specificId] = MaterialAssignment(mesh.m_material->GetAsset(), mesh.m_material);
}
}
++lodIndex;
}
}
return materials;
}
MaterialAssignmentMap GetMaterialAssignmentsFromModel(Data::Instance<AZ::RPI::Model> model);
} // namespace Render
} // namespace AZ
@@ -1,20 +1,20 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/RTTI/ReflectContext.h>
@@ -26,6 +26,9 @@ namespace AZ
{
using MaterialAssignmentLodIndex = AZ::u64;
//! MaterialAssignmentId is used to address available and overridable material slots on a model.
//! The LOD and one of the model's original material asset IDs are used as coordinates that identify
//! a specific material slot or a set of slots matching either.
struct MaterialAssignmentId final
{
AZ_RTTI(AZ::Render::MaterialAssignmentId, "{EB603581-4654-4C17-B6DE-AE61E79EDA97}");
@@ -34,69 +37,37 @@ namespace AZ
MaterialAssignmentId() = default;
MaterialAssignmentId(MaterialAssignmentLodIndex lodIndex, const AZ::Data::AssetId& materialAssetId)
: m_lodIndex(lodIndex)
, m_materialAssetId(materialAssetId)
{
}
MaterialAssignmentId(MaterialAssignmentLodIndex lodIndex, const AZ::Data::AssetId& materialAssetId);
static MaterialAssignmentId CreateDefault()
{
return MaterialAssignmentId(NonLodIndex, AZ::Data::AssetId());
}
//! Create an ID that maps to all material slots, regardless of asset ID or LOD, effectively applying to an entire model.
static MaterialAssignmentId CreateDefault();
static MaterialAssignmentId CreateFromAssetOnly(AZ::Data::AssetId materialAssetId)
{
return MaterialAssignmentId(NonLodIndex, materialAssetId);
}
//! Create an ID that maps to all material slots with a corresponding asset ID, regardless of LOD.
static MaterialAssignmentId CreateFromAssetOnly(AZ::Data::AssetId materialAssetId);
static MaterialAssignmentId CreateFromLodAndAsset(MaterialAssignmentLodIndex lodIndex, AZ::Data::AssetId materialAssetId)
{
return MaterialAssignmentId(lodIndex, materialAssetId);
}
//! Create an ID that maps to a specific material slot with a corresponding asset ID and LOD.
static MaterialAssignmentId CreateFromLodAndAsset(MaterialAssignmentLodIndex lodIndex, AZ::Data::AssetId materialAssetId);
bool IsDefault() const
{
return m_lodIndex == NonLodIndex && !m_materialAssetId.IsValid();
}
//! Returns true if the asset ID and LOD are invalid
bool IsDefault() const;
bool IsAssetOnly() const
{
return m_lodIndex == NonLodIndex && m_materialAssetId.IsValid();
}
//! Returns true if the asset ID is valid and LOD is invalid
bool IsAssetOnly() const;
bool IsLodAndAsset() const
{
return m_lodIndex != NonLodIndex && m_materialAssetId.IsValid();
}
//! Returns true if the asset ID and LOD are both valid
bool IsLodAndAsset() const;
//! Creates a string composed of the asset path and LOD
AZStd::string ToString() const;
AZStd::string ToString() const
{
AZStd::string assetPathString;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetPathString, &AZ::Data::AssetCatalogRequests::GetAssetPathById, m_materialAssetId);
AZ::StringFunc::Path::StripPath(assetPathString);
AZ::StringFunc::Path::StripExtension(assetPathString);
return AZStd::string::format("%s:%llu", assetPathString.c_str(), m_lodIndex);
}
//! Creates a hash composed of the asset ID sub ID and LOD
size_t GetHash() const;
size_t GetHash() const
{
size_t seed = 0;
AZStd::hash_combine(seed, m_lodIndex);
AZStd::hash_combine(seed, m_materialAssetId);
return seed;
}
//! Returns true if both asset ID sub IDs and LODs match
bool operator==(const MaterialAssignmentId& rhs) const;
bool operator==(const MaterialAssignmentId& rhs) const
{
return m_lodIndex == rhs.m_lodIndex && m_materialAssetId == rhs.m_materialAssetId;
}
bool operator!=(const MaterialAssignmentId& rhs) const
{
return m_lodIndex != rhs.m_lodIndex || m_materialAssetId != rhs.m_materialAssetId;
}
//! Returns true if both asset ID sub IDs and LODs do not match
bool operator!=(const MaterialAssignmentId& rhs) const;
static constexpr MaterialAssignmentLodIndex NonLodIndex = -1;
MaterialAssignmentLodIndex m_lodIndex = NonLodIndex;
@@ -116,4 +87,4 @@ namespace AZStd
return id.GetHash();
}
};
} //namespace AZStd
} // namespace AZStd
@@ -11,8 +11,8 @@
*/
#include <Atom/Feature/Material/MaterialAssignment.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AZ
{
@@ -67,5 +67,101 @@ namespace AZ
}
}
MaterialAssignment::MaterialAssignment(const AZ::Data::AssetId& materialAssetId)
: m_materialInstance()
{
m_materialAsset.Create(materialAssetId);
}
MaterialAssignment::MaterialAssignment(const Data::Asset<RPI::MaterialAsset>& asset)
: m_materialAsset(asset)
, m_materialInstance()
{
}
MaterialAssignment::MaterialAssignment(const Data::Asset<RPI::MaterialAsset>& asset, const Data::Instance<RPI::Material>& instance)
: m_materialAsset(asset)
, m_materialInstance(instance)
{
}
void MaterialAssignment::RebuildInstance()
{
if (m_materialAsset.IsReady())
{
m_materialInstance =
m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_materialAsset) : RPI::Material::Create(m_materialAsset);
AZ_Error("MaterialAssignment", m_materialInstance, "Material instance not initialized");
}
}
AZStd::string MaterialAssignment::ToString() const
{
AZStd::string assetPathString;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
assetPathString, &AZ::Data::AssetCatalogRequests::GetAssetPathById, m_materialAsset.GetId());
return assetPathString;
}
const MaterialAssignment& GetMaterialAssignmentFromMap(const MaterialAssignmentMap& materials, const MaterialAssignmentId& id)
{
const auto& materialItr = materials.find(id);
return materialItr != materials.end() ? materialItr->second : DefaultMaterialAssignment;
}
const MaterialAssignment& GetMaterialAssignmentFromMapWithFallback(
const MaterialAssignmentMap& materials, const MaterialAssignmentId& id)
{
const MaterialAssignment& lodAssignment = GetMaterialAssignmentFromMap(materials, id);
if (lodAssignment.m_materialInstance.get())
{
return lodAssignment;
}
const MaterialAssignment& assetAssignment =
GetMaterialAssignmentFromMap(materials, MaterialAssignmentId::CreateFromAssetOnly(id.m_materialAssetId));
if (assetAssignment.m_materialInstance.get())
{
return assetAssignment;
}
const MaterialAssignment& defaultAssignment = GetMaterialAssignmentFromMap(materials, DefaultMaterialAssignmentId);
if (defaultAssignment.m_materialInstance.get())
{
return defaultAssignment;
}
return DefaultMaterialAssignment;
}
MaterialAssignmentMap GetMaterialAssignmentsFromModel(Data::Instance<AZ::RPI::Model> model)
{
MaterialAssignmentMap materials;
materials[DefaultMaterialAssignmentId] = MaterialAssignment();
if (model)
{
size_t lodIndex = 0;
for (const Data::Instance<AZ::RPI::ModelLod>& lod : model->GetLods())
{
for (const AZ::RPI::ModelLod::Mesh& mesh : lod->GetMeshes())
{
if (mesh.m_material)
{
const MaterialAssignmentId generalId = MaterialAssignmentId::CreateFromAssetOnly(mesh.m_material->GetAssetId());
materials[generalId] = MaterialAssignment(mesh.m_material->GetAsset(), mesh.m_material);
const MaterialAssignmentId specificId =
MaterialAssignmentId::CreateFromLodAndAsset(lodIndex, mesh.m_material->GetAssetId());
materials[specificId] = MaterialAssignment(mesh.m_material->GetAsset(), mesh.m_material);
}
}
++lodIndex;
}
}
return materials;
}
} // namespace Render
} // namespace AZ
@@ -11,8 +11,8 @@
*/
#include <Atom/Feature/Material/MaterialAssignmentId.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AZ
{
@@ -47,5 +47,70 @@ namespace AZ
;
}
}
MaterialAssignmentId::MaterialAssignmentId(MaterialAssignmentLodIndex lodIndex, const AZ::Data::AssetId& materialAssetId)
: m_lodIndex(lodIndex)
, m_materialAssetId(materialAssetId)
{
}
MaterialAssignmentId MaterialAssignmentId::CreateDefault()
{
return MaterialAssignmentId(NonLodIndex, AZ::Data::AssetId());
}
MaterialAssignmentId MaterialAssignmentId::CreateFromAssetOnly(AZ::Data::AssetId materialAssetId)
{
return MaterialAssignmentId(NonLodIndex, materialAssetId);
}
MaterialAssignmentId MaterialAssignmentId::CreateFromLodAndAsset(
MaterialAssignmentLodIndex lodIndex, AZ::Data::AssetId materialAssetId)
{
return MaterialAssignmentId(lodIndex, materialAssetId);
}
bool MaterialAssignmentId::IsDefault() const
{
return m_lodIndex == NonLodIndex && !m_materialAssetId.IsValid();
}
bool MaterialAssignmentId::IsAssetOnly() const
{
return m_lodIndex == NonLodIndex && m_materialAssetId.IsValid();
}
bool MaterialAssignmentId::IsLodAndAsset() const
{
return m_lodIndex != NonLodIndex && m_materialAssetId.IsValid();
}
AZStd::string MaterialAssignmentId::ToString() const
{
AZStd::string assetPathString;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
assetPathString, &AZ::Data::AssetCatalogRequests::GetAssetPathById, m_materialAssetId);
AZ::StringFunc::Path::StripPath(assetPathString);
AZ::StringFunc::Path::StripExtension(assetPathString);
return AZStd::string::format("%s:%llu", assetPathString.c_str(), m_lodIndex);
}
size_t MaterialAssignmentId::GetHash() const
{
size_t seed = 0;
AZStd::hash_combine(seed, m_lodIndex);
AZStd::hash_combine(seed, m_materialAssetId.m_subId);
return seed;
}
bool MaterialAssignmentId::operator==(const MaterialAssignmentId& rhs) const
{
return m_lodIndex == rhs.m_lodIndex && m_materialAssetId.m_subId == rhs.m_materialAssetId.m_subId;
}
bool MaterialAssignmentId::operator!=(const MaterialAssignmentId& rhs) const
{
return m_lodIndex != rhs.m_lodIndex || m_materialAssetId.m_subId != rhs.m_materialAssetId.m_subId;
}
} // namespace Render
} // namespace AZ
@@ -27,8 +27,6 @@ set(FILES
Include/Atom/Feature/ImGui/SystemBus.h
Include/Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessor.h
Include/Atom/Feature/LookupTable/LookupTableAsset.h
Include/Atom/Feature/Material/MaterialAssignment.h
Include/Atom/Feature/Material/MaterialAssignmentId.h
Include/Atom/Feature/Mesh/MeshFeatureProcessor.h
Include/Atom/Feature/PostProcessing/PostProcessingConstants.h
Include/Atom/Feature/PostProcessing/SMAAFeatureProcessorInterface.h
@@ -155,8 +153,6 @@ set(FILES
Source/LookupTable/LookupTableAsset.cpp
Source/Material/ConvertEmissiveUnitFunctor.cpp
Source/Material/ConvertEmissiveUnitFunctor.h
Source/Material/MaterialAssignment.cpp
Source/Material/MaterialAssignmentId.cpp
Source/Material/ShaderEnableFunctor.cpp
Source/Material/ShaderEnableFunctor.h
Source/Material/SubsurfaceTransmissionParameterFunctor.cpp
@@ -10,8 +10,12 @@
#
set(FILES
Include/Atom/Feature/Material/MaterialAssignment.h
Include/Atom/Feature/Material/MaterialAssignmentId.h
Include/Atom/Feature/Utils/LightingPreset.h
Include/Atom/Feature/Utils/ModelPreset.h
Source/Material/MaterialAssignment.cpp
Source/Material/MaterialAssignmentId.cpp
Source/Utils/LightingPreset.cpp
Source/Utils/ModelPreset.cpp
)
@@ -25,6 +25,7 @@ ly_add_target(
Gem::Atom_Utils.Static
Gem::Atom_Feature_Common
Gem::Atom_Feature_Common.Public
Gem::Atom_Feature_Common.Static
Gem::Atom_RPI.Public
Gem::Atom_RHI.Reflect
Gem::AtomLyIntegration_CommonFeatures.Public