diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 679e64e283..363723f7da 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -376,7 +376,7 @@ namespace AZ else { AZ_Assert(false, "Invalid mesh handle"); - return {0,0,0.0f,0.0f}; + return {RPI::Cullable::LodType::Default, 0, 0.0f, 0.0f }; } } 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 e89e180540..82e9c733c8 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Culling.h @@ -70,17 +70,22 @@ namespace AZ }; CullData m_cullData; - using LodType = uint8_t; + enum LodType : uint8_t + { + Default = 0, + ScreenCoverage, + SpecificLod, + }; using LodOverride = uint8_t; - static constexpr uint8_t NoLodOverride = AZStd::numeric_limits::max(); - static constexpr uint8_t DefaultLodType = AZStd::numeric_limits::max(); struct LodConfiguration { - LodType m_lodType; - LodOverride m_lodOverride; - float m_minimumScreenCoverage; - float m_qualityDecayRate; + LodType m_lodType = LodType::Default; + LodOverride m_lodOverride = 0; + // 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; // For default, mesh should cover at least a screen pixel at 1080p to be drawn; + // 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; }; struct LodData @@ -98,12 +103,7 @@ 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. - static constexpr float DefaultMinimumScreenCoverage = 1.0f / 1080.0f; //For default, mesh should cover at least a screen pixel at 1080p to be drawn - // The screen area decay between 0 and 1, i.e. closer to 1 -> lose quality immediately, closer to 0 -> never lose quality - static constexpr float DefaultQualityDecayRate = 0.5f; - - LodConfiguration m_lodConfiguration = { DefaultLodType, NoLodOverride, DefaultMinimumScreenCoverage, DefaultQualityDecayRate }; + LodConfiguration m_lodConfiguration; }; LodData m_lodData; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index f22d5c7224..c0d9023fa9 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -672,20 +672,25 @@ namespace AZ } }; - if (lodData.m_lodConfiguration.m_lodOverride == Cullable::NoLodOverride) + switch (lodData.m_lodConfiguration.m_lodType) { - for (const Cullable::LodData::Lod& lod : lodData.m_lods) - { - //Note that this supports overlapping lod ranges (to suport cross-fading lods, for example) - if (approxScreenPercentage >= lod.m_screenCoverageMin && approxScreenPercentage <= lod.m_screenCoverageMax) + case Cullable::LodType::SpecificLod: + if (lodData.m_lodConfiguration.m_lodOverride < lodData.m_lods.size()) { - addLodToDrawPacket(lod); + addLodToDrawPacket(lodData.m_lods.at(lodData.m_lodConfiguration.m_lodOverride)); } - } - } - else if(lodData.m_lodConfiguration.m_lodOverride < lodData.m_lods.size()) - { - addLodToDrawPacket(lodData.m_lods.at(lodData.m_lodConfiguration.m_lodOverride)); + break; + case Cullable::LodType::ScreenCoverage: + default: + for (const Cullable::LodData::Lod& lod : lodData.m_lods) + { + // Note that this supports overlapping lod ranges (to suport cross-fading lods, for example) + if (approxScreenPercentage >= lod.m_screenCoverageMin && approxScreenPercentage <= lod.m_screenCoverageMax) + { + addLodToDrawPacket(lod); + } + } + break; } return numVisibleDrawPackets; diff --git a/Gems/Atom/Tools/MaterialEditor/Assets/MaterialEditor/ViewportModels/QuadSphere_Default.material b/Gems/Atom/Tools/MaterialEditor/Assets/MaterialEditor/ViewportModels/QuadSphere_Default.material new file mode 100644 index 0000000000..4905c302fe --- /dev/null +++ b/Gems/Atom/Tools/MaterialEditor/Assets/MaterialEditor/ViewportModels/QuadSphere_Default.material @@ -0,0 +1,28 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardPBR.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 3, + "properties": { + "baseColor": { + "color": [ + 0.800000011920929, + 0.800000011920929, + 0.800000011920929, + 1.0 + ], + "textureMap": "MaterialEditor/ViewportModels/_dev_shaderball_00_basecolor.png" + }, + "emissive": { + "color": [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + "opacity": { + "factor": 1.0 + } + } +} \ No newline at end of file diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp index 0719d9f2bb..ff0741fc31 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp @@ -79,20 +79,25 @@ namespace AZ ->DataElement(AZ::Edit::UIHandlers::CheckBox, &MeshComponentConfig::m_useForwardPassIblSpecular, "Use Forward Pass IBL Specular", "Renders IBL specular reflections in the forward pass, using only the most influential probe (based on the position of the entity) and the global IBL cubemap. Can reduce rendering costs, but only recommended for static objects that are affected by at most one reflection probe.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - ->ClassElement(AZ::Edit::ClassElements::Group, "Lod Configuration") - ->Attribute(AZ::Edit::Attributes::AutoExpand, false) ->DataElement(AZ::Edit::UIHandlers::ComboBox, &MeshComponentConfig::m_lodType, "Lod Type", "Lod Method.") ->Attribute(AZ::Edit::Attributes::EnumValues, &MeshComponentConfig::GetLodTypeValues) + ->Attribute(AZ::Edit::Attributes::Visibility, &MeshComponentConfig::IsAssetSet) + ->ClassElement(AZ::Edit::ClassElements::Group, "Lod Configuration") + ->Attribute(AZ::Edit::Attributes::AutoExpand, false) + ->Attribute(AZ::Edit::Attributes::Visibility, &MeshComponentConfig::ShowLodConfig) ->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::LodTypeIsSpecificLOD) ->DataElement(AZ::Edit::UIHandlers::Slider, &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) ->Attribute(AZ::Edit::Attributes::Suffix, " percent") + ->Attribute(AZ::Edit::Attributes::Visibility, &MeshComponentConfig::LodTypeIsScreenCoverage) ->DataElement(AZ::Edit::UIHandlers::Slider, &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) + ->Attribute(AZ::Edit::Attributes::Visibility, &MeshComponentConfig::LodTypeIsScreenCoverage) ; } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp index adcf570899..3d049d0f97 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp @@ -31,11 +31,30 @@ namespace AZ { namespace Render { + + namespace MeshComponentControllerVersionUtility + { + bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) + { + if (classElement.GetVersion() < 2) + { + RPI::Cullable::LodOverride lodOverride = classElement.FindElement(AZ_CRC("LodOverride")); + static constexpr uint8_t old_NoLodOverride = AZStd::numeric_limits ::max(); + if (lodOverride == old_NoLodOverride) + { + classElement.AddElementWithData(context, "LodType", RPI::Cullable::LodType::SpecificLod); + } + } + return true; + } + } // namespace MeshComponentControllerVersionUtility + void MeshComponentConfig::Reflect(ReflectContext* context) { if (auto* serializeContext = azrtti_cast(context)) { serializeContext->Class() + //->Version(2, &MeshComponentControllerVersionUtility::VersionConverter) ->Version(1) ->Field("ModelAsset", &MeshComponentConfig::m_modelAsset) ->Field("SortKey", &MeshComponentConfig::m_sortKey) @@ -53,6 +72,21 @@ namespace AZ return m_modelAsset.GetId().IsValid(); } + bool MeshComponentConfig::LodTypeIsScreenCoverage() + { + return m_lodType == RPI::Cullable::LodType::ScreenCoverage; + } + + bool MeshComponentConfig::LodTypeIsSpecificLOD() + { + return m_lodType == RPI::Cullable::LodType::SpecificLod; + } + + bool MeshComponentConfig::ShowLodConfig() + { + return LodTypeIsScreenCoverage() && LodTypeIsSpecificLOD(); + } + AZStd::vector> MeshComponentConfig::GetLodOverrideValues() { AZStd::vector> values; @@ -75,9 +109,9 @@ namespace AZ } values.reserve(lodCount + 1); - values.push_back({ RPI::Cullable::NoLodOverride, "Not Set" }); + values.push_back({0, "Default (Highest)" }); - for (uint32_t i = 0; i < lodCount; ++i) + for (uint32_t i = 1; i < lodCount; ++i) { AZStd::string enumDescription = AZStd::string::format("Lod %i", i); values.push_back({ aznumeric_cast(i), enumDescription.c_str() }); @@ -89,7 +123,9 @@ namespace AZ AZStd::vector> MeshComponentConfig::GetLodTypeValues() { return { - {RPI::Cullable::DefaultLodType, "Default"} + {aznumeric_cast(0), "Default"}, + {aznumeric_cast(1), "Screen Coverage" }, + {aznumeric_cast(2), "Specific Lod" } }; } @@ -112,12 +148,12 @@ namespace AZ if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { - behaviorContext->ConstantProperty("NoLodOverride", BehaviorConstant(RPI::Cullable::NoLodOverride)) + behaviorContext->ConstantProperty("DefaultLodOverride", BehaviorConstant(0)) ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) ->Attribute(AZ::Script::Attributes::Category, "render") ->Attribute(AZ::Script::Attributes::Module, "render"); - behaviorContext->ConstantProperty("DefaultLodType", BehaviorConstant(RPI::Cullable::DefaultLodType)) + behaviorContext->ConstantProperty("DefaultLodType", BehaviorConstant(RPI::Cullable::LodType::Default)) ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) ->Attribute(AZ::Script::Attributes::Category, "render") ->Attribute(AZ::Script::Attributes::Module, "render"); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h index 1b479ed3eb..5f38088ca3 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h @@ -44,6 +44,9 @@ namespace AZ // Editor helper functions bool IsAssetSet(); + bool LodTypeIsScreenCoverage(); + bool LodTypeIsSpecificLOD(); + bool ShowLodConfig(); AZStd::vector> GetLodOverrideValues(); AZStd::vector> GetLodTypeValues(); @@ -52,10 +55,10 @@ namespace AZ bool m_excludeFromReflectionCubeMaps = false; bool m_useForwardPassIblSpecular = false; - RPI::Cullable::LodType m_lodType = RPI::Cullable::DefaultLodType; - RPI::Cullable::LodOverride m_lodOverride = RPI::Cullable::NoLodOverride; - float m_minimumScreenCoverage = RPI::Cullable::LodData::DefaultMinimumScreenCoverage; - float m_qualityDecayRate = RPI::Cullable::LodData::DefaultQualityDecayRate; + RPI::Cullable::LodType m_lodType = RPI::Cullable::LodType::Default; + RPI::Cullable::LodOverride m_lodOverride = aznumeric_cast(0); + float m_minimumScreenCoverage = 1.0f / 1080.0f; + float m_qualityDecayRate = 0.5f; }; class MeshComponentController final