Files
o3de/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.cpp
T
Mike Balfour 089391f761 Terrain System cleanups and unit tests (#4119)
* Remove the "TEST_SUPPORTED" traits.
Terrain unit tests should be usable on all platforms, so they shouldn't need a platform-specific trait to enable/disable.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Fix a few misc terrain bugs.
* Change Activate/Deactivate to happen immediately instead of deferring.  There were too many order-of-operation bugs caused by trying to defer this.
* Added implementation for calculating normals.
* Fixed bug where GetHeightSynchronous wasn't stopping at the highest-priority layer.
* Added locks for SurfaceData bus to help ensure we lock our mutexes in the correct order and avoid deadlocks.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Add trivial TerrainSystem tests.
Tests construction, Activate(), Deactivate(), and destruction.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Unified Terrain system calls on single bus.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Added mock for TerrainDataNotificationBus listener.
Also added unit tests to verify the listener, and added in missing notification events.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Removed extra Sampler class.
Fixed up APIs to correctly pass Sampler and terrainExistsPtr around.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Switched MockTerrainSystem to be proper gmock.
This makes it for flexible to use and easier to reuse from other test environments.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Fix settings bug caused by bad order of operations that occurred when the methods moved to a different bus.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Eliminate extra EBus by simplifying area initialization.
Previously, there was a back-and-forth ebus signal used for the terrain system to find any terrain spawners that were created prior to the terrain system activation.  Now it uses the more simple technique of just grabbing all the spawners that are currently hooked up to the spawner ebus.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Switch to NiceMock so that "uninteresting" mock calls get ignored.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Addressed PR feedback.
Filled in terrainExistsPtr at the end, and added it to GetNormal as well.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Fixed shader height calculation.
It was off by half a pixel, and it was interpolating, both of which were wrong.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>
2021-09-15 10:57:37 -05:00

167 lines
6.7 KiB
C++

/*
* 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 <Components/TerrainLayerSpawnerComponent.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <GradientSignal/Ebuses/GradientRequestBus.h>
#include <SurfaceData/SurfaceDataProviderRequestBus.h>
#include <TerrainSystem/TerrainSystemBus.h>
namespace Terrain
{
void TerrainLayerSpawnerConfig::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<TerrainLayerSpawnerConfig, AZ::ComponentConfig>()
->Version(1)
->Field("Layer", &TerrainLayerSpawnerConfig::m_layer)
->Field("Priority", &TerrainLayerSpawnerConfig::m_priority)
->Field("UseGroundPlane", &TerrainLayerSpawnerConfig::m_useGroundPlane)
;
AZ::EditContext* edit = serialize->GetEditContext();
if (edit)
{
edit->Class<TerrainLayerSpawnerConfig>(
"Terrain Layer Spawner Component", "Provide terrain data for a region of the world")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &TerrainLayerSpawnerConfig::m_layer, "Layer Priority", "Defines a high level order that terrain spawners are applied")
->Attribute(AZ::Edit::Attributes::EnumValues, &TerrainLayerSpawnerConfig::GetSelectableLayers)
->DataElement(AZ::Edit::UIHandlers::Slider, &TerrainLayerSpawnerConfig::m_priority, "Sub Priority", "Defines order terrain spawners are applied within a layer. Larger numbers = higher priority")
->Attribute(AZ::Edit::Attributes::Min, AreaConstants::s_priorityMin)
->Attribute(AZ::Edit::Attributes::Max, AreaConstants::s_priorityMax)
->Attribute(AZ::Edit::Attributes::SoftMin, AreaConstants::s_priorityMin)
->Attribute(AZ::Edit::Attributes::SoftMax, AreaConstants::s_prioritySoftMax)
->DataElement(AZ::Edit::UIHandlers::Default, &TerrainLayerSpawnerConfig::m_useGroundPlane, "Use Ground Plane", "Determines whether or not to provide a default ground plane")
;
}
}
}
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> TerrainLayerSpawnerConfig::GetSelectableLayers() const
{
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> selectableLayers;
selectableLayers.push_back({ AreaConstants::s_backgroundLayer, AZStd::string("Background") });
selectableLayers.push_back({ AreaConstants::s_foregroundLayer, AZStd::string("Foreground") });
return selectableLayers;
}
void TerrainLayerSpawnerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("TerrainAreaService"));
}
void TerrainLayerSpawnerComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("TerrainAreaService"));
}
void TerrainLayerSpawnerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService"));
}
void TerrainLayerSpawnerComponent::Reflect(AZ::ReflectContext* context)
{
TerrainLayerSpawnerConfig::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<TerrainLayerSpawnerComponent, AZ::Component>()
->Version(0)
->Field("Configuration", &TerrainLayerSpawnerComponent::m_configuration)
;
}
}
TerrainLayerSpawnerComponent::TerrainLayerSpawnerComponent(const TerrainLayerSpawnerConfig& configuration)
: m_configuration(configuration)
{
}
void TerrainLayerSpawnerComponent::Activate()
{
AZ::TransformNotificationBus::Handler::BusConnect(GetEntityId());
LmbrCentral::ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId());
TerrainSpawnerRequestBus::Handler::BusConnect(GetEntityId());
TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RegisterArea, GetEntityId());
}
void TerrainLayerSpawnerComponent::Deactivate()
{
TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::UnregisterArea, GetEntityId());
TerrainSpawnerRequestBus::Handler::BusDisconnect();
LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect();
AZ::TransformNotificationBus::Handler::BusDisconnect();
}
bool TerrainLayerSpawnerComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
{
if (auto config = azrtti_cast<const TerrainLayerSpawnerConfig*>(baseConfig))
{
m_configuration = *config;
return true;
}
return false;
}
bool TerrainLayerSpawnerComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
{
if (auto config = azrtti_cast<TerrainLayerSpawnerConfig*>(outBaseConfig))
{
*config = m_configuration;
return true;
}
return false;
}
void TerrainLayerSpawnerComponent::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, [[maybe_unused]] const AZ::Transform& world)
{
RefreshArea();
}
void TerrainLayerSpawnerComponent::OnShapeChanged([[maybe_unused]] ShapeChangeReasons changeReason)
{
RefreshArea();
}
void TerrainLayerSpawnerComponent::GetPriority(AZ::u32& outLayer, AZ::u32& outPriority)
{
outLayer = m_configuration.m_layer;
outPriority = m_configuration.m_priority;
}
bool TerrainLayerSpawnerComponent::GetUseGroundPlane()
{
return m_configuration.m_useGroundPlane;
}
void TerrainLayerSpawnerComponent::RefreshArea()
{
TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId());
}
}