Files
o3de/Gems/GradientSignal/Code/Source/Components/SurfaceSlopeGradientComponent.cpp
T
Mike Balfour d9ba0af645 SurfacePoint data structure encapsulations (#7413)
* First pass at encapsulating SurfacePointList.
The biggest challenge in optimizing SurfacePointList(s) usage is the overall memory management associated with it. There are M surface points with N surface mask entries created for every input point, which leads to a lot of container reallocation and memory shuffling when processing multiple input points. By encapsulating the list, it should become easier to preallocate the entries, as well as keep "helper data" around for managing the bookkeeping to associate the input points with the output points.

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

* Small fixes and TODO reminders.

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

* Encapsulate surface point creation and separate EnumeratePoints out from modifications.

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

* Start removing SurfacePoint from the exposed API.

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

* Changed SurfacePointList to split out the surface point storage to allow for span<> usage over time.

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

* Removed entity id

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

* Removed SurfacePoint from SurfaceData, changed all remaining uses to AzFramework::SurfaceData::SurfacePoint.

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

* Encapsulated SurfaceTagWeightMap and renamed to SurfaceTagWeights.

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

* Fixed make file.

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

* Better commenting and parameter naming.

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

* Renamed methods to be more descriptive.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>
2022-02-04 11:27:59 -06:00

345 lines
16 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 <GradientSignal/Components/SurfaceSlopeGradientComponent.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <SurfaceData/SurfaceDataSystemRequestBus.h>
#include <GradientSignal/Util.h>
#include <LmbrCentral/Dependency/DependencyMonitor.h>
namespace GradientSignal
{
void SurfaceSlopeGradientConfig::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<SurfaceSlopeGradientConfig, AZ::ComponentConfig>()
->Version(1)
->Field("SurfaceTagsToSample", &SurfaceSlopeGradientConfig::m_surfaceTagsToSample)
->Field("SlopeMin", &SurfaceSlopeGradientConfig::m_slopeMin)
->Field("SlopeMax", &SurfaceSlopeGradientConfig::m_slopeMax)
->Field("RampType", &SurfaceSlopeGradientConfig::m_rampType)
->Field("SmoothStep", &SurfaceSlopeGradientConfig::m_smoothStep)
;
AZ::EditContext* edit = serialize->GetEditContext();
if (edit)
{
edit->Class<SurfaceSlopeGradientConfig>(
"Slope Gradient", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &SurfaceSlopeGradientConfig::m_surfaceTagsToSample, "Surface Tags to track", "")
->DataElement(AZ::Edit::UIHandlers::Slider, &SurfaceSlopeGradientConfig::m_slopeMin, "Slope Min", "Minimum surface slope angle in degrees.")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Max, 90.0f)
->DataElement(AZ::Edit::UIHandlers::Slider, &SurfaceSlopeGradientConfig::m_slopeMax, "Slope Max", "Maximum surface slope angle in degrees.")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Max, 90.0f)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &SurfaceSlopeGradientConfig::m_rampType, "Ramp Type", "Type of ramp to apply to the slope.")
->EnumAttribute(SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_DOWN, "Linear Ramp Down")
->EnumAttribute(SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_UP, "Linear Ramp Up")
->EnumAttribute(SurfaceSlopeGradientConfig::RampType::SMOOTH_STEP, "Smooth Step")
// Note: ReadOnly doesn't currently propagate to children, so instead we hide/show smooth step parameters when
// we change the ramp type. If ReadOnly is ever changed to propagate downwards, we should change the next line
// to PropertyRefreshLevels::AttributesAndLevels and change the Visibility line below on m_smoothStep
// to AZ::Edit::PropertyVisibility::Show.
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->DataElement(AZ::Edit::UIHandlers::Default, &SurfaceSlopeGradientConfig::m_smoothStep, "Smooth Step Settings", "Parameters for controlling the smooth-step curve.")
->Attribute(AZ::Edit::Attributes::Visibility, &SurfaceSlopeGradientConfig::GetSmoothStepParameterVisibility)
->Attribute(AZ::Edit::Attributes::ReadOnly, &SurfaceSlopeGradientConfig::IsSmoothStepReadOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, false)
;
}
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<SurfaceSlopeGradientConfig>()
->Attribute(AZ::Script::Attributes::Category, "Vegetation")
->Constructor()
->Property("slopeMin", BehaviorValueProperty(&SurfaceSlopeGradientConfig::m_slopeMin))
->Property("slopeMax", BehaviorValueProperty(&SurfaceSlopeGradientConfig::m_slopeMax))
->Property("rampType",
[](SurfaceSlopeGradientConfig* config) { return reinterpret_cast<AZ::u8&>(config->m_rampType); },
[](SurfaceSlopeGradientConfig* config, const AZ::u8& i) { config->m_rampType = static_cast<SurfaceSlopeGradientConfig::RampType>(i); })
->Property("smoothStep", BehaviorValueProperty(&SurfaceSlopeGradientConfig::m_smoothStep))
->Method("GetNumTags", &SurfaceSlopeGradientConfig::GetNumTags)
->Method("GetTag", &SurfaceSlopeGradientConfig::GetTag)
->Method("RemoveTag", &SurfaceSlopeGradientConfig::RemoveTag)
->Method("AddTag", &SurfaceSlopeGradientConfig::AddTag)
;
}
}
size_t SurfaceSlopeGradientConfig::GetNumTags() const
{
return m_surfaceTagsToSample.size();
}
AZ::Crc32 SurfaceSlopeGradientConfig::GetTag(int tagIndex) const
{
if (tagIndex < m_surfaceTagsToSample.size())
{
return m_surfaceTagsToSample[tagIndex];
}
return AZ::Crc32();
}
void SurfaceSlopeGradientConfig::RemoveTag(int tagIndex)
{
if (tagIndex < m_surfaceTagsToSample.size())
{
m_surfaceTagsToSample.erase(m_surfaceTagsToSample.begin() + tagIndex);
}
}
void SurfaceSlopeGradientConfig::AddTag(AZStd::string tag)
{
m_surfaceTagsToSample.push_back(SurfaceData::SurfaceTag(tag));
}
void SurfaceSlopeGradientComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("GradientService", 0x21c18d23));
}
void SurfaceSlopeGradientComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("GradientService", 0x21c18d23));
services.push_back(AZ_CRC("GradientTransformService", 0x8c8c5ecc));
}
void SurfaceSlopeGradientComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& services)
{
}
void SurfaceSlopeGradientComponent::Reflect(AZ::ReflectContext* context)
{
SurfaceSlopeGradientConfig::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<SurfaceSlopeGradientComponent, AZ::Component>()
->Version(0)
->Field("Configuration", &SurfaceSlopeGradientComponent::m_configuration)
;
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Constant("SurfaceSlopeGradientComponentTypeId", BehaviorConstant(SurfaceSlopeGradientComponentTypeId));
behaviorContext->Class<SurfaceSlopeGradientComponent>()->RequestBus("SurfaceSlopeGradientRequestBus");
behaviorContext->EBus<SurfaceSlopeGradientRequestBus>("SurfaceSlopeGradientRequestBus")
->Attribute(AZ::Script::Attributes::Category, "Vegetation")
->Event("GetSlopeMin", &SurfaceSlopeGradientRequestBus::Events::GetSlopeMin)
->Event("SetSlopeMin", &SurfaceSlopeGradientRequestBus::Events::SetSlopeMin)
->VirtualProperty("SlopeMin", "GetSlopeMin", "SetSlopeMin")
->Event("GetSlopeMax", &SurfaceSlopeGradientRequestBus::Events::GetSlopeMax)
->Event("SetSlopeMax", &SurfaceSlopeGradientRequestBus::Events::SetSlopeMax)
->VirtualProperty("SlopeMax", "GetSlopeMax", "SetSlopeMax")
->Event("GetNumTags", &SurfaceSlopeGradientRequestBus::Events::GetNumTags)
->Event("GetTag", &SurfaceSlopeGradientRequestBus::Events::GetTag)
->Event("RemoveTag", &SurfaceSlopeGradientRequestBus::Events::RemoveTag)
->Event("AddTag", &SurfaceSlopeGradientRequestBus::Events::AddTag)
->Event("GetRampType", &SurfaceSlopeGradientRequestBus::Events::GetRampType)
->Event("SetRampType", &SurfaceSlopeGradientRequestBus::Events::SetRampType)
->VirtualProperty("RampType", "GetRampType", "SetRampType")
;
}
}
SurfaceSlopeGradientComponent::SurfaceSlopeGradientComponent(const SurfaceSlopeGradientConfig& configuration)
: m_configuration(configuration)
{
}
void SurfaceSlopeGradientComponent::Activate()
{
GradientRequestBus::Handler::BusConnect(GetEntityId());
SurfaceSlopeGradientRequestBus::Handler::BusConnect(GetEntityId());
SmoothStepRequestBus::Handler::BusConnect(GetEntityId());
}
void SurfaceSlopeGradientComponent::Deactivate()
{
GradientRequestBus::Handler::BusDisconnect();
SurfaceSlopeGradientRequestBus::Handler::BusDisconnect();
SmoothStepRequestBus::Handler::BusDisconnect();
}
bool SurfaceSlopeGradientComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
{
if (auto config = azrtti_cast<const SurfaceSlopeGradientConfig*>(baseConfig))
{
m_configuration = *config;
return true;
}
return false;
}
bool SurfaceSlopeGradientComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
{
if (auto config = azrtti_cast<SurfaceSlopeGradientConfig*>(outBaseConfig))
{
*config = m_configuration;
return true;
}
return false;
}
float SurfaceSlopeGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
{
SurfaceData::SurfacePointList points;
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePoints,
sampleParams.m_position, m_configuration.m_surfaceTagsToSample, points);
const float angleMin = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMin, 0.0f, 90.0f));
const float angleMax = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMax, 0.0f, 90.0f));
return GetSlopeRatio(points, angleMin, angleMax);
}
void SurfaceSlopeGradientComponent::GetValues(AZStd::span<const AZ::Vector3> positions, AZStd::span<float> outValues) const
{
if (positions.size() != outValues.size())
{
AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size());
return;
}
bool valuesFound = false;
// Rather than calling GetSurfacePoints on the EBus repeatedly in a loop, we instead pass a lambda into the EBus that contains
// the loop within it so that we can avoid the repeated EBus-calling overhead.
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(
[this, positions, &outValues, &valuesFound](SurfaceData::SurfaceDataSystemRequestBus::Events* surfaceDataRequests)
{
// It's possible that there's nothing connected to the EBus, so keep track of the fact that we have valid results.
valuesFound = true;
SurfaceData::SurfacePointList points;
const float angleMin = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMin, 0.0f, 90.0f));
const float angleMax = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMax, 0.0f, 90.0f));
for (size_t index = 0; index < positions.size(); index++)
{
points.Clear();
surfaceDataRequests->GetSurfacePoints(positions[index], m_configuration.m_surfaceTagsToSample, points);
outValues[index] = GetSlopeRatio(points, angleMin, angleMax);
}
});
if (!valuesFound)
{
// No surface tags, so no output values.
AZStd::fill(outValues.begin(), outValues.end(), 0.0f);
}
}
float SurfaceSlopeGradientComponent::GetSlopeMin() const
{
return m_configuration.m_slopeMin;
}
void SurfaceSlopeGradientComponent::SetSlopeMin(float slopeMin)
{
m_configuration.m_slopeMin = slopeMin;
LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
}
float SurfaceSlopeGradientComponent::GetSlopeMax() const
{
return m_configuration.m_slopeMax;
}
void SurfaceSlopeGradientComponent::SetSlopeMax(float slopeMax)
{
m_configuration.m_slopeMax = slopeMax;
LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
}
size_t SurfaceSlopeGradientComponent::GetNumTags() const
{
return m_configuration.GetNumTags();
}
AZ::Crc32 SurfaceSlopeGradientComponent::GetTag(int tagIndex) const
{
return m_configuration.GetTag(tagIndex);
}
void SurfaceSlopeGradientComponent::RemoveTag(int tagIndex)
{
m_configuration.RemoveTag(tagIndex);
LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
}
void SurfaceSlopeGradientComponent::AddTag(AZStd::string tag)
{
m_configuration.AddTag(tag);
LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
}
AZ::u8 SurfaceSlopeGradientComponent::GetRampType() const
{
return static_cast<AZ::u8>(m_configuration.m_rampType);
}
void SurfaceSlopeGradientComponent::SetRampType(AZ::u8 type)
{
m_configuration.m_rampType = static_cast<SurfaceSlopeGradientConfig::RampType>(type);
LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
}
float SurfaceSlopeGradientComponent::GetFallOffRange() const
{
return m_configuration.m_smoothStep.m_falloffRange;
}
void SurfaceSlopeGradientComponent::SetFallOffRange(float range)
{
m_configuration.m_smoothStep.m_falloffRange = range;
LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
}
float SurfaceSlopeGradientComponent::GetFallOffStrength() const
{
return m_configuration.m_smoothStep.m_falloffStrength;
}
void SurfaceSlopeGradientComponent::SetFallOffStrength(float strength)
{
m_configuration.m_smoothStep.m_falloffStrength = strength;
LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
}
float SurfaceSlopeGradientComponent::GetFallOffMidpoint() const
{
return m_configuration.m_smoothStep.m_falloffMidpoint;
}
void SurfaceSlopeGradientComponent::SetFallOffMidpoint(float midpoint)
{
m_configuration.m_smoothStep.m_falloffMidpoint = midpoint;
LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
}
}