From 4a4c93f8662e0cc69c178ebd23101bf27762e86d Mon Sep 17 00:00:00 2001 From: sphrose <82213493+sphrose@users.noreply.github.com> Date: Tue, 5 Oct 2021 17:53:05 +0100 Subject: [PATCH] Terrain/sphrose/surface gradient list component (#4409) * cherry-pick conflict fix Signed-off-by: sphrose <82213493+sphrose@users.noreply.github.com> * Missed include file Signed-off-by: sphrose <82213493+sphrose@users.noreply.github.com> * review changes. Signed-off-by: sphrose <82213493+sphrose@users.noreply.github.com> * cherry-pick merge fix Signed-off-by: sphrose <82213493+sphrose@users.noreply.github.com> * review changes. Signed-off-by: sphrose <82213493+sphrose@users.noreply.github.com> * bug fix Signed-off-by: sphrose <82213493+sphrose@users.noreply.github.com> * review changes. Signed-off-by: sphrose <82213493+sphrose@users.noreply.github.com> * compile fix Signed-off-by: sphrose <82213493+sphrose@users.noreply.github.com> * compile fix Signed-off-by: sphrose <82213493+sphrose@users.noreply.github.com> --- .../Terrain/TerrainDataRequestBus.h | 47 +++++- Gems/PhysX/Code/Tests/PhysXTestUtil.h | 4 + .../SurfaceData/SurfaceDataConstants.h | 3 +- .../Ebuses/TerrainAreaSurfaceRequestBus.h | 35 ++++ .../Ebuses/TerrainGradientSurfaceListBus.h | 34 ++++ .../TerrainSurfaceGradientListComponent.cpp | 157 ++++++++++++++++++ .../TerrainSurfaceGradientListComponent.h | 77 +++++++++ ...torTerrainSurfaceGradientListComponent.cpp | 22 +++ ...ditorTerrainSurfaceGradientListComponent.h | 32 ++++ .../Code/Source/EditorTerrainModule.cpp | 2 + Gems/Terrain/Code/Source/TerrainModule.cpp | 2 + .../Source/TerrainSystem/TerrainSystem.cpp | 143 +++++++++++++--- .../Code/Source/TerrainSystem/TerrainSystem.h | 30 +++- .../Code/terrain_editor_shared_files.cmake | 2 + Gems/Terrain/Code/terrain_files.cmake | 3 + 15 files changed, 567 insertions(+), 26 deletions(-) create mode 100644 Gems/Terrain/Code/Include/Terrain/Ebuses/TerrainAreaSurfaceRequestBus.h create mode 100644 Gems/Terrain/Code/Include/Terrain/Ebuses/TerrainGradientSurfaceListBus.h create mode 100644 Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp create mode 100644 Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.h create mode 100644 Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.cpp create mode 100644 Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.h diff --git a/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h b/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h index 92eb28a110..3f6a8f0960 100644 --- a/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h +++ b/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -17,16 +18,38 @@ namespace AzFramework { namespace SurfaceData { + namespace Constants + { + static const char* s_unassignedTagName = "(unassigned)"; + } + struct SurfaceTagWeight { AZ_TYPE_INFO(SurfaceTagWeight, "{EA14018E-E853-4BF5-8E13-D83BB99A54CC}"); - AZ::Crc32 m_surfaceType; - float m_weight; //! A Value in the range [0.0f .. 1.0f] + AZ::Crc32 m_surfaceType = AZ::Crc32(Constants::s_unassignedTagName); + float m_weight = 0.0f; //! A Value in the range [0.0f .. 1.0f] //! Don't call this directly. TerrainDataRequests::Reflect is doing it already. static void Reflect(AZ::ReflectContext* context); }; + + struct SurfaceTagWeightComparator + { + bool operator()(const SurfaceTagWeight& tagWeight1, const SurfaceTagWeight& tagWeight2) const + { + if (!AZ::IsClose(tagWeight1.m_weight, tagWeight2.m_weight)) + { + return tagWeight1.m_weight > tagWeight2.m_weight; + } + else + { + return tagWeight1.m_surfaceType > tagWeight2.m_surfaceType; + } + } + }; + + using OrderedSurfaceTagWeightSet = AZStd::set; } //namespace SurfaceData namespace Terrain @@ -75,8 +98,28 @@ namespace AzFramework //! @terrainExists: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain HOLE then *terrainExistsPtr will be set to false, //! otherwise *terrainExistsPtr will be set to true. virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeight(AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; + virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromVector2(const AZ::Vector2& inPosition, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const = 0; virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromFloats(float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0; + //! Given an XY coordinate, return the set of surface types and weights. The Vector3 input position version is defined to ignore + //! the input Z value. + virtual void GetSurfaceWeights( + const AZ::Vector3& inPosition, + SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + Sampler sampleFilter = Sampler::DEFAULT, + bool* terrainExistsPtr = nullptr) const = 0; + virtual void GetSurfaceWeightsFromVector2( + const AZ::Vector2& inPosition, + SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + Sampler sampleFilter = Sampler::DEFAULT, + bool* terrainExistsPtr = nullptr) const = 0; + virtual void GetSurfaceWeightsFromFloats( + float x, + float y, + SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + Sampler sampleFilter = Sampler::DEFAULT, + bool* terrainExistsPtr = nullptr) const = 0; + //! Convenience function for low level systems that can't do a reverse lookup from Crc to string. Everyone else should use GetMaxSurfaceWeight or GetMaxSurfaceWeightFromFloats. //! Not available in the behavior context. //! Returns nullptr if the position is inside a hole or outside of the terrain boundaries. diff --git a/Gems/PhysX/Code/Tests/PhysXTestUtil.h b/Gems/PhysX/Code/Tests/PhysXTestUtil.h index 8e81b9cb89..1c86768de7 100644 --- a/Gems/PhysX/Code/Tests/PhysXTestUtil.h +++ b/Gems/PhysX/Code/Tests/PhysXTestUtil.h @@ -115,7 +115,11 @@ namespace PhysX } float GetHeightFromFloats(float, float, Sampler, bool*) const override { return {}; } AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeight(AZ::Vector3, Sampler, bool*) const override { return {}; } + AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromVector2(const AZ::Vector2&, Sampler, bool*) const override { return {}; } AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromFloats(float, float, Sampler, bool*) const override { return {}; } + void GetSurfaceWeights(const AZ::Vector3&, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&, Sampler, bool*) const override {} + void GetSurfaceWeightsFromVector2(const AZ::Vector2&, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&, Sampler, bool*) const override{}; + void GetSurfaceWeightsFromFloats(float, float, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet&, Sampler, bool*) const override {} const char* GetMaxSurfaceName(AZ::Vector3, Sampler, bool*) const override { return {}; } bool GetIsHoleFromFloats(float, float, Sampler) const override { return {}; } AZ::Vector3 GetNormal(AZ::Vector3, Sampler, bool*) const override { return {}; } diff --git a/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataConstants.h b/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataConstants.h index 9b18e4f9e7..3f4a1bb605 100644 --- a/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataConstants.h +++ b/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataConstants.h @@ -9,12 +9,13 @@ #pragma once #include +#include namespace SurfaceData { namespace Constants { - static const char* s_unassignedTagName = "(unassigned)"; + static const char* s_unassignedTagName = AzFramework::SurfaceData::Constants::s_unassignedTagName; static const char* s_terrainHoleTagName = "terrainHole"; static const char* s_terrainTagName = "terrain"; diff --git a/Gems/Terrain/Code/Include/Terrain/Ebuses/TerrainAreaSurfaceRequestBus.h b/Gems/Terrain/Code/Include/Terrain/Ebuses/TerrainAreaSurfaceRequestBus.h new file mode 100644 index 0000000000..4c04cf1e42 --- /dev/null +++ b/Gems/Terrain/Code/Include/Terrain/Ebuses/TerrainAreaSurfaceRequestBus.h @@ -0,0 +1,35 @@ +/* + * 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 + +#include + +namespace Terrain +{ + + //! This bus provides retrieval of information from Terrain Surfaces. + class TerrainAreaSurfaceRequests + : public AZ::ComponentBus + { + public: + //////////////////////////////////////////////////////////////////////// + // EBusTraits + using MutexType = AZStd::recursive_mutex; + //////////////////////////////////////////////////////////////////////// + + virtual ~TerrainAreaSurfaceRequests() = default; + + //! Get the surfaces and weights from a gradient at a given position sorted into descending order of weight. + virtual void GetSurfaceWeights(const AZ::Vector3& inPosition, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights) const = 0; + }; + + using TerrainAreaSurfaceRequestBus = AZ::EBus; +} // namespace Terrain diff --git a/Gems/Terrain/Code/Include/Terrain/Ebuses/TerrainGradientSurfaceListBus.h b/Gems/Terrain/Code/Include/Terrain/Ebuses/TerrainGradientSurfaceListBus.h new file mode 100644 index 0000000000..5734a6e023 --- /dev/null +++ b/Gems/Terrain/Code/Include/Terrain/Ebuses/TerrainGradientSurfaceListBus.h @@ -0,0 +1,34 @@ +/* + * 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 + +#include + +namespace Terrain +{ + + //! This bus provides retrieval of information from Terrain Surfaces. + class TerrainAreaSurfaceRequests + : public AZ::ComponentBus + { + public: + //////////////////////////////////////////////////////////////////////// + // EBusTraits + using MutexType = AZStd::recursive_mutex; + //////////////////////////////////////////////////////////////////////// + + virtual ~TerrainAreaSurfaceRequests() = default; + + virtual void GetSurfaceWeights(const AZ::Vector3& inPosition, SurfaceData::SurfaceTagWeightMap& surfaceWeights) const = 0; + }; + + using TerrainAreaSurfaceRequestBus = AZ::EBus; +} // namespace Terrain diff --git a/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp new file mode 100644 index 0000000000..2fbd6a4814 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.cpp @@ -0,0 +1,157 @@ +/* + * 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 + +#include +#include + +#include + +namespace Terrain +{ + void TerrainSurfaceGradientMapping::Reflect(AZ::ReflectContext* context) + { + if (auto serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(1) + ->Field("Gradient Entity", &TerrainSurfaceGradientMapping::m_gradientEntityId) + ->Field("Surface Tag", &TerrainSurfaceGradientMapping::m_surfaceTag) + ; + + if (auto edit = serialize->GetEditContext()) + { + edit->Class("Terrain Surface Gradient Mapping", "Mapping between a gradient and a surface.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + + ->DataElement( + AZ::Edit::UIHandlers::Default, &TerrainSurfaceGradientMapping::m_gradientEntityId, "Gradient Entity", "ID of Entity providing a gradient.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) + ->UIElement("GradientPreviewer", "Previewer") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "") + ->Attribute(AZ_CRC_CE("GradientEntity"), &TerrainSurfaceGradientMapping::m_gradientEntityId) + ->DataElement( + AZ::Edit::UIHandlers::Default, &TerrainSurfaceGradientMapping::m_surfaceTag, "Surface Tag", + "Surface type to map to this gradient.") + ; + } + } + } + + void TerrainSurfaceGradientListConfig::Reflect(AZ::ReflectContext* context) + { + TerrainSurfaceGradientMapping::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("Mappings", &TerrainSurfaceGradientListConfig::m_gradientSurfaceMappings) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class( + "Terrain Surface Gradient List Component", "Provide mapping between gradients and surfaces.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + + ->DataElement( + AZ::Edit::UIHandlers::Default, &TerrainSurfaceGradientListConfig::m_gradientSurfaceMappings, "Gradient to Surface Mappings", "Maps Gradient Entities to Surfaces.") + ; + } + } + } + + void TerrainSurfaceGradientListComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainSurfaceProviderService")); + } + + void TerrainSurfaceGradientListComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainSurfaceProviderService")); + } + + void TerrainSurfaceGradientListComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC("TerrainAreaService")); + } + + void TerrainSurfaceGradientListComponent::Reflect(AZ::ReflectContext* context) + { + TerrainSurfaceGradientListConfig::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(0)->Field("Configuration", &TerrainSurfaceGradientListComponent::m_configuration) + ; + } + } + + TerrainSurfaceGradientListComponent::TerrainSurfaceGradientListComponent(const TerrainSurfaceGradientListConfig& configuration) + : m_configuration(configuration) + { + } + + void TerrainSurfaceGradientListComponent::Activate() + { + Terrain::TerrainAreaSurfaceRequestBus::Handler::BusConnect(GetEntityId()); + } + + void TerrainSurfaceGradientListComponent::Deactivate() + { + Terrain::TerrainAreaSurfaceRequestBus::Handler::BusDisconnect(); + } + + bool TerrainSurfaceGradientListComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) + { + if (auto config = azrtti_cast(baseConfig)) + { + m_configuration = *config; + return true; + } + return false; + } + + bool TerrainSurfaceGradientListComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + { + if (auto config = azrtti_cast(outBaseConfig)) + { + *config = m_configuration; + return true; + } + return false; + } + + void TerrainSurfaceGradientListComponent::GetSurfaceWeights(const AZ::Vector3& inPosition, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights) const + { + outSurfaceWeights.clear(); + + const GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f)); + + for (const auto& mapping : m_configuration.m_gradientSurfaceMappings) + { + float weight = 0.0f; + GradientSignal::GradientRequestBus::EventResult(weight, mapping.m_gradientEntityId, &GradientSignal::GradientRequestBus::Events::GetValue, params); + + AzFramework::SurfaceData::SurfaceTagWeight tagWeight; + tagWeight.m_surfaceType = mapping.m_surfaceTag; + tagWeight.m_weight = weight; + outSurfaceWeights.emplace(tagWeight); + } + } +} // namespace Terrain diff --git a/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.h b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.h new file mode 100644 index 0000000000..799038354c --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainSurfaceGradientListComponent.h @@ -0,0 +1,77 @@ +/* + * 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 +#include +#include +#include + +#include + +namespace LmbrCentral +{ + template + class EditorWrappedComponentBase; +} + +namespace Terrain +{ + class TerrainSurfaceGradientMapping final + { + public: + AZ_CLASS_ALLOCATOR(TerrainSurfaceGradientMapping, AZ::SystemAllocator, 0); + AZ_RTTI(TerrainSurfaceGradientMapping, "{473AD2CE-F22A-45A9-803F-2192F3D9F2BF}"); + static void Reflect(AZ::ReflectContext* context); + + AZ::EntityId m_gradientEntityId; + SurfaceData::SurfaceTag m_surfaceTag; + }; + + class TerrainSurfaceGradientListConfig : public AZ::ComponentConfig + { + public: + AZ_CLASS_ALLOCATOR(TerrainSurfaceGradientListConfig, AZ::SystemAllocator, 0); + AZ_RTTI(TerrainSurfaceGradientListConfig, "{E9B083AD-8D30-47DA-8F8E-AA089BFA35E9}", AZ::ComponentConfig); + static void Reflect(AZ::ReflectContext* context); + + AZStd::vector m_gradientSurfaceMappings; + }; + + class TerrainSurfaceGradientListComponent + : public AZ::Component + , public Terrain::TerrainAreaSurfaceRequestBus::Handler + { + public: + template + friend class LmbrCentral::EditorWrappedComponentBase; + AZ_COMPONENT(TerrainSurfaceGradientListComponent, "{51F97C95-6B8A-4B06-B394-BD25BFCC8B7E}"); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void Reflect(AZ::ReflectContext* context); + + TerrainSurfaceGradientListComponent(const TerrainSurfaceGradientListConfig& configuration); + TerrainSurfaceGradientListComponent() = default; + ~TerrainSurfaceGradientListComponent() = default; + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override; + bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; + + // TerrainAreaSurfaceRequestBus + void GetSurfaceWeights(const AZ::Vector3& inPosition, AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights) const override; + + private: + TerrainSurfaceGradientListConfig m_configuration; + }; +} // namespace Terrain diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.cpp b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.cpp new file mode 100644 index 0000000000..cf2c44225a --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.cpp @@ -0,0 +1,22 @@ +/* + * 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 +#include +#include + +namespace Terrain +{ + void EditorTerrainSurfaceGradientListComponent::Reflect(AZ::ReflectContext* context) + { + BaseClassType::ReflectSubClass(context, 1, + &LmbrCentral::EditorWrappedComponentBaseVersionConverter + ); + } +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.h b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.h new file mode 100644 index 0000000000..e2c5f1b280 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.h @@ -0,0 +1,32 @@ +/* + * 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 +#include +#include + +namespace Terrain +{ + class EditorTerrainSurfaceGradientListComponent + : public LmbrCentral::EditorWrappedComponentBase + { + public: + using BaseClassType = LmbrCentral::EditorWrappedComponentBase; + AZ_EDITOR_COMPONENT(EditorTerrainSurfaceGradientListComponent, "{49831E91-A11F-4EFF-A824-6D85C284B934}", BaseClassType); + static void Reflect(AZ::ReflectContext* context); + + static constexpr const char* const s_categoryName = "Terrain"; + static constexpr const char* const s_componentName = "Terrain Surface Gradient List"; + static constexpr const char* const s_componentDescription = "Provides a mapping between gradients and surface tags for use by the terrain system."; + static constexpr const char* const s_icon = "Editor/Icons/Components/TerrainLayerSpawner.svg"; + static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/TerrainLayerSpawner.svg"; + static constexpr const char* const s_helpUrl = ""; + }; +} diff --git a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp index 5305087694..9107ea8541 100644 --- a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp +++ b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ namespace Terrain { Terrain::EditorTerrainHeightGradientListComponent::CreateDescriptor(), Terrain::EditorTerrainLayerSpawnerComponent::CreateDescriptor(), + Terrain::EditorTerrainSurfaceGradientListComponent::CreateDescriptor(), Terrain::EditorTerrainSystemComponent::CreateDescriptor(), Terrain::EditorTerrainWorldComponent::CreateDescriptor(), Terrain::EditorTerrainWorldDebuggerComponent::CreateDescriptor(), diff --git a/Gems/Terrain/Code/Source/TerrainModule.cpp b/Gems/Terrain/Code/Source/TerrainModule.cpp index 24b87b4ab3..6aeddc3cd5 100644 --- a/Gems/Terrain/Code/Source/TerrainModule.cpp +++ b/Gems/Terrain/Code/Source/TerrainModule.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include namespace Terrain @@ -30,6 +31,7 @@ namespace Terrain TerrainWorldRendererComponent::CreateDescriptor(), TerrainHeightGradientListComponent::CreateDescriptor(), TerrainLayerSpawnerComponent::CreateDescriptor(), + TerrainSurfaceGradientListComponent::CreateDescriptor(), TerrainSurfaceDataSystemComponent::CreateDescriptor(), }); } diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp index 46ac677dd1..821e0bd2e2 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp @@ -8,10 +8,17 @@ #include #include +#include #include #include #include +#include +#include +#include + +#include + using namespace Terrain; bool TerrainLayerPriorityComparator::operator()(const AZ::EntityId& layer1id, const AZ::EntityId& layer2id) const @@ -201,6 +208,7 @@ float TerrainSystem::GetHeightSynchronous(float x, float y, Sampler sampler, boo break; } + // For now, always set terrainExists to true, as we don't have a way to author data for terrain holes yet. if (terrainExistsPtr) { *terrainExistsPtr = terrainExists; @@ -291,33 +299,134 @@ AZ::Vector3 TerrainSystem::GetNormalFromFloats(float x, float y, Sampler sampler AzFramework::SurfaceData::SurfaceTagWeight TerrainSystem::GetMaxSurfaceWeight( - [[maybe_unused]] AZ::Vector3 position, [[maybe_unused]] Sampler sampleFilter, [[maybe_unused]] bool* terrainExistsPtr) const + const AZ::Vector3 position, Sampler sampleFilter, bool* terrainExistsPtr) const { - if (terrainExistsPtr) - { - *terrainExistsPtr = true; - } + return GetMaxSurfaceWeightFromFloats(position.GetX(), position.GetY(), sampleFilter, terrainExistsPtr); +} - return AzFramework::SurfaceData::SurfaceTagWeight(); +AzFramework::SurfaceData::SurfaceTagWeight TerrainSystem::GetMaxSurfaceWeightFromVector2(const AZ::Vector2& inPosition, Sampler sampleFilter, bool* terrainExistsPtr) const +{ + return GetMaxSurfaceWeightFromFloats(inPosition.GetX(), inPosition.GetY(), sampleFilter, terrainExistsPtr); } AzFramework::SurfaceData::SurfaceTagWeight TerrainSystem::GetMaxSurfaceWeightFromFloats( - [[maybe_unused]] float x, - [[maybe_unused]] float y, - [[maybe_unused]] Sampler sampleFilter, - [[maybe_unused]] bool* terrainExistsPtr) const + const float x, const float y, Sampler sampleFilter, bool* terrainExistsPtr) const { if (terrainExistsPtr) { *terrainExistsPtr = true; } - return AzFramework::SurfaceData::SurfaceTagWeight(); + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet weightSet; + + GetOrderedSurfaceWeights(x, y, sampleFilter, weightSet, terrainExistsPtr); + + if (weightSet.empty()) + { + return {}; + } + + return *weightSet.begin(); } -const char* TerrainSystem::GetMaxSurfaceName( - [[maybe_unused]] AZ::Vector3 position, [[maybe_unused]] Sampler sampleFilter, [[maybe_unused]] bool* terrainExistsPtr) const +AZ::EntityId TerrainSystem::FindBestAreaEntityAtPosition(float x, float y, AZ::Aabb& bounds) const { + AZ::Vector3 inPosition = AZ::Vector3(x, y, 0); + + // Find the highest priority layer that encompasses this position + AZStd::shared_lock lock(m_areaMutex); + + // The areas are sorted into priority order: the first area that contains inPosition is the most suitable. + for (const auto& [areaId, areaBounds] : m_registeredAreas) + { + inPosition.SetZ(areaBounds.GetMin().GetZ()); + if (areaBounds.Contains(inPosition)) + { + bounds = areaBounds; + return areaId; + } + } + + return AZ::EntityId(); +} + +void TerrainSystem::GetOrderedSurfaceWeights( + const float x, + const float y, + [[maybe_unused]] Sampler sampler, + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + bool* terrainExistsPtr) const +{ + AZ::Aabb bounds; + AZ::EntityId bestAreaId = FindBestAreaEntityAtPosition(x, y, bounds); + + if (terrainExistsPtr) + { + GetHeightFromFloats(x, y, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, terrainExistsPtr); + } + + outSurfaceWeights.clear(); + + if (!bestAreaId.IsValid()) + { + return; + } + + const AZ::Vector3 inPosition = AZ::Vector3(x, y, 0.0f); + + // Get all the surfaces with weights at the given point. + Terrain::TerrainAreaSurfaceRequestBus::Event( + bestAreaId, &Terrain::TerrainAreaSurfaceRequestBus::Events::GetSurfaceWeights, inPosition, outSurfaceWeights); +} + +void TerrainSystem::GetSurfaceWeights( + const AZ::Vector3& inPosition, + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + Sampler sampleFilter, + bool* terrainExistsPtr) const +{ + if (terrainExistsPtr) + { + *terrainExistsPtr = true; + } + + GetOrderedSurfaceWeights(inPosition.GetX(), inPosition.GetY(), sampleFilter, outSurfaceWeights, terrainExistsPtr); +} + +void TerrainSystem::GetSurfaceWeightsFromVector2( + const AZ::Vector2& inPosition, + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + Sampler sampleFilter, + bool* terrainExistsPtr) const +{ + // For now, always set terrainExists to true, as we don't have a way to author data for terrain holes yet. + if (terrainExistsPtr) + { + *terrainExistsPtr = true; + } + + GetOrderedSurfaceWeights(inPosition.GetX(), inPosition.GetY(), sampleFilter, outSurfaceWeights, terrainExistsPtr); +} + +void TerrainSystem::GetSurfaceWeightsFromFloats( + float x, + float y, + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + Sampler sampleFilter, + bool* terrainExistsPtr) const +{ + // For now, always set terrainExists to true, as we don't have a way to author data for terrain holes yet. + if (terrainExistsPtr) + { + *terrainExistsPtr = true; + } + + GetOrderedSurfaceWeights(x, y, sampleFilter, outSurfaceWeights, terrainExistsPtr); +} + +const char* TerrainSystem::GetMaxSurfaceName([[maybe_unused]] AZ::Vector3 position, [[maybe_unused]] Sampler sampleFilter, [[maybe_unused]] bool* terrainExistsPtr) const +{ + // For now, always set terrainExists to true, as we don't have a way to author data for terrain holes yet. if (terrainExistsPtr) { *terrainExistsPtr = true; @@ -327,14 +436,6 @@ const char* TerrainSystem::GetMaxSurfaceName( } /* -void TerrainSystem::GetSurfaceWeights( - [[maybe_unused]] const AZ::Vector3& inPosition, - [[maybe_unused]] Sampler sampleFilter, - [[maybe_unused]] SurfaceData::SurfaceTagWeightMap& outSurfaceWeights) -{ - // TODO: implement -} - void TerrainSystem::GetSurfacePoint( const AZ::Vector3& inPosition, [[maybe_unused]] Sampler sampleFilter, SurfaceData::SurfacePoint& outSurfacePoint) { diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h index a9240e02a6..66c48850ef 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h @@ -70,9 +70,28 @@ namespace Terrain //! HOLE then *terrainExistsPtr will be set to false, //! otherwise *terrainExistsPtr will be set to true. AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeight( - AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + const AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromVector2( + const AZ::Vector2& inPosition, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const override; AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromFloats( - float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + const float x, const float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + + void GetSurfaceWeights( + const AZ::Vector3& inPosition, + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + Sampler sampleFilter = Sampler::DEFAULT, + bool* terrainExistsPtr = nullptr) const override; + void GetSurfaceWeightsFromVector2( + const AZ::Vector2& inPosition, + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + Sampler sampleFilter = Sampler::DEFAULT, + bool* terrainExistsPtr = nullptr) const override; + void GetSurfaceWeightsFromFloats( + float x, + float y, + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + Sampler sampleFilter = Sampler::DEFAULT, + bool* terrainExistsPtr = nullptr) const override; //! Convenience function for low level systems that can't do a reverse lookup from Crc to string. Everyone else should use //! GetMaxSurfaceWeight or GetMaxSurfaceWeightFromFloats. Not available in the behavior context. Returns nullptr if the position is @@ -96,6 +115,13 @@ namespace Terrain private: void ClampPosition(float x, float y, AZ::Vector2& outPosition, AZ::Vector2& normalizedDelta) const; + AZ::EntityId FindBestAreaEntityAtPosition(float x, float y, AZ::Aabb& bounds) const; + void GetOrderedSurfaceWeights( + const float x, + const float y, + Sampler sampler, + AzFramework::SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights, + bool* terrainExistsPtr) const; float GetHeightSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const; float GetTerrainAreaHeight(float x, float y, bool& terrainExists) const; AZ::Vector3 GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const; diff --git a/Gems/Terrain/Code/terrain_editor_shared_files.cmake b/Gems/Terrain/Code/terrain_editor_shared_files.cmake index d4719f46d5..efb68eca31 100644 --- a/Gems/Terrain/Code/terrain_editor_shared_files.cmake +++ b/Gems/Terrain/Code/terrain_editor_shared_files.cmake @@ -11,6 +11,8 @@ set(FILES Source/EditorComponents/EditorTerrainHeightGradientListComponent.h Source/EditorComponents/EditorTerrainLayerSpawnerComponent.cpp Source/EditorComponents/EditorTerrainLayerSpawnerComponent.h + Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.cpp + Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.h Source/EditorComponents/EditorTerrainWorldComponent.cpp Source/EditorComponents/EditorTerrainWorldComponent.h Source/EditorComponents/EditorTerrainWorldDebuggerComponent.cpp diff --git a/Gems/Terrain/Code/terrain_files.cmake b/Gems/Terrain/Code/terrain_files.cmake index 2c013fa332..6d190b7d5e 100644 --- a/Gems/Terrain/Code/terrain_files.cmake +++ b/Gems/Terrain/Code/terrain_files.cmake @@ -7,12 +7,15 @@ # set(FILES + Include/Terrain/Ebuses/TerrainAreaSurfaceRequestBus.h Source/Components/TerrainHeightGradientListComponent.cpp Source/Components/TerrainHeightGradientListComponent.h Source/Components/TerrainLayerSpawnerComponent.cpp Source/Components/TerrainLayerSpawnerComponent.h Source/Components/TerrainSurfaceDataSystemComponent.cpp Source/Components/TerrainSurfaceDataSystemComponent.h + Source/Components/TerrainSurfaceGradientListComponent.cpp + Source/Components/TerrainSurfaceGradientListComponent.h Source/Components/TerrainSystemComponent.cpp Source/Components/TerrainSystemComponent.h Source/Components/TerrainWorldComponent.cpp