diff --git a/Gems/Terrain/Code/CMakeLists.txt b/Gems/Terrain/Code/CMakeLists.txt index 17ac3d8474..442093a885 100644 --- a/Gems/Terrain/Code/CMakeLists.txt +++ b/Gems/Terrain/Code/CMakeLists.txt @@ -22,9 +22,12 @@ ly_add_target( Gem::Atom_RPI.Public Gem::Atom_Utils.Static Gem::Atom_Feature_Common + Gem::AtomLyIntegration_CommonFeatures.Static Gem::GradientSignal Gem::SurfaceData Gem::LmbrCentral + + ) ly_add_target( diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.cpp b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.cpp index cf2c44225a..bc823734b4 100644 --- a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.cpp +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainSurfaceGradientListComponent.cpp @@ -14,7 +14,8 @@ namespace Terrain { void EditorTerrainSurfaceGradientListComponent::Reflect(AZ::ReflectContext* context) { - BaseClassType::ReflectSubClass(context, 1, + BaseClassType::ReflectSubClass( + context, 1, &LmbrCentral::EditorWrappedComponentBaseVersionConverter ); diff --git a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp index 94c6f5c3c6..6fd8979e4a 100644 --- a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp +++ b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace Terrain @@ -28,6 +29,7 @@ namespace Terrain Terrain::EditorTerrainMacroMaterialComponent::CreateDescriptor(), Terrain::EditorTerrainSurfaceGradientListComponent::CreateDescriptor(), Terrain::EditorTerrainSystemComponent::CreateDescriptor(), + Terrain::EditorTerrainSurfaceMaterialsListComponent::CreateDescriptor(), Terrain::EditorTerrainWorldComponent::CreateDescriptor(), Terrain::EditorTerrainWorldDebuggerComponent::CreateDescriptor(), Terrain::EditorTerrainWorldRendererComponent::CreateDescriptor(), diff --git a/Gems/Terrain/Code/Source/TerrainModule.cpp b/Gems/Terrain/Code/Source/TerrainModule.cpp index 878c6b44c8..c3a7890565 100644 --- a/Gems/Terrain/Code/Source/TerrainModule.cpp +++ b/Gems/Terrain/Code/Source/TerrainModule.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include namespace Terrain @@ -26,6 +27,7 @@ namespace Terrain : AZ::Module() { m_descriptors.insert(m_descriptors.end(), { + TerrainSurfaceMaterialsListComponent::CreateDescriptor(), TerrainSystemComponent::CreateDescriptor(), TerrainWorldComponent::CreateDescriptor(), TerrainWorldDebuggerComponent::CreateDescriptor(), diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp new file mode 100644 index 0000000000..271919070c --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp @@ -0,0 +1,305 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +namespace Terrain +{ + void TerrainSurfaceMaterialMapping::Reflect(AZ::ReflectContext* context) + { + if (auto serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(1) + ->Field("Surface", &TerrainSurfaceMaterialMapping::m_surfaceTag) + ->Field("MaterialAsset", &TerrainSurfaceMaterialMapping::m_materialAsset); + + if (auto edit = serialize->GetEditContext()) + { + edit->Class("Terrain Surface Gradient Mapping", "Mapping between a surface and a material.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show) + + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &TerrainSurfaceMaterialMapping::m_surfaceTag, "Surface Tag", "Surface type to map to a material.") + ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainSurfaceMaterialMapping::m_materialAsset, "Material Asset", "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::ShowProductAssetFileName, true) + ; + } + } + } + + void TerrainSurfaceMaterialsListConfig::Reflect(AZ::ReflectContext* context) + { + TerrainSurfaceMaterialMapping::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("Mappings", &TerrainSurfaceMaterialsListConfig::m_surfaceMaterials); + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class( + "Terrain Surface Material List Component", "Provide mapping between surfaces and render materials.") + ->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, &TerrainSurfaceMaterialsListConfig::m_surfaceMaterials, + "Gradient to Material Mappings", "Maps surfaces to materials."); + } + } + } + + void TerrainSurfaceMaterialsListComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainMaterialProviderService")); + } + + void TerrainSurfaceMaterialsListComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainMaterialProviderService")); + } + + void TerrainSurfaceMaterialsListComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); + } + + void TerrainSurfaceMaterialsListComponent::Reflect(AZ::ReflectContext* context) + { + TerrainSurfaceMaterialsListConfig::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(0) + ->Field("Configuration", &TerrainSurfaceMaterialsListComponent::m_configuration); + } + } + + TerrainSurfaceMaterialsListComponent::TerrainSurfaceMaterialsListComponent(const TerrainSurfaceMaterialsListConfig& configuration) + : m_configuration(configuration) + { + } + + void TerrainSurfaceMaterialsListComponent::Activate() + { + m_cachedAabb = AZ::Aabb::CreateNull(); + + // Set all the materials as inactive and start loading. + for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials) + { + if (surfaceMaterialMapping.m_materialAsset.GetId().IsValid()) + { + surfaceMaterialMapping.m_active = false; + surfaceMaterialMapping.m_materialAsset.QueueLoad(); + AZ::Data::AssetBus::Handler::BusConnect(surfaceMaterialMapping.m_materialAsset.GetId()); + } + } + } + + void TerrainSurfaceMaterialsListComponent::Deactivate() + { + TerrainAreaMaterialRequestBus::Handler::BusDisconnect(); + + for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials) + { + if (surfaceMaterialMapping.m_materialAsset.GetId().IsValid()) + { + AZ::Data::AssetBus::Handler::BusDisconnect(surfaceMaterialMapping.m_materialAsset.GetId()); + surfaceMaterialMapping.m_materialAsset.Release(); + surfaceMaterialMapping.m_materialInstance.reset(); + surfaceMaterialMapping.m_activeMaterialAssetId = AZ::Data::AssetId(); + } + } + + HandleMaterialStateChanges(); + } + + int TerrainSurfaceMaterialsListComponent::CountMaterialIDInstances(AZ::Data::AssetId id) const + { + int count = 0; + + for (const auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials) + { + if (surfaceMaterialMapping.m_activeMaterialAssetId == id) + { + count++; + } + } + + return count; + } + + void TerrainSurfaceMaterialsListComponent::HandleMaterialStateChanges() + { + bool anyMaterialIsActive = false; + bool anyMaterialWasAlreadyActive = false; + + for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials) + { + const bool wasPreviouslyActive = surfaceMaterialMapping.m_active; + const bool isNowActive = (surfaceMaterialMapping.m_materialInstance != nullptr); + + if (wasPreviouslyActive) + { + anyMaterialWasAlreadyActive = true; + } + + if (isNowActive) + { + anyMaterialIsActive = true; + } + + surfaceMaterialMapping.m_active = isNowActive; + + if (!wasPreviouslyActive && !isNowActive) + { + // A material has been assigned but has not yet completed loading. + } + else if (!wasPreviouslyActive && isNowActive) + { + // Remember the asset id so we can disconnect from the AssetBus if the material asset is removed. + surfaceMaterialMapping.m_activeMaterialAssetId = surfaceMaterialMapping.m_materialAsset.GetId(); + + TerrainAreaMaterialNotificationBus::Broadcast( + &TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingCreated, GetEntityId(), + surfaceMaterialMapping.m_surfaceTag, + surfaceMaterialMapping.m_materialInstance); + } + else if (wasPreviouslyActive && !isNowActive) + { + // Don't disconnect from the AssetBus if this material is mapped more than once. + if (CountMaterialIDInstances(surfaceMaterialMapping.m_activeMaterialAssetId) == 1) + { + AZ::Data::AssetBus::Handler::BusDisconnect(surfaceMaterialMapping.m_activeMaterialAssetId); + } + + surfaceMaterialMapping.m_activeMaterialAssetId = AZ::Data::AssetId(); + + TerrainAreaMaterialNotificationBus::Broadcast( + &TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingDestroyed, GetEntityId(), + surfaceMaterialMapping.m_surfaceTag); + } + else + { + TerrainAreaMaterialNotificationBus::Broadcast( + &TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingChanged, GetEntityId(), + surfaceMaterialMapping.m_surfaceTag, + surfaceMaterialMapping.m_materialInstance); + } + } + + if (!anyMaterialWasAlreadyActive && anyMaterialIsActive) + { + // Cache the current shape bounds. + LmbrCentral::ShapeComponentRequestsBus::EventResult( + m_cachedAabb, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); + + // Start listening for data requests. + TerrainAreaMaterialRequestBus::Handler::BusConnect(GetEntityId()); + + // Start listening for shape changes. + LmbrCentral::ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId()); + + } + else if (anyMaterialWasAlreadyActive && !anyMaterialIsActive) + { + // All materials have been deactivated, stop listening for requests and notifications. + m_cachedAabb = AZ::Aabb::CreateNull(); + LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect(); + TerrainAreaMaterialRequestBus::Handler::BusConnect(GetEntityId()); + } + } + + bool TerrainSurfaceMaterialsListComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) + { + if (auto config = azrtti_cast(baseConfig)) + { + m_configuration = *config; + return true; + } + return false; + } + + bool TerrainSurfaceMaterialsListComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + { + if (auto config = azrtti_cast(outBaseConfig)) + { + *config = m_configuration; + return true; + } + return false; + } + + void TerrainSurfaceMaterialsListComponent::OnShapeChanged([[maybe_unused]] ShapeComponentNotifications::ShapeChangeReasons reasons) + { + AZ::Aabb oldAabb = m_cachedAabb; + + LmbrCentral::ShapeComponentRequestsBus::EventResult( + m_cachedAabb, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); + + TerrainAreaMaterialNotificationBus::Broadcast( + &TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingRegionChanged, GetEntityId(), oldAabb, + m_cachedAabb); + } + + const AZStd::vector& TerrainSurfaceMaterialsListComponent::GetSurfaceMaterialMappings( + AZ::Aabb& region) const + { + region = m_cachedAabb; + + return m_configuration.m_surfaceMaterials; + } + + void TerrainSurfaceMaterialsListComponent::OnAssetReady(AZ::Data::Asset asset) + { + // Find the missing material instance with the correct id. + for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials) + { + if (surfaceMaterialMapping.m_materialAsset.GetId() == asset.GetId() && + (!surfaceMaterialMapping.m_materialInstance || + surfaceMaterialMapping.m_materialInstance->GetAssetId() != surfaceMaterialMapping.m_materialAsset.GetId())) + { + surfaceMaterialMapping.m_materialInstance = AZ::RPI::Material::FindOrCreate(surfaceMaterialMapping.m_materialAsset); + surfaceMaterialMapping.m_materialAsset.Release(); + } + } + HandleMaterialStateChanges(); + } + + void TerrainSurfaceMaterialsListComponent::OnAssetReloaded(AZ::Data::Asset asset) + { + OnAssetReady(asset); + } +} // namespace Terrain diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h new file mode 100644 index 0000000000..0c477330b1 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h @@ -0,0 +1,101 @@ +/* + * 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 +#include +#include +#include + + +namespace LmbrCentral +{ + template + class EditorWrappedComponentBase; +} + +namespace Terrain +{ + struct TerrainSurfaceMaterialMapping final + { + public: + AZ_CLASS_ALLOCATOR(TerrainSurfaceMaterialMapping, AZ::SystemAllocator, 0); + AZ_RTTI(TerrainSurfaceMaterialMapping, "{37D2A586-CDDD-4FB7-A7D6-0B4CC575AB8C}"); + static void Reflect(AZ::ReflectContext* context); + + SurfaceData::SurfaceTag m_surfaceTag; + AZ::Data::AssetId m_activeMaterialAssetId; + AZ::Data::Asset m_materialAsset; + AZ::Data::Instance m_materialInstance; + + bool m_active = false; + }; + + class TerrainSurfaceMaterialsListConfig : public AZ::ComponentConfig + { + public: + AZ_CLASS_ALLOCATOR(TerrainSurfaceMaterialsListConfig, AZ::SystemAllocator, 0); + AZ_RTTI(TerrainSurfaceMaterialsListConfig, "{68A1CB1B-C835-4C3A-8D1C-08692E07711A}", AZ::ComponentConfig); + static void Reflect(AZ::ReflectContext* context); + + AZStd::vector m_surfaceMaterials; + }; + + class TerrainSurfaceMaterialsListComponent + : public AZ::Component + , private TerrainAreaMaterialRequestBus::Handler + , private AZ::Data::AssetBus::Handler + , private LmbrCentral::ShapeComponentNotificationsBus::Handler + { + public: + template + friend class LmbrCentral::EditorWrappedComponentBase; + AZ_COMPONENT(TerrainSurfaceMaterialsListComponent, "{93CF3938-FBC3-4E55-B825-27BA94A5CD35}"); + 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); + + TerrainSurfaceMaterialsListComponent(const TerrainSurfaceMaterialsListConfig& configuration); + TerrainSurfaceMaterialsListComponent() = default; + ~TerrainSurfaceMaterialsListComponent() = 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; + + private: + void HandleMaterialStateChanges(); + int CountMaterialIDInstances(AZ::Data::AssetId id) const; + + //////////////////////////////////////////////////////////////////////// + // ShapeComponentNotificationsBus + void OnShapeChanged(ShapeComponentNotifications::ShapeChangeReasons reasons) override; + + ////////////////////////////////////////////////////////////////////////// + // TerrainAreaMaterialRequestBus + const AZStd::vector& GetSurfaceMaterialMappings(AZ::Aabb& region) const override; + + ////////////////////////////////////////////////////////////////////////// + // AZ::Data::AssetBus::Handler + void OnAssetReady(AZ::Data::Asset asset) override; + void OnAssetReloaded(AZ::Data::Asset asset) override; + + TerrainSurfaceMaterialsListConfig m_configuration; + + AZ::Aabb m_cachedAabb; + }; +} // namespace Terrain diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainSurfaceMaterialsListComponent.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainSurfaceMaterialsListComponent.cpp new file mode 100644 index 0000000000..ee02c83578 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainSurfaceMaterialsListComponent.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 EditorTerrainSurfaceMaterialsListComponent::Reflect(AZ::ReflectContext* context) + { + BaseClassType::ReflectSubClass(context, 1, + &LmbrCentral::EditorWrappedComponentBaseVersionConverter + ); + } +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainSurfaceMaterialsListComponent.h b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainSurfaceMaterialsListComponent.h new file mode 100644 index 0000000000..f0d03a6082 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainSurfaceMaterialsListComponent.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 EditorTerrainSurfaceMaterialsListComponent + : public LmbrCentral::EditorWrappedComponentBase + { + public: + using BaseClassType = LmbrCentral::EditorWrappedComponentBase; + AZ_EDITOR_COMPONENT(EditorTerrainSurfaceMaterialsListComponent, "{335CDED5-2E76-4342-8675-A60F66C471BF}", BaseClassType); + static void Reflect(AZ::ReflectContext* context); + + static constexpr const char* const s_categoryName = "Terrain"; + static constexpr const char* const s_componentName = "Terrain Surface Materials List"; + static constexpr const char* const s_componentDescription = "Provides a mapping between surface tags and render materials."; + static constexpr const char* const s_icon = "Editor/Icons/Components/TerrainHeight.svg"; + static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/TerrainHeight.svg"; + static constexpr const char* const s_helpUrl = ""; + }; +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainAreaMaterialRequestBus.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainAreaMaterialRequestBus.h new file mode 100644 index 0000000000..dfedf0b34a --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainAreaMaterialRequestBus.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 + +namespace Terrain +{ + //! This bus provides retrieval of information from Terrain Surfaces. + class TerrainAreaMaterialRequests + : public AZ::ComponentBus + { + public: + //////////////////////////////////////////////////////////////////////// + // EBusTraits + using MutexType = AZStd::recursive_mutex; + //////////////////////////////////////////////////////////////////////// + + virtual ~TerrainAreaMaterialRequests() = default; + + //! Get the Material asset assigned to a particular surface tag. + virtual const AZStd::vector& GetSurfaceMaterialMappings(AZ::Aabb& region) const = 0; + }; + + using TerrainAreaMaterialRequestBus = AZ::EBus; + + //! Notifications for when the surface -> material mapping changes.. + class TerrainAreaMaterialNotifications : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + + virtual void OnTerrainSurfaceMaterialMappingCreated( + [[maybe_unused]] AZ::EntityId entityId, + [[maybe_unused]] SurfaceData::SurfaceTag surface, + [[maybe_unused]] AZ::Data::Instance material) + { + } + + virtual void OnTerrainSurfaceMaterialMappingDestroyed( + [[maybe_unused]] AZ::EntityId entityId, + [[maybe_unused]] SurfaceData::SurfaceTag surface) + { + } + + virtual void OnTerrainSurfaceMaterialMappingChanged( + [[maybe_unused]] AZ::EntityId entityId, + [[maybe_unused]] SurfaceData::SurfaceTag surface, + [[maybe_unused]] AZ::Data::Instance material) + { + } + + virtual void OnTerrainSurfaceMaterialMappingRegionChanged( + [[maybe_unused]] AZ::EntityId entityId, + [[maybe_unused]] const AZ::Aabb& oldRegion, + [[maybe_unused]] const AZ::Aabb& newRegion) + { + } + }; + using TerrainAreaMaterialNotificationBus = AZ::EBus; +} // namespace Terrain diff --git a/Gems/Terrain/Code/Tests/SurfaceMaterialsListTest.cpp b/Gems/Terrain/Code/Tests/SurfaceMaterialsListTest.cpp new file mode 100644 index 0000000000..686ca349f5 --- /dev/null +++ b/Gems/Terrain/Code/Tests/SurfaceMaterialsListTest.cpp @@ -0,0 +1,90 @@ +/* + * 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 + +using ::testing::NiceMock; +using ::testing::AtLeast; +using ::testing::_; + +namespace UnitTest +{ + class TerrainSurfaceMaterialsListTest : public ::testing::Test + { + protected: + AZ::ComponentApplication m_app; + + void SetUp() override + { + AZ::ComponentApplication::Descriptor appDesc; + appDesc.m_memoryBlocksByteSize = 20 * 1024 * 1024; + appDesc.m_recordingMode = AZ::Debug::AllocationRecords::RECORD_NO_RECORDS; + appDesc.m_stackRecordLevels = 20; + + m_app.Create(appDesc); + } + + AZStd::unique_ptr CreateEntity() + { + auto entity = AZStd::make_unique(); + entity->Init(); + return entity; + } + + AZStd::unique_ptr CreateEntityWithShapeComponents() + { + auto entity = CreateEntity(); + + auto shapeComponent = entity->CreateComponent(); + m_app.RegisterComponentDescriptor(shapeComponent->CreateDescriptor()); + + return entity; + } + + Terrain::TerrainSurfaceMaterialsListComponent* AddSurfaceMaterialListComponent(AZ::Entity* entity) + { + auto surfaceMaterialsListComponent = entity->CreateComponent(); + m_app.RegisterComponentDescriptor(surfaceMaterialsListComponent->CreateDescriptor()); + + return surfaceMaterialsListComponent; + } + + void TearDown() override + { + m_app.Destroy(); + } + }; + + TEST_F(TerrainSurfaceMaterialsListTest, SurfaceGradientListRequiresShapeToActivate) + { + auto entity = CreateEntity(); + + AddSurfaceMaterialListComponent(entity.get()); + + const AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails(); + EXPECT_FALSE(sortOutcome.IsSuccess()); + + entity.reset(); + } + + TEST_F(TerrainSurfaceMaterialsListTest, SurfaceGradientListActivatesSuccessfully) + { + auto entity = CreateEntityWithShapeComponents(); + + AddSurfaceMaterialListComponent(entity.get()); + + entity->Activate(); + + EXPECT_EQ(entity->GetState(), AZ::Entity::State::Active); + + entity.reset(); + } +} // namespace UnitTest diff --git a/Gems/Terrain/Code/terrain_editor_shared_files.cmake b/Gems/Terrain/Code/terrain_editor_shared_files.cmake index 2db46dc264..334c89ec73 100644 --- a/Gems/Terrain/Code/terrain_editor_shared_files.cmake +++ b/Gems/Terrain/Code/terrain_editor_shared_files.cmake @@ -25,6 +25,8 @@ set(FILES Source/EditorTerrainModule.h Source/TerrainModule.cpp Source/TerrainModule.h + Source/TerrainRenderer/EditorComponents/EditorTerrainSurfaceMaterialsListComponent.cpp + Source/TerrainRenderer/EditorComponents/EditorTerrainSurfaceMaterialsListComponent.h Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.cpp Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.h ) diff --git a/Gems/Terrain/Code/terrain_files.cmake b/Gems/Terrain/Code/terrain_files.cmake index 5739ed6079..780dd1bdb0 100644 --- a/Gems/Terrain/Code/terrain_files.cmake +++ b/Gems/Terrain/Code/terrain_files.cmake @@ -24,10 +24,13 @@ set(FILES Source/Components/TerrainWorldDebuggerComponent.h Source/Components/TerrainWorldRendererComponent.cpp Source/Components/TerrainWorldRendererComponent.h + Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp + Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h Source/TerrainRenderer/TerrainFeatureProcessor.cpp Source/TerrainRenderer/TerrainFeatureProcessor.h + Source/TerrainRenderer/TerrainAreaMaterialRequestBus.h Source/TerrainRenderer/TerrainMacroMaterialBus.h Source/TerrainSystem/TerrainSystem.cpp Source/TerrainSystem/TerrainSystem.h diff --git a/Gems/Terrain/Code/terrain_tests_files.cmake b/Gems/Terrain/Code/terrain_tests_files.cmake index 3ce1d05003..793bc01ac5 100644 --- a/Gems/Terrain/Code/terrain_tests_files.cmake +++ b/Gems/Terrain/Code/terrain_tests_files.cmake @@ -10,5 +10,6 @@ set(FILES Tests/TerrainTest.cpp Tests/TerrainSystemTest.cpp Tests/LayerSpawnerTests.cpp + Tests/SurfaceMaterialsListTest.cpp Tests/MockAxisAlignedBoxShapeComponent.h )