From 5b88d96eec29a5b6359243f5413693f3acf619f0 Mon Sep 17 00:00:00 2001 From: Kyle B Date: Mon, 2 Aug 2021 13:36:10 -0700 Subject: [PATCH] Exposed fields to the mesh component giving greater Lod control from the editor Signed-off-by: Kyle B --- .../Atom/Feature/Mesh/MeshFeatureProcessor.h | 12 ++- .../Mesh/MeshFeatureProcessorInterface.h | 7 ++ .../Code/Source/Mesh/MeshFeatureProcessor.cpp | 74 +++++++++++++++++-- .../Code/Include/Atom/RPI.Public/Culling.h | 5 ++ .../CommonFeatures/Mesh/MeshComponentBus.h | 6 ++ .../Code/Source/Mesh/EditorMeshComponent.cpp | 7 ++ .../Source/Mesh/MeshComponentController.cpp | 32 ++++++++ .../Source/Mesh/MeshComponentController.h | 7 ++ .../Code/Source/AtomActorInstance.cpp | 20 +++++ .../Code/Source/AtomActorInstance.h | 4 + 10 files changed, 167 insertions(+), 7 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h index a2d9517ec4..3cdff52ba7 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h @@ -63,8 +63,12 @@ namespace AZ void SetRayTracingData(); void SetSortKey(RHI::DrawItemSortKey sortKey); RHI::DrawItemSortKey GetSortKey(); - void SetLodOverride(RPI::Cullable::LodOverride lodOverride); + void SetLodOverride( RPI::Cullable::LodOverride lodOverride); RPI::Cullable::LodOverride GetLodOverride(); + void SetMinimumScreenCoverage(float minimumScreenCoverage); + float GetMinimumScreenCoverage(); + void SetQualityDecayRate(float qualityDecayRate); + float GetQualityDecayRate(); void UpdateDrawPackets(bool forceUpdate = false); void BuildCullable(); void UpdateCullBounds(const TransformServiceFeatureProcessor* transformService); @@ -158,6 +162,12 @@ namespace AZ void SetLodOverride(const MeshHandle& meshHandle, RPI::Cullable::LodOverride lodOverride) override; RPI::Cullable::LodOverride GetLodOverride(const MeshHandle& meshHandle) override; + void SetMinimumScreenCoverage(const MeshHandle& meshHandle, float minimumScreenCoverage) override; + float GetMinimumScreenCoverage(const MeshHandle& meshHandle) override; + + void SetQualityDecayRate(const MeshHandle& meshHandle, float qualityDecayRate) override; + float GetQualityDecayRate(const MeshHandle& meshHandle) override; + void SetExcludeFromReflectionCubeMaps(const MeshHandle& meshHandle, bool excludeFromReflectionCubeMaps) override; void SetRayTracingEnabled(const MeshHandle& meshHandle, bool rayTracingEnabled) override; void SetVisible(const MeshHandle& meshHandle, bool visible) override; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h index 4e74302276..b71b420625 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h @@ -98,6 +98,13 @@ namespace AZ virtual void SetLodOverride(const MeshHandle& meshHandle, RPI::Cullable::LodOverride lodOverride) = 0; //! Gets the LOD override for a given mesh handle. virtual RPI::Cullable::LodOverride GetLodOverride(const MeshHandle& meshHandle) = 0; + + virtual void SetMinimumScreenCoverage(const MeshHandle& meshHandle, float minimumScreenCoverage) = 0; + virtual float GetMinimumScreenCoverage(const MeshHandle& meshHandle) = 0; + + virtual void SetQualityDecayRate(const MeshHandle& meshHandle, float qualityDecayRate) = 0; + virtual float GetQualityDecayRate(const MeshHandle& meshHandle) = 0; + //! Sets the option to exclude this mesh from baked reflection probe cubemaps virtual void SetExcludeFromReflectionCubeMaps(const MeshHandle& meshHandle, bool excludeFromReflectionCubeMaps) = 0; //! Sets the option to exclude this mesh from raytracing diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index c6362f3a5d..ce8ead9318 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -380,6 +380,48 @@ namespace AZ } } + void MeshFeatureProcessor::SetMinimumScreenCoverage( const MeshHandle& meshHandle, float minimumScreenCoverage) + { + if (meshHandle.IsValid()) + { + meshHandle->SetMinimumScreenCoverage(minimumScreenCoverage); + } + } + + float MeshFeatureProcessor::GetMinimumScreenCoverage(const MeshHandle& meshHandle) + { + if (meshHandle.IsValid()) + { + return meshHandle->GetMinimumScreenCoverage(); + } + else + { + AZ_Assert(false, "Invalid mesh handle"); + return 0; + } + } + + void MeshFeatureProcessor::SetQualityDecayRate(const MeshHandle& meshHandle, float qualityDecayRate) + { + if (meshHandle.IsValid()) + { + meshHandle->SetQualityDecayRate(qualityDecayRate); + } + } + + float MeshFeatureProcessor::GetQualityDecayRate(const MeshHandle& meshHandle) + { + if (meshHandle.IsValid()) + { + return meshHandle->GetQualityDecayRate(); + } + else + { + AZ_Assert(false, "Invalid mesh handle"); + return 0; + } + } + void MeshFeatureProcessor::SetExcludeFromReflectionCubeMaps(const MeshHandle& meshHandle, bool excludeFromReflectionCubeMaps) { if (meshHandle.IsValid()) @@ -1001,6 +1043,26 @@ namespace AZ return m_cullable.m_lodData.m_lodOverride; } + void MeshDataInstance::SetMinimumScreenCoverage(float minimumScreenCoverage) + { + m_cullable.m_lodData.m_minimumScreenCoverage = minimumScreenCoverage; + } + + float MeshDataInstance::GetMinimumScreenCoverage() + { + return m_cullable.m_lodData.m_minimumScreenCoverage; + } + + void MeshDataInstance::SetQualityDecayRate(float qualityDecayRate) + { + m_cullable.m_lodData.m_qualityDecayRate = qualityDecayRate; + } + + float MeshDataInstance::GetQualityDecayRate() + { + return m_cullable.m_lodData.m_qualityDecayRate; + } + void MeshDataInstance::UpdateDrawPackets(bool forceUpdate /*= false*/) { AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); @@ -1036,13 +1098,11 @@ namespace AZ cullData.m_drawListMask.reset(); const size_t lodCount = lodAssets.size(); + for (size_t lodIndex = 0; lodIndex < lodCount; ++lodIndex) { //initialize the lod RPI::Cullable::LodData::Lod& lod = lodData.m_lods[lodIndex]; - //[GFX TODO][ATOM-5562] - Level of detail: override lod distances and add global lod multiplier(s) - static const float MinimumScreenCoverage = 1.0f/1080.0f; //mesh should cover at least a screen pixel at 1080p to be drawn - static const float ReductionFactor = 0.5f; if (lodIndex == 0) { //first lod @@ -1051,17 +1111,19 @@ namespace AZ else { //every other lod: use the previous lod's min - lod.m_screenCoverageMax = AZStd::GetMax(lodData.m_lods[lodIndex-1].m_screenCoverageMin, MinimumScreenCoverage); + lod.m_screenCoverageMax = AZStd::GetMax(lodData.m_lods[lodIndex - 1].m_screenCoverageMin, lodData.m_minimumScreenCoverage); } + if (lodIndex < lodAssets.size() - 1) { //first and middle lods: compute a stepdown value for the min - lod.m_screenCoverageMin = AZStd::GetMax(ReductionFactor * lod.m_screenCoverageMax, MinimumScreenCoverage); + lod.m_screenCoverageMin = + AZStd::GetMax(lodData.m_qualityDecayRate * lod.m_screenCoverageMax, lodData.m_minimumScreenCoverage); } else { //last lod: use MinimumScreenCoverage for the min - lod.m_screenCoverageMin = MinimumScreenCoverage; + lod.m_screenCoverageMin = lodData.m_minimumScreenCoverage; } lod.m_drawPackets.clear(); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h index bcc4dfb89f..30cf091735 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -88,6 +88,11 @@ namespace AZ //! Suggest setting to: 0.5f*localAabb.GetExtents().GetMaxElement() float m_lodSelectionRadius = 1.0f; + // the minimum possibe area a sphere enclosing a mesh projected onto the screen should have before it is culled. + float m_minimumScreenCoverage = 1.0f / 1080.0f; + // The screen area decay between 0 and 1, i.e. closer to 1 -> lose quality immediately, closer to 0 -> never lose quality + float m_qualityDecayRate = 0.5f; + LodOverride m_lodOverride = NoLodOverride; }; LodData m_lodData; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h index ff75f7f45a..baef507b7a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h @@ -39,6 +39,12 @@ namespace AZ virtual void SetLodOverride(RPI::Cullable::LodOverride lodOverride) = 0; virtual RPI::Cullable::LodOverride GetLodOverride() const = 0; + virtual void SetMinimumScreenCoverage(float minimumScreenCoverage) = 0; + virtual float GetMinimumScreenCoverage() const = 0; + + virtual void SetQualityDecayRate(float qualityDecayRate) = 0; + virtual float GetQualityDecayRate() const = 0; + virtual void SetVisibility(bool visible) = 0; virtual bool GetVisibility() const = 0; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp index fc62690cc4..bdbf445b7f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp @@ -76,6 +76,13 @@ namespace AZ ->DataElement(AZ::Edit::UIHandlers::ComboBox, &MeshComponentConfig::m_lodOverride, "Lod Override", "Allows the rendered LOD to be overridden instead of being calculated automatically.") ->Attribute(AZ::Edit::Attributes::EnumValues, &MeshComponentConfig::GetLodOverrideValues) ->Attribute(AZ::Edit::Attributes::Visibility, &MeshComponentConfig::IsAssetSet) + ->DataElement(AZ::Edit::UIHandlers::Default, &MeshComponentConfig::m_minimumScreenCoverage, "Minimum Screen Coverage", "Minimum proportion of screen area an entitiy takes up, after that the entitiy is culled.") + ->Attribute(AZ::Edit::Attributes::Min, 0.f) + ->Attribute(AZ::Edit::Attributes::Max, 1.f) + ->DataElement(AZ::Edit::UIHandlers::Default, &MeshComponentConfig::m_qualityDecayRate, "Quality Decay Rate", + "Rate at which mesh quality decays (0 -> always stay highest quality, 1 -> quality falls off to lowest quality immediately).") + ->Attribute(AZ::Edit::Attributes::Min, 0.f) + ->Attribute(AZ::Edit::Attributes::Max, 1.f) ->DataElement(AZ::Edit::UIHandlers::CheckBox, &MeshComponentConfig::m_excludeFromReflectionCubeMaps, "Exclude from reflection cubemaps", "Mesh will not be visible in baked reflection probe cubemaps") ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement(AZ::Edit::UIHandlers::CheckBox, &MeshComponentConfig::m_useForwardPassIblSpecular, "Use Forward Pass IBL Specular", diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp index 29bd9a839b..5fde185e4c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp @@ -40,6 +40,8 @@ namespace AZ ->Field("ModelAsset", &MeshComponentConfig::m_modelAsset) ->Field("SortKey", &MeshComponentConfig::m_sortKey) ->Field("LodOverride", &MeshComponentConfig::m_lodOverride) + ->Field("MinimumScreenCoverage", &MeshComponentConfig::m_minimumScreenCoverage) + ->Field("QualityDecayRate", &MeshComponentConfig::m_qualityDecayRate) ->Field("ExcludeFromReflectionCubeMaps", &MeshComponentConfig::m_excludeFromReflectionCubeMaps) ->Field("UseForwardPassIBLSpecular", &MeshComponentConfig::m_useForwardPassIblSpecular); } @@ -116,10 +118,16 @@ namespace AZ ->Event("GetSortKey", &MeshComponentRequestBus::Events::GetSortKey) ->Event("SetLodOverride", &MeshComponentRequestBus::Events::SetLodOverride) ->Event("GetLodOverride", &MeshComponentRequestBus::Events::GetLodOverride) + ->Event("SetMinimumScreenCoverage", &MeshComponentRequestBus::Events::SetMinimumScreenCoverage) + ->Event("GetMinimumScreenCoverage", &MeshComponentRequestBus::Events::GetMinimumScreenCoverage) + ->Event("SetQualityDecayRate", &MeshComponentRequestBus::Events::SetQualityDecayRate) + ->Event("GetQualityDecayRate", &MeshComponentRequestBus::Events::GetQualityDecayRate) ->VirtualProperty("ModelAssetId", "GetModelAssetId", "SetModelAssetId") ->VirtualProperty("ModelAssetPath", "GetModelAssetPath", "SetModelAssetPath") ->VirtualProperty("SortKey", "GetSortKey", "SetSortKey") ->VirtualProperty("LodOverride", "GetLodOverride", "SetLodOverride") + ->VirtualProperty("MinimumScreenCoverage", "GetMinimumScreenCoverage", "SetMinimumScreenCoverage") + ->VirtualProperty("QualityDecayRate", "GetQualityDecayRate", "SetQualityDecayRate") ; } } @@ -325,6 +333,8 @@ namespace AZ m_meshFeatureProcessor->SetTransform(m_meshHandle, transform, m_cachedNonUniformScale); m_meshFeatureProcessor->SetSortKey(m_meshHandle, m_configuration.m_sortKey); m_meshFeatureProcessor->SetLodOverride(m_meshHandle, m_configuration.m_lodOverride); + m_meshFeatureProcessor->SetMinimumScreenCoverage(m_meshHandle, m_configuration.m_minimumScreenCoverage); + m_meshFeatureProcessor->SetQualityDecayRate(m_meshHandle, m_configuration.m_qualityDecayRate); m_meshFeatureProcessor->SetExcludeFromReflectionCubeMaps(m_meshHandle, m_configuration.m_excludeFromReflectionCubeMaps); m_meshFeatureProcessor->SetVisible(m_meshHandle, m_isVisible); @@ -426,6 +436,28 @@ namespace AZ return m_meshFeatureProcessor->GetSortKey(m_meshHandle); } + void MeshComponentController::SetMinimumScreenCoverage(float minimumScreenCoverage) + { + m_configuration.m_minimumScreenCoverage = minimumScreenCoverage; + m_meshFeatureProcessor->SetMinimumScreenCoverage(m_meshHandle, minimumScreenCoverage); + } + + float MeshComponentController::GetMinimumScreenCoverage() const + { + return m_meshFeatureProcessor->GetMinimumScreenCoverage(m_meshHandle); + } + + void MeshComponentController::SetQualityDecayRate(float qualityDecayRate) + { + m_configuration.m_qualityDecayRate = qualityDecayRate; + m_meshFeatureProcessor->SetQualityDecayRate(m_meshHandle, qualityDecayRate); + } + + float MeshComponentController::GetQualityDecayRate() const + { + return m_meshFeatureProcessor->GetQualityDecayRate(m_meshHandle); + } + void MeshComponentController::SetVisibility(bool visible) { if (m_isVisible != visible) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h index 80b483452f..f94771b3e5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h @@ -46,6 +46,8 @@ namespace AZ Data::Asset m_modelAsset = { AZ::Data::AssetLoadBehavior::QueueLoad }; RHI::DrawItemSortKey m_sortKey = 0; RPI::Cullable::LodOverride m_lodOverride = RPI::Cullable::NoLodOverride; + float m_minimumScreenCoverage = 1.0f / 1080.0f; + float m_qualityDecayRate = 0.5f; bool m_excludeFromReflectionCubeMaps = false; bool m_useForwardPassIblSpecular = false; }; @@ -97,6 +99,11 @@ namespace AZ void SetLodOverride(RPI::Cullable::LodOverride lodOverride) override; RPI::Cullable::LodOverride GetLodOverride() const override; + void SetMinimumScreenCoverage(float minimumScreenCoverage) override; + float GetMinimumScreenCoverage() const override; + void SetQualityDecayRate(float qualityDecayRate) override; + float GetQualityDecayRate() const override; + void SetVisibility(bool visible) override; bool GetVisibility() const override; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index 4c6045d7ce..63912aa8a6 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -415,6 +415,26 @@ namespace AZ return m_meshFeatureProcessor->GetLodOverride(*m_meshHandle); } + void AtomActorInstance::SetMinimumScreenCoverage(float minimumScreenCoverage) + { + m_meshFeatureProcessor->SetMinimumScreenCoverage(*m_meshHandle, minimumScreenCoverage); + } + + float AtomActorInstance::GetMinimumScreenCoverage() const + { + return m_meshFeatureProcessor->GetMinimumScreenCoverage(*m_meshHandle); + } + + void AtomActorInstance::SetQualityDecayRate(float qualityDecayRate) + { + m_meshFeatureProcessor->SetQualityDecayRate(*m_meshHandle, qualityDecayRate); + } + + float AtomActorInstance::GetQualityDecayRate() const + { + return m_meshFeatureProcessor->GetQualityDecayRate(*m_meshHandle); + } + void AtomActorInstance::SetVisibility(bool visible) { SetIsVisible(visible); diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h index c854fed3c1..bd9afc668e 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h @@ -140,6 +140,10 @@ namespace AZ RHI::DrawItemSortKey GetSortKey() const override; void SetLodOverride(RPI::Cullable::LodOverride lodOverride) override; RPI::Cullable::LodOverride GetLodOverride() const override; + void SetMinimumScreenCoverage(float minimumScreenCoverage) override; + float GetMinimumScreenCoverage() const override; + void SetQualityDecayRate(float qualityDecayRate) override; + float GetQualityDecayRate() const override; void SetVisibility(bool visible) override; bool GetVisibility() const override; // GetWorldBounds/GetLocalBounds already overridden by BoundsRequestBus::Handler