From 931a127b7b8b42f92f7a391c37cb4ac5c77bc69c Mon Sep 17 00:00:00 2001 From: guthadam Date: Wed, 12 May 2021 13:47:47 -0500 Subject: [PATCH 1/2] 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 --- .../Feature/Material/MaterialAssignment.h | 125 ++++-------------- .../Feature/Material/MaterialAssignmentId.h | 101 +++++--------- .../Source/Material/MaterialAssignment.cpp | 98 +++++++++++++- .../Source/Material/MaterialAssignmentId.cpp | 67 +++++++++- .../Code/atom_feature_common_files.cmake | 4 - ...m_feature_common_staticlibrary_files.cmake | 4 + .../EMotionFXAtom/Code/CMakeLists.txt | 1 + 7 files changed, 232 insertions(+), 168 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h index 089f4e8c62..58fb2e0f8a 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignment.h @@ -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 -#include -#include #include +#include +#include #include +#include 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& asset) - : m_materialAsset(asset) - , m_materialInstance() - { - } + MaterialAssignment(const Data::Asset& asset); - MaterialAssignment(const Data::Asset& asset, const Data::Instance& instance) - : m_materialAsset(asset) - , m_materialInstance(instance) - { - } + MaterialAssignment(const Data::Asset& asset, const Data::Instance& 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 m_materialAsset; Data::Instance 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 model) - { - MaterialAssignmentMap materials; - materials[DefaultMaterialAssignmentId] = MaterialAssignment(); - - if (model) - { - size_t lodIndex = 0; - for (const Data::Instance& 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 model); } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignmentId.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignmentId.h index 267e65743a..80de6d8041 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignmentId.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Material/MaterialAssignmentId.h @@ -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 #include #include -#include #include #include #include @@ -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 diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp index f817e8324b..ffb5469aef 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignment.cpp @@ -11,8 +11,8 @@ */ #include -#include #include +#include 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& asset) + : m_materialAsset(asset) + , m_materialInstance() + { + } + + MaterialAssignment::MaterialAssignment(const Data::Asset& asset, const Data::Instance& 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 model) + { + MaterialAssignmentMap materials; + materials[DefaultMaterialAssignmentId] = MaterialAssignment(); + + if (model) + { + size_t lodIndex = 0; + for (const Data::Instance& 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 diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignmentId.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignmentId.cpp index 4813136d2e..0fe89d49b8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignmentId.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignmentId.cpp @@ -11,8 +11,8 @@ */ #include -#include #include +#include 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 diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 47000d8a5c..8926b0c19f 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -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 diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_staticlibrary_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_staticlibrary_files.cmake index d33e861c02..553f307409 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_staticlibrary_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_staticlibrary_files.cmake @@ -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 ) diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt b/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt index dc969d61d3..6492f4f13a 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/CMakeLists.txt @@ -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 From d85e0500d5d33c215dc96898b0269a9c1138c884 Mon Sep 17 00:00:00 2001 From: guthadam Date: Thu, 13 May 2021 00:55:29 -0500 Subject: [PATCH 2/2] PR feedback --- .../Common/Code/Source/Material/MaterialAssignmentId.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignmentId.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignmentId.cpp index 0fe89d49b8..59de229445 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignmentId.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialAssignmentId.cpp @@ -110,7 +110,7 @@ namespace AZ bool MaterialAssignmentId::operator!=(const MaterialAssignmentId& rhs) const { - return m_lodIndex != rhs.m_lodIndex || m_materialAssetId.m_subId != rhs.m_materialAssetId.m_subId; + return !(*this == rhs); } } // namespace Render } // namespace AZ