Another batch of GetValues() overrides. (#6915)
* Another batch of GetValues() overrides. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Added missing headers. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Addressed PR feedback. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>
This commit is contained in:
@@ -99,6 +99,7 @@ namespace GradientSignal
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// GradientRequestBus
|
||||
float GetValue(const GradientSampleParams& sampleParams) const override;
|
||||
void GetValues(AZStd::span<AZ::Vector3> positions, AZStd::span<float> outValues) const override;
|
||||
bool IsEntityInHierarchy(const AZ::EntityId& entityId) const override;
|
||||
|
||||
protected:
|
||||
@@ -110,6 +111,34 @@ namespace GradientSignal
|
||||
MixedGradientLayer* GetLayer(int layerIndex) override;
|
||||
|
||||
private:
|
||||
static float PerformMixingOperation(MixedGradientLayer::MixingOperation operation, float prevValue, float currentUnpremultiplied)
|
||||
{
|
||||
switch (operation)
|
||||
{
|
||||
case MixedGradientLayer::MixingOperation::Initialize:
|
||||
return currentUnpremultiplied;
|
||||
case MixedGradientLayer::MixingOperation::Multiply:
|
||||
return prevValue * currentUnpremultiplied;
|
||||
case MixedGradientLayer::MixingOperation::Add:
|
||||
return prevValue + currentUnpremultiplied;
|
||||
case MixedGradientLayer::MixingOperation::Subtract:
|
||||
return prevValue - currentUnpremultiplied;
|
||||
case MixedGradientLayer::MixingOperation::Min:
|
||||
return AZStd::min(prevValue, currentUnpremultiplied);
|
||||
case MixedGradientLayer::MixingOperation::Max:
|
||||
return AZStd::max(prevValue, currentUnpremultiplied);
|
||||
case MixedGradientLayer::MixingOperation::Average:
|
||||
return (prevValue + currentUnpremultiplied) / 2.0f;
|
||||
case MixedGradientLayer::MixingOperation::Normal:
|
||||
return currentUnpremultiplied;
|
||||
case MixedGradientLayer::MixingOperation::Overlay:
|
||||
return (prevValue >= 0.5f) ? (1.0f - (2.0f * (1.0f - prevValue) * (1.0f - currentUnpremultiplied)))
|
||||
: (2.0f * prevValue * currentUnpremultiplied);
|
||||
default:
|
||||
return currentUnpremultiplied;
|
||||
}
|
||||
}
|
||||
|
||||
MixedGradientConfig m_configuration;
|
||||
LmbrCentral::DependencyMonitor m_dependencyMonitor;
|
||||
};
|
||||
|
||||
+34
@@ -73,6 +73,7 @@ namespace GradientSignal
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// GradientRequestBus
|
||||
float GetValue(const GradientSampleParams& sampleParams) const override;
|
||||
void GetValues(AZStd::span<AZ::Vector3> positions, AZStd::span<float> outValues) const override;
|
||||
bool IsEntityInHierarchy(const AZ::EntityId& entityId) const override;
|
||||
|
||||
protected:
|
||||
@@ -86,6 +87,39 @@ namespace GradientSignal
|
||||
GradientSampler& GetGradientSampler() override;
|
||||
|
||||
private:
|
||||
|
||||
static float PosterizeValue(float input, float bands, PosterizeGradientConfig::ModeType mode)
|
||||
{
|
||||
const float clampedInput = AZ::GetClamp(input, 0.0f, 1.0f);
|
||||
float output = 0.0f;
|
||||
|
||||
// "quantize" the input down to a number that goes from 0 to (bands-1)
|
||||
const float band = AZ::GetMin(floorf(clampedInput * bands), bands - 1.0f);
|
||||
|
||||
// Given our quantized band, produce the right output for that band range.
|
||||
switch (mode)
|
||||
{
|
||||
default:
|
||||
case PosterizeGradientConfig::ModeType::Floor:
|
||||
// Floor: the output range should be the lowest value of each band, or (0 to bands-1) / bands
|
||||
output = (band + 0.0f) / bands;
|
||||
break;
|
||||
case PosterizeGradientConfig::ModeType::Round:
|
||||
// Round: the output range should be the midpoint of each band, or (0.5 to bands-0.5) / bands
|
||||
output = (band + 0.5f) / bands;
|
||||
break;
|
||||
case PosterizeGradientConfig::ModeType::Ceiling:
|
||||
// Ceiling: the output range should be the highest value of each band, or (1 to bands) / bands
|
||||
output = (band + 1.0f) / bands;
|
||||
break;
|
||||
case PosterizeGradientConfig::ModeType::Ps:
|
||||
// Ps: the output range should be equally distributed from 0-1, or (0 to bands-1) / (bands-1)
|
||||
output = band / (bands - 1.0f);
|
||||
break;
|
||||
}
|
||||
return AZ::GetMin(output, 1.0f);
|
||||
}
|
||||
|
||||
PosterizeGradientConfig m_configuration;
|
||||
LmbrCentral::DependencyMonitor m_dependencyMonitor;
|
||||
};
|
||||
|
||||
+1
@@ -64,6 +64,7 @@ namespace GradientSignal
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// GradientRequestBus
|
||||
float GetValue(const GradientSampleParams& sampleParams) const override;
|
||||
void GetValues(AZStd::span<AZ::Vector3> positions, AZStd::span<float> outValues) const override;
|
||||
bool IsEntityInHierarchy(const AZ::EntityId& entityId) const override;
|
||||
|
||||
protected:
|
||||
|
||||
+1
@@ -71,6 +71,7 @@ namespace GradientSignal
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// GradientRequestBus
|
||||
float GetValue(const GradientSampleParams& sampleParams) const override;
|
||||
void GetValues(AZStd::span<AZ::Vector3> positions, AZStd::span<float> outValues) const override;
|
||||
bool IsEntityInHierarchy(const AZ::EntityId& entityId) const override;
|
||||
|
||||
protected:
|
||||
|
||||
+18
-1
@@ -15,6 +15,7 @@
|
||||
#include <SurfaceData/SurfaceDataTypes.h>
|
||||
#include <LmbrCentral/Dependency/DependencyNotificationBus.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <GradientSignal/Util.h>
|
||||
|
||||
namespace LmbrCentral
|
||||
{
|
||||
@@ -89,6 +90,7 @@ namespace GradientSignal
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// GradientRequestBus
|
||||
float GetValue(const GradientSampleParams& sampleParams) const override;
|
||||
void GetValues(AZStd::span<AZ::Vector3> positions, AZStd::span<float> outValues) const override;
|
||||
|
||||
protected:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -105,7 +107,22 @@ namespace GradientSignal
|
||||
void AddTag(AZStd::string tag) override;
|
||||
|
||||
private:
|
||||
mutable AZStd::recursive_mutex m_cacheMutex;
|
||||
static float CalculateAltitudeRatio(const SurfaceData::SurfacePointList& points, float altitudeMin, float altitudeMax)
|
||||
{
|
||||
if (points.empty())
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// GetSurfacePoints (which was used to populate the points list) always returns points in decreasing height order, so the
|
||||
// first point in the list contains the highest altitude.
|
||||
const float highestAltitude = points.front().m_position.GetZ();
|
||||
|
||||
// Turn the absolute altitude value into a 0-1 value by returning the % of the given altitude range that it falls at.
|
||||
return GetRatio(altitudeMin, altitudeMax, highestAltitude);
|
||||
}
|
||||
|
||||
mutable AZStd::shared_mutex m_cacheMutex;
|
||||
SurfaceAltitudeGradientConfig m_configuration;
|
||||
LmbrCentral::DependencyMonitor m_dependencyMonitor;
|
||||
AZStd::atomic_bool m_dirty{ false };
|
||||
|
||||
+16
@@ -70,6 +70,7 @@ namespace GradientSignal
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// GradientRequestBus
|
||||
float GetValue(const GradientSampleParams& sampleParams) const override;
|
||||
void GetValues(AZStd::span<AZ::Vector3> positions, AZStd::span<float> outValues) const override;
|
||||
|
||||
protected:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -80,6 +81,21 @@ namespace GradientSignal
|
||||
void AddTag(AZStd::string tag) override;
|
||||
|
||||
private:
|
||||
static float GetMaxSurfaceWeight(const SurfaceData::SurfacePointList& points)
|
||||
{
|
||||
float result = 0.0f;
|
||||
|
||||
for (const auto& point : points)
|
||||
{
|
||||
for (const auto& [maskId, weight] : point.m_masks)
|
||||
{
|
||||
result = AZ::GetMax(AZ::GetClamp(weight, 0.0f, 1.0f), result);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SurfaceMaskGradientConfig m_configuration;
|
||||
LmbrCentral::DependencyMonitor m_dependencyMonitor;
|
||||
};
|
||||
|
||||
+33
@@ -14,6 +14,7 @@
|
||||
#include <GradientSignal/Ebuses/SurfaceSlopeGradientRequestBus.h>
|
||||
#include <SurfaceData/SurfaceDataTypes.h>
|
||||
#include <GradientSignal/SmoothStep.h>
|
||||
#include <GradientSignal/Util.h>
|
||||
|
||||
namespace LmbrCentral
|
||||
{
|
||||
@@ -91,6 +92,7 @@ namespace GradientSignal
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// GradientRequestBus
|
||||
float GetValue(const GradientSampleParams& sampleParams) const override;
|
||||
void GetValues(AZStd::span<AZ::Vector3> positions, AZStd::span<float> outValues) const override;
|
||||
|
||||
protected:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -121,6 +123,37 @@ namespace GradientSignal
|
||||
void SetFallOffMidpoint(float midpoint) override;
|
||||
|
||||
private:
|
||||
float GetSlopeRatio(const SurfaceData::SurfacePointList& points, float angleMin, float angleMax) const
|
||||
{
|
||||
if (points.empty())
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Assuming our surface normal vector is actually normalized, we can get the slope
|
||||
// by just grabbing the Z value. It's the same thing as normal.Dot(AZ::Vector3::CreateAxisZ()).
|
||||
AZ_Assert(
|
||||
points.front().m_normal.GetNormalized().IsClose(points.front().m_normal),
|
||||
"Surface normals are expected to be normalized");
|
||||
const float slope = points.front().m_normal.GetZ();
|
||||
// Convert slope back to an angle so that we can lerp in "angular space", not "slope value space".
|
||||
// (We want our 0-1 range to be linear across the range of angles)
|
||||
const float slopeAngle = acosf(slope);
|
||||
|
||||
switch (m_configuration.m_rampType)
|
||||
{
|
||||
case SurfaceSlopeGradientConfig::RampType::SMOOTH_STEP:
|
||||
return m_configuration.m_smoothStep.GetSmoothedValue(GetRatio(angleMin, angleMax, slopeAngle));
|
||||
case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_UP:
|
||||
// For ramp up, linearly interpolate from min to max.
|
||||
return GetRatio(angleMin, angleMax, slopeAngle);
|
||||
case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_DOWN:
|
||||
default:
|
||||
// For ramp down, linearly interpolate from max to min.
|
||||
return GetRatio(angleMax, angleMin, slopeAngle);
|
||||
}
|
||||
}
|
||||
|
||||
SurfaceSlopeGradientConfig m_configuration;
|
||||
};
|
||||
}
|
||||
|
||||
+1
@@ -65,6 +65,7 @@ namespace GradientSignal
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// GradientRequestBus
|
||||
float GetValue(const GradientSampleParams& sampleParams) const override;
|
||||
void GetValues(AZStd::span<AZ::Vector3> positions, AZStd::span<float> outValues) const override;
|
||||
bool IsEntityInHierarchy(const AZ::EntityId& entityId) const override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace GradientSignal
|
||||
auto ClearOutputValues = [](AZStd::span<float> outValues)
|
||||
{
|
||||
// If we don't have a valid gradient (or it is fully transparent), clear out all the output values.
|
||||
memset(outValues.data(), 0, outValues.size() * sizeof(float));
|
||||
AZStd::fill(outValues.begin(), outValues.end(), 0.0f);
|
||||
};
|
||||
|
||||
if (m_opacity <= 0.0f || !m_gradientId.IsValid())
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/Serialization/EditContextConstants.inl>
|
||||
#include <AzCore/std/containers/span.h>
|
||||
#include <GradientSignal/GradientSampler.h>
|
||||
#include <GradientSignal/Util.h>
|
||||
|
||||
@@ -26,29 +27,46 @@ namespace GradientSignal
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
inline float GetSmoothedValue(float inputValue) const;
|
||||
inline void GetSmoothedValues(AZStd::span<float> inOutValues) const;
|
||||
|
||||
float m_falloffMidpoint = 0.5f;
|
||||
float m_falloffRange = 0.5f;
|
||||
float m_falloffStrength = 0.25f;
|
||||
|
||||
private:
|
||||
inline float CalculateSmoothedValue(float min, float max, float valueFalloffStrength, float inputValue) const;
|
||||
};
|
||||
|
||||
inline float SmoothStep::GetSmoothedValue(float inputValue) const
|
||||
inline float SmoothStep::CalculateSmoothedValue(float min, float max, float valueFalloffStrength, float inputValue) const
|
||||
{
|
||||
float output = 0.0f;
|
||||
|
||||
const float value = AZ::GetClamp(inputValue, 0.0f, 1.0f);
|
||||
const float valueFalloffStrength = AZ::GetClamp(m_falloffStrength, 0.0f, 1.0f);
|
||||
|
||||
float min = m_falloffMidpoint - m_falloffRange / 2.0f;
|
||||
float max = m_falloffMidpoint + m_falloffRange / 2.0f;
|
||||
|
||||
float result1 = GetRatio(min, min + valueFalloffStrength, value);
|
||||
result1 = GetSmoothStep(result1);
|
||||
float result2 = GetRatio(max - valueFalloffStrength, max, value);
|
||||
result2 = GetSmoothStep(result2);
|
||||
|
||||
output = result1 * (1.0f - result2);
|
||||
|
||||
return output;
|
||||
return result1 * (1.0f - result2);
|
||||
}
|
||||
}
|
||||
|
||||
inline float SmoothStep::GetSmoothedValue(float inputValue) const
|
||||
{
|
||||
const float min = m_falloffMidpoint - m_falloffRange / 2.0f;
|
||||
const float max = m_falloffMidpoint + m_falloffRange / 2.0f;
|
||||
const float valueFalloffStrength = AZ::GetClamp(m_falloffStrength, 0.0f, 1.0f);
|
||||
|
||||
return CalculateSmoothedValue(min, max, valueFalloffStrength, inputValue);
|
||||
}
|
||||
|
||||
inline void SmoothStep::GetSmoothedValues(AZStd::span<float> inOutValues) const
|
||||
{
|
||||
const float min = m_falloffMidpoint - m_falloffRange / 2.0f;
|
||||
const float max = m_falloffMidpoint + m_falloffRange / 2.0f;
|
||||
const float valueFalloffStrength = AZ::GetClamp(m_falloffStrength, 0.0f, 1.0f);
|
||||
|
||||
for (auto& inOutValue : inOutValues)
|
||||
{
|
||||
inOutValue = CalculateSmoothedValue(min, max, valueFalloffStrength, inOutValue);
|
||||
}
|
||||
}
|
||||
} // namespace GradientSignal
|
||||
|
||||
@@ -257,62 +257,80 @@ namespace GradientSignal
|
||||
|
||||
float MixedGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Entity);
|
||||
|
||||
//accumulate the mixed/combined result of all layers and operations
|
||||
float result = 0.0f;
|
||||
float operationResult = 0.0f;
|
||||
|
||||
for (const auto& layer : m_configuration.m_layers)
|
||||
{
|
||||
// added check to prevent opacity of 0.0, which will bust when we unpremultiply the alpha out
|
||||
if (layer.m_enabled && layer.m_gradientSampler.m_opacity != 0.0f)
|
||||
{
|
||||
// Precalculate the inverse opacity that we'll use for blending the current accumulated value with.
|
||||
// In the one case of "Initialize" blending, force this value to 0 so that we erase any accumulated values.
|
||||
const float inverseOpacity = (layer.m_operation == MixedGradientLayer::MixingOperation::Initialize)
|
||||
? 0.0f
|
||||
: (1.0f - layer.m_gradientSampler.m_opacity);
|
||||
|
||||
// this includes leveling and opacity result, we need unpremultiplied opacity to combine properly
|
||||
float current = layer.m_gradientSampler.GetValue(sampleParams);
|
||||
// unpremultiplied alpha (we clamp the end result)
|
||||
float currentUnpremultiplied = current / layer.m_gradientSampler.m_opacity;
|
||||
switch (layer.m_operation)
|
||||
{
|
||||
default:
|
||||
case MixedGradientLayer::MixingOperation::Initialize:
|
||||
//reset the result of the mixed/combined layers to the current value
|
||||
result = 0.0f;
|
||||
operationResult = currentUnpremultiplied;
|
||||
break;
|
||||
case MixedGradientLayer::MixingOperation::Multiply:
|
||||
operationResult = result * currentUnpremultiplied;
|
||||
break;
|
||||
case MixedGradientLayer::MixingOperation::Add:
|
||||
operationResult = result + currentUnpremultiplied;
|
||||
break;
|
||||
case MixedGradientLayer::MixingOperation::Subtract:
|
||||
operationResult = result - currentUnpremultiplied;
|
||||
break;
|
||||
case MixedGradientLayer::MixingOperation::Min:
|
||||
operationResult = AZStd::min(currentUnpremultiplied, result);
|
||||
break;
|
||||
case MixedGradientLayer::MixingOperation::Max:
|
||||
operationResult = AZStd::max(currentUnpremultiplied, result);
|
||||
break;
|
||||
case MixedGradientLayer::MixingOperation::Average:
|
||||
operationResult = (result + currentUnpremultiplied) / 2.0f;
|
||||
break;
|
||||
case MixedGradientLayer::MixingOperation::Normal:
|
||||
operationResult = currentUnpremultiplied;
|
||||
break;
|
||||
case MixedGradientLayer::MixingOperation::Overlay:
|
||||
operationResult = (result >= 0.5f) ? (1.0f - (2.0f * (1.0f - result) * (1.0f - currentUnpremultiplied))) : (2.0f * result * currentUnpremultiplied);
|
||||
break;
|
||||
}
|
||||
const float currentUnpremultiplied = current / layer.m_gradientSampler.m_opacity;
|
||||
const float operationResult = PerformMixingOperation(layer.m_operation, result, currentUnpremultiplied);
|
||||
// blend layers (re-applying opacity, which is why we needed to use unpremultiplied)
|
||||
result = (result * (1.0f - layer.m_gradientSampler.m_opacity)) + (operationResult * layer.m_gradientSampler.m_opacity);
|
||||
result = (result * inverseOpacity) + (operationResult * layer.m_gradientSampler.m_opacity);
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::GetClamp(result, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void MixedGradientComponent::GetValues(AZStd::span<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;
|
||||
}
|
||||
|
||||
// Initialize all of our output data to 0.0f. Layer blends will combine with this, so we need it to have an initial value.
|
||||
AZStd::fill(outValues.begin(), outValues.end(), 0.0f);
|
||||
|
||||
AZStd::vector<float> layerValues(positions.size());
|
||||
|
||||
// accumulate the mixed/combined result of all layers and operations
|
||||
for (const auto& layer : m_configuration.m_layers)
|
||||
{
|
||||
// added check to prevent opacity of 0.0, which will bust when we unpremultiply the alpha out
|
||||
if (layer.m_enabled && layer.m_gradientSampler.m_opacity != 0.0f)
|
||||
{
|
||||
// Precalculate the inverse opacity that we'll use for blending the current accumulated value with.
|
||||
// In the one case of "Initialize" blending, force this value to 0 so that we erase any accumulated values.
|
||||
const float inverseOpacity = (layer.m_operation == MixedGradientLayer::MixingOperation::Initialize)
|
||||
? 0.0f
|
||||
: (1.0f - layer.m_gradientSampler.m_opacity);
|
||||
|
||||
// this includes leveling and opacity result, we need unpremultiplied opacity to combine properly
|
||||
layer.m_gradientSampler.GetValues(positions, layerValues);
|
||||
|
||||
for (size_t index = 0; index < outValues.size(); index++)
|
||||
{
|
||||
// unpremultiplied alpha (we clamp the end result)
|
||||
const float currentUnpremultiplied = layerValues[index] / layer.m_gradientSampler.m_opacity;
|
||||
const float operationResult = PerformMixingOperation(layer.m_operation, outValues[index], currentUnpremultiplied);
|
||||
// blend layers (re-applying opacity, which is why we needed to use unpremultiplied)
|
||||
outValues[index] = (outValues[index] * inverseOpacity) + (operationResult * layer.m_gradientSampler.m_opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& outValue : outValues)
|
||||
{
|
||||
outValue = AZ::GetClamp(outValue, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool MixedGradientComponent::IsEntityInHierarchy(const AZ::EntityId& entityId) const
|
||||
{
|
||||
for (const auto& layer : m_configuration.m_layers)
|
||||
|
||||
@@ -151,34 +151,28 @@ namespace GradientSignal
|
||||
float PosterizeGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
|
||||
{
|
||||
const float bands = AZ::GetMax(static_cast<float>(m_configuration.m_bands), 2.0f);
|
||||
const float input = AZ::GetClamp(m_configuration.m_gradientSampler.GetValue(sampleParams), 0.0f, 1.0f);
|
||||
float output = 0.0f;
|
||||
const float input = m_configuration.m_gradientSampler.GetValue(sampleParams);
|
||||
return PosterizeValue(input, bands, m_configuration.m_mode);
|
||||
}
|
||||
|
||||
// "quantize" the input down to a number that goes from 0 to (bands-1)
|
||||
const float band = AZ::GetClamp(floorf(input * bands), 0.0f, bands - 1.0f);
|
||||
|
||||
// Given our quantized band, produce the right output for that band range.
|
||||
switch (m_configuration.m_mode)
|
||||
void PosterizeGradientComponent::GetValues(AZStd::span<AZ::Vector3> positions, AZStd::span<float> outValues) const
|
||||
{
|
||||
if (positions.size() != outValues.size())
|
||||
{
|
||||
default:
|
||||
case PosterizeGradientConfig::ModeType::Floor:
|
||||
// Floor: the output range should be the lowest value of each band, or (0 to bands-1) / bands
|
||||
output = (band + 0.0f) / bands;
|
||||
break;
|
||||
case PosterizeGradientConfig::ModeType::Round:
|
||||
// Round: the output range should be the midpoint of each band, or (0.5 to bands-0.5) / bands
|
||||
output = (band + 0.5f) / bands;
|
||||
break;
|
||||
case PosterizeGradientConfig::ModeType::Ceiling:
|
||||
// Ceiling: the output range should be the highest value of each band, or (1 to bands) / bands
|
||||
output = (band + 1.0f) / bands;
|
||||
break;
|
||||
case PosterizeGradientConfig::ModeType::Ps:
|
||||
// Ps: the output range should be equally distributed from 0-1, or (0 to bands-1) / (bands-1)
|
||||
output = band / (bands - 1.0f);
|
||||
break;
|
||||
AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size());
|
||||
return;
|
||||
}
|
||||
|
||||
const float bands = AZ::GetMax(static_cast<float>(m_configuration.m_bands), 2.0f);
|
||||
|
||||
// Fill in the outValues with all of the generated inupt gradient values.
|
||||
m_configuration.m_gradientSampler.GetValues(positions, outValues);
|
||||
|
||||
// Run through all the input values and posterize them.
|
||||
for (auto& outValue : outValues)
|
||||
{
|
||||
outValue = PosterizeValue(outValue, bands, m_configuration.m_mode);
|
||||
}
|
||||
return AZ::GetClamp(output, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
bool PosterizeGradientComponent::IsEntityInHierarchy(const AZ::EntityId& entityId) const
|
||||
|
||||
@@ -131,13 +131,18 @@ namespace GradientSignal
|
||||
|
||||
float ReferenceGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Entity);
|
||||
return m_configuration.m_gradientSampler.GetValue(sampleParams);
|
||||
}
|
||||
|
||||
float output = 0.0f;
|
||||
void ReferenceGradientComponent::GetValues(AZStd::span<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;
|
||||
}
|
||||
|
||||
output = m_configuration.m_gradientSampler.GetValue(sampleParams);
|
||||
|
||||
return output;
|
||||
m_configuration.m_gradientSampler.GetValues(positions, outValues);
|
||||
}
|
||||
|
||||
bool ReferenceGradientComponent::IsEntityInHierarchy(const AZ::EntityId& entityId) const
|
||||
|
||||
@@ -168,12 +168,20 @@ namespace GradientSignal
|
||||
|
||||
float SmoothStepGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
|
||||
{
|
||||
float output = 0.0f;
|
||||
const float value = m_configuration.m_gradientSampler.GetValue(sampleParams);
|
||||
return m_configuration.m_smoothStep.GetSmoothedValue(value);
|
||||
}
|
||||
|
||||
const float value = AZ::GetClamp(m_configuration.m_gradientSampler.GetValue(sampleParams), 0.0f, 1.0f);
|
||||
output = m_configuration.m_smoothStep.GetSmoothedValue(value);
|
||||
void SmoothStepGradientComponent::GetValues(AZStd::span<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;
|
||||
}
|
||||
|
||||
return output;
|
||||
m_configuration.m_gradientSampler.GetValues(positions, outValues);
|
||||
m_configuration.m_smoothStep.GetSmoothedValues(outValues);
|
||||
}
|
||||
|
||||
bool SmoothStepGradientComponent::IsEntityInHierarchy(const AZ::EntityId& entityId) const
|
||||
|
||||
@@ -202,19 +202,49 @@ namespace GradientSignal
|
||||
|
||||
float SurfaceAltitudeGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
|
||||
{
|
||||
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
|
||||
AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex);
|
||||
|
||||
SurfaceData::SurfacePointList points;
|
||||
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePoints,
|
||||
sampleParams.m_position, m_configuration.m_surfaceTagsToSample, points);
|
||||
|
||||
if (points.empty())
|
||||
return CalculateAltitudeRatio(points, m_configuration.m_altitudeMin, m_configuration.m_altitudeMax);
|
||||
}
|
||||
|
||||
void SurfaceAltitudeGradientComponent::GetValues(AZStd::span<AZ::Vector3> positions, AZStd::span<float> outValues) const
|
||||
{
|
||||
if (positions.size() != outValues.size())
|
||||
{
|
||||
return 0.0f;
|
||||
AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size());
|
||||
return;
|
||||
}
|
||||
|
||||
const AZ::Vector3& position = points.front().m_position;
|
||||
return GetRatio(m_configuration.m_altitudeMin, m_configuration.m_altitudeMax, position.GetZ());
|
||||
AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex);
|
||||
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;
|
||||
|
||||
// For each position, call GetSurfacePoints() and turn the height into a 0-1 value based on our min/max altitudes.
|
||||
for (size_t index = 0; index < positions.size(); index++)
|
||||
{
|
||||
points.clear();
|
||||
surfaceDataRequests->GetSurfacePoints(positions[index], m_configuration.m_surfaceTagsToSample, points);
|
||||
outValues[index] = CalculateAltitudeRatio(points, m_configuration.m_altitudeMin, m_configuration.m_altitudeMax);
|
||||
}
|
||||
});
|
||||
|
||||
if (!valuesFound)
|
||||
{
|
||||
// No surface data, so no output values.
|
||||
AZStd::fill(outValues.begin(), outValues.end(), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceAltitudeGradientComponent::OnCompositionChanged()
|
||||
@@ -246,7 +276,7 @@ namespace GradientSignal
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Entity);
|
||||
|
||||
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
|
||||
AZStd::unique_lock<decltype(m_cacheMutex)> lock(m_cacheMutex);
|
||||
|
||||
if (m_configuration.m_shapeEntityId.IsValid())
|
||||
{
|
||||
|
||||
@@ -161,8 +161,6 @@ namespace GradientSignal
|
||||
|
||||
float SurfaceMaskGradientComponent::GetValue(const GradientSampleParams& params) const
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Entity);
|
||||
|
||||
float result = 0.0f;
|
||||
|
||||
if (!m_configuration.m_surfaceTagList.empty())
|
||||
@@ -171,18 +169,50 @@ namespace GradientSignal
|
||||
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePoints,
|
||||
params.m_position, m_configuration.m_surfaceTagList, points);
|
||||
|
||||
for (const auto& point : points)
|
||||
{
|
||||
for (const auto& maskPair : point.m_masks)
|
||||
{
|
||||
result = AZ::GetMax(AZ::GetClamp(maskPair.second, 0.0f, 1.0f), result);
|
||||
}
|
||||
}
|
||||
result = GetMaxSurfaceWeight(points);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SurfaceMaskGradientComponent::GetValues(AZStd::span<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;
|
||||
|
||||
if (!m_configuration.m_surfaceTagList.empty())
|
||||
{
|
||||
// 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;
|
||||
|
||||
for (size_t index = 0; index < positions.size(); index++)
|
||||
{
|
||||
points.clear();
|
||||
surfaceDataRequests->GetSurfacePoints(positions[index], m_configuration.m_surfaceTagList, points);
|
||||
outValues[index] = GetMaxSurfaceWeight(points);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!valuesFound)
|
||||
{
|
||||
// No surface tags, so no output values.
|
||||
AZStd::fill(outValues.begin(), outValues.end(), 0.0f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
size_t SurfaceMaskGradientComponent::GetNumTags() const
|
||||
{
|
||||
return m_configuration.GetNumTags();
|
||||
|
||||
@@ -209,36 +209,50 @@ namespace GradientSignal
|
||||
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePoints,
|
||||
sampleParams.m_position, m_configuration.m_surfaceTagsToSample, points);
|
||||
|
||||
if (points.empty())
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Assuming our surface normal vector is actually normalized, we can get the slope
|
||||
// by just grabbing the Z value. It's the same thing as normal.Dot(AZ::Vector3::CreateAxisZ()).
|
||||
AZ_Assert(points.front().m_normal.GetNormalized().IsClose(points.front().m_normal), "Surface normals are expected to be normalized");
|
||||
const float slope = points.front().m_normal.GetZ();
|
||||
// Convert slope back to an angle so that we can lerp in "angular space", not "slope value space".
|
||||
// (We want our 0-1 range to be linear across the range of angles)
|
||||
const float slopeAngle = acosf(slope);
|
||||
|
||||
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));
|
||||
|
||||
switch (m_configuration.m_rampType)
|
||||
return GetSlopeRatio(points, angleMin, angleMax);
|
||||
}
|
||||
|
||||
void SurfaceSlopeGradientComponent::GetValues(AZStd::span<AZ::Vector3> positions, AZStd::span<float> outValues) const
|
||||
{
|
||||
if (positions.size() != outValues.size())
|
||||
{
|
||||
case SurfaceSlopeGradientConfig::RampType::SMOOTH_STEP:
|
||||
return m_configuration.m_smoothStep.GetSmoothedValue(GetRatio(angleMin, angleMax, slopeAngle));
|
||||
case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_UP:
|
||||
// For ramp up, linearly interpolate from min to max.
|
||||
return GetRatio(angleMin, angleMax, slopeAngle);
|
||||
case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_DOWN:
|
||||
default:
|
||||
// For ramp down, linearly interpolate from max to min.
|
||||
return GetRatio(angleMax, angleMin, slopeAngle);
|
||||
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;
|
||||
|
||||
@@ -138,11 +138,22 @@ namespace GradientSignal
|
||||
|
||||
float ThresholdGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
|
||||
{
|
||||
float output = 0.0f;
|
||||
return (m_configuration.m_gradientSampler.GetValue(sampleParams) <= m_configuration.m_threshold) ? 0.0f : 1.0f;
|
||||
}
|
||||
|
||||
output = m_configuration.m_gradientSampler.GetValue(sampleParams) <= m_configuration.m_threshold ? 0.0f : 1.0f;
|
||||
void ThresholdGradientComponent::GetValues(AZStd::span<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;
|
||||
}
|
||||
|
||||
return output;
|
||||
m_configuration.m_gradientSampler.GetValues(positions, outValues);
|
||||
for (auto& outValue : outValues)
|
||||
{
|
||||
outValue = (outValue <= m_configuration.m_threshold) ? 0.0f : 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool ThresholdGradientComponent::IsEntityInHierarchy(const AZ::EntityId& entityId) const
|
||||
|
||||
Reference in New Issue
Block a user