First batch of GetValues() overrides (#6852)
* Benchmarks and tests for Image and Constant GetValues Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Verify GetValues for Perlin and Random Gradients Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Standardized the assert format for GetValues(). Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * More GetValues unit tests and test cleanup Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed typos Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * GetValues() unit tests for surface gradients. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Benchmarks for ShapeAreaFalloff Gradient Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Added benchmarks for all remaining gradients and cleaned up the helper methods. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Renamed class for better report formatting. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Added missing Mocks dependencies for the Editor tests. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * First batch of specific GetValues() overrides. Each one is measurably faster than the generic version. Also, in ShapeAreaFalloffGradient, I optimized and simplified the logic a bit, so GetValue() is marginally faster too. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>
This commit is contained in:
@@ -62,6 +62,7 @@ namespace GradientSignal
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// GradientRequestBus
|
||||
float GetValue(const GradientSampleParams& sampleParams) const override;
|
||||
void GetValues(AZStd::array_view<AZ::Vector3> positions, AZStd::array_view<float> outValues) const override;
|
||||
|
||||
protected:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace GradientSignal
|
||||
|
||||
// GradientRequestBus overrides...
|
||||
float GetValue(const GradientSampleParams& sampleParams) const override;
|
||||
void GetValues(AZStd::array_view<AZ::Vector3> positions, AZStd::array_view<float> outValues) const override;
|
||||
|
||||
// AZ::Data::AssetBus overrides...
|
||||
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
|
||||
|
||||
@@ -70,6 +70,7 @@ namespace GradientSignal
|
||||
|
||||
// GradientRequestBus overrides...
|
||||
float GetValue(const GradientSampleParams& sampleParams) const override;
|
||||
void GetValues(AZStd::array_view<AZ::Vector3> positions, AZStd::array_view<float> outValues) const override;
|
||||
|
||||
private:
|
||||
PerlinGradientConfig m_configuration;
|
||||
|
||||
@@ -61,6 +61,7 @@ namespace GradientSignal
|
||||
|
||||
// GradientRequestBus overrides...
|
||||
float GetValue(const GradientSampleParams& sampleParams) const override;
|
||||
void GetValues(AZStd::array_view<AZ::Vector3> positions, AZStd::array_view<float> outValues) const override;
|
||||
|
||||
private:
|
||||
RandomGradientConfig m_configuration;
|
||||
@@ -73,5 +74,7 @@ namespace GradientSignal
|
||||
// RandomGradientRequestBus overrides...
|
||||
int GetRandomSeed() const override;
|
||||
void SetRandomSeed(int seed) override;
|
||||
|
||||
float GetRandomValue(const AZ::Vector3& position, AZStd::size_t seed) const;
|
||||
};
|
||||
}
|
||||
|
||||
+1
@@ -69,6 +69,7 @@ namespace GradientSignal
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// GradientRequestBus
|
||||
float GetValue(const GradientSampleParams& sampleParams) const override;
|
||||
void GetValues(AZStd::array_view<AZ::Vector3> positions, AZStd::array_view<float> outValues) const override;
|
||||
|
||||
protected:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -134,6 +134,22 @@ namespace GradientSignal
|
||||
return m_configuration.m_value;
|
||||
}
|
||||
|
||||
void ConstantGradientComponent::GetValues(
|
||||
[[maybe_unused]] AZStd::array_view<AZ::Vector3> positions, AZStd::array_view<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;
|
||||
}
|
||||
|
||||
for (auto& outValue : outValues)
|
||||
{
|
||||
float& writableOutValue = const_cast<float&>(outValue);
|
||||
writableOutValue = m_configuration.m_value;
|
||||
}
|
||||
}
|
||||
|
||||
float ConstantGradientComponent::GetConstantValue() const
|
||||
{
|
||||
return m_configuration.m_value;
|
||||
|
||||
@@ -219,6 +219,39 @@ namespace GradientSignal
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void ImageGradientComponent::GetValues(AZStd::array_view<AZ::Vector3> positions, AZStd::array_view<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;
|
||||
}
|
||||
|
||||
AZ::Vector3 uvw;
|
||||
bool wasPointRejected = false;
|
||||
|
||||
AZStd::shared_lock<decltype(m_imageMutex)> imageLock(m_imageMutex);
|
||||
|
||||
for (size_t index = 0; index < positions.size(); index++)
|
||||
{
|
||||
// The const_cast is necessary for now since array_view currently only supports const entries.
|
||||
// If/when array_view is fixed to support non-const, or AZStd::span gets created, the const_cast can get removed.
|
||||
auto& outValue = const_cast<float&>(outValues[index]);
|
||||
|
||||
m_gradientTransform.TransformPositionToUVWNormalized(positions[index], uvw, wasPointRejected);
|
||||
|
||||
if (!wasPointRejected)
|
||||
{
|
||||
outValue = GetValueFromImageAsset(
|
||||
m_configuration.m_imageAsset, uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
outValue = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string ImageGradientComponent::GetImageAssetPath() const
|
||||
{
|
||||
AZStd::string assetPathString;
|
||||
|
||||
@@ -203,6 +203,40 @@ namespace GradientSignal
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void PerlinGradientComponent::GetValues(AZStd::array_view<AZ::Vector3> positions, AZStd::array_view<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;
|
||||
}
|
||||
|
||||
AZ::Vector3 uvw;
|
||||
bool wasPointRejected = false;
|
||||
|
||||
AZStd::shared_lock<decltype(m_transformMutex)> lock(m_transformMutex);
|
||||
|
||||
for (size_t index = 0; index < positions.size(); index++)
|
||||
{
|
||||
// The const_cast is necessary for now since array_view currently only supports const entries.
|
||||
// If/when array_view is fixed to support non-const, or AZStd::span gets created, the const_cast can get removed.
|
||||
auto& outValue = const_cast<float&>(outValues[index]);
|
||||
|
||||
m_gradientTransform.TransformPositionToUVW(positions[index], uvw, wasPointRejected);
|
||||
|
||||
if (!wasPointRejected)
|
||||
{
|
||||
outValue = m_perlinImprovedNoise->GenerateOctaveNoise(
|
||||
uvw.GetX(), uvw.GetY(), uvw.GetZ(), m_configuration.m_octave, m_configuration.m_amplitude,
|
||||
m_configuration.m_frequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
outValue = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int PerlinGradientComponent::GetRandomSeed() const
|
||||
{
|
||||
return m_configuration.m_randomSeed;
|
||||
|
||||
@@ -145,6 +145,22 @@ namespace GradientSignal
|
||||
m_gradientTransform = newTransform;
|
||||
}
|
||||
|
||||
float RandomGradientComponent::GetRandomValue(const AZ::Vector3& position, AZStd::size_t seed) const
|
||||
{
|
||||
// generating stable pseudo-random noise from a position based hash
|
||||
float x = position.GetX();
|
||||
float y = position.GetY();
|
||||
AZStd::size_t result = 0;
|
||||
|
||||
AZStd::hash_combine<float>(result, x * seed + y);
|
||||
AZStd::hash_combine<float>(result, y * seed + x);
|
||||
AZStd::hash_combine<float>(result, x * y * seed);
|
||||
|
||||
// always returns [0.0,1.0]
|
||||
return static_cast<float>(result % std::numeric_limits<AZ::u8>::max()) / static_cast<float>(std::numeric_limits<AZ::u8>::max());
|
||||
}
|
||||
|
||||
|
||||
float RandomGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
|
||||
{
|
||||
|
||||
@@ -158,23 +174,50 @@ namespace GradientSignal
|
||||
|
||||
if (!wasPointRejected)
|
||||
{
|
||||
//generating stable pseudo-random noise from a position based hash
|
||||
float x = uvw.GetX();
|
||||
float y = uvw.GetY();
|
||||
AZStd::size_t result = 0;
|
||||
const AZStd::size_t seed = m_configuration.m_randomSeed + AZStd::size_t(2); // Add 2 to avoid seeds 0 and 1, which can create strange patterns with this particular algorithm
|
||||
const AZStd::size_t seed = m_configuration.m_randomSeed +
|
||||
AZStd::size_t(2); // Add 2 to avoid seeds 0 and 1, which can create strange patterns with this particular algorithm
|
||||
|
||||
AZStd::hash_combine<float>(result, x * seed + y);
|
||||
AZStd::hash_combine<float>(result, y * seed + x);
|
||||
AZStd::hash_combine<float>(result, x * y * seed);
|
||||
|
||||
//always returns [0.0,1.0]
|
||||
return static_cast<float>(result % std::numeric_limits<AZ::u8>::max()) / static_cast<float>(std::numeric_limits<AZ::u8>::max());
|
||||
return GetRandomValue(uvw, seed);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void RandomGradientComponent::GetValues(AZStd::array_view<AZ::Vector3> positions, AZStd::array_view<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;
|
||||
}
|
||||
|
||||
AZ::Vector3 uvw;
|
||||
bool wasPointRejected = false;
|
||||
const AZStd::size_t seed = m_configuration.m_randomSeed +
|
||||
AZStd::size_t(2); // Add 2 to avoid seeds 0 and 1, which can create strange patterns with this particular algorithm
|
||||
|
||||
AZStd::shared_lock<decltype(m_transformMutex)> lock(m_transformMutex);
|
||||
|
||||
for (size_t index = 0; index < positions.size(); index++)
|
||||
{
|
||||
// The const_cast is necessary for now since array_view currently only supports const entries.
|
||||
// If/when array_view is fixed to support non-const, or AZStd::span gets created, the const_cast can get removed.
|
||||
auto& outValue = const_cast<float&>(outValues[index]);
|
||||
|
||||
m_gradientTransform.TransformPositionToUVW(positions[index], uvw, wasPointRejected);
|
||||
|
||||
if (!wasPointRejected)
|
||||
{
|
||||
outValue = GetRandomValue(uvw, seed);
|
||||
}
|
||||
else
|
||||
{
|
||||
outValue = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int RandomGradientComponent::GetRandomSeed() const
|
||||
{
|
||||
return m_configuration.m_randomSeed;
|
||||
|
||||
@@ -157,21 +157,62 @@ namespace GradientSignal
|
||||
|
||||
float ShapeAreaFalloffGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Entity);
|
||||
|
||||
float distance = 0.0f;
|
||||
LmbrCentral::ShapeComponentRequestsBus::EventResult(distance, m_configuration.m_shapeEntityId, &LmbrCentral::ShapeComponentRequestsBus::Events::DistanceFromPoint, sampleParams.m_position);
|
||||
|
||||
// In the special case of 0 falloff, make sure that all points inside the shape (0 distance) return
|
||||
// 1.0, and all points outside the shape return 0.
|
||||
if (m_configuration.m_falloffWidth == 0.0f)
|
||||
// Since this is outer falloff, distance should give us values from 1.0 at the minimum distance to 0.0 at the maximum distance.
|
||||
// The statement is written specifically to handle the 0 falloff case as well. For 0 falloff, all points inside the shape
|
||||
// (0 distance) return 1.0, and all points outside the shape return 0. This works because division by 0 gives infinity, which gets
|
||||
// clamped by the GetMax() to 0. However, if distance == 0, it would give us NaN, so we have the separate conditional check to
|
||||
// handle that case and clamp to 1.0.
|
||||
return (distance <= 0.0f) ? 1.0f : AZ::GetMax(1.0f - (distance / m_configuration.m_falloffWidth), 0.0f);
|
||||
}
|
||||
|
||||
void ShapeAreaFalloffGradientComponent::GetValues(AZStd::array_view<AZ::Vector3> positions, AZStd::array_view<float> outValues) const
|
||||
{
|
||||
if (positions.size() != outValues.size())
|
||||
{
|
||||
return (distance > 0.0f) ? 0.0f : 1.0f;
|
||||
AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// Since this is outer falloff, distance should give us values from 1.0 at the minimum distance
|
||||
// to 0.0 at the maximum distance.
|
||||
return GetRatio(m_configuration.m_falloffWidth, 0.0f, distance);
|
||||
bool shapeConnected = false;
|
||||
const float falloffWidth = m_configuration.m_falloffWidth;
|
||||
|
||||
LmbrCentral::ShapeComponentRequestsBus::Event(
|
||||
m_configuration.m_shapeEntityId,
|
||||
[falloffWidth, positions, &outValues, &shapeConnected](LmbrCentral::ShapeComponentRequestsBus::Events* shapeRequests)
|
||||
{
|
||||
shapeConnected = true;
|
||||
|
||||
for (size_t index = 0; index < positions.size(); index++)
|
||||
{
|
||||
// The const_cast is necessary for now since array_view currently only supports const entries.
|
||||
// If/when array_view is fixed to support non-const, or AZStd::span gets created, the const_cast can get removed.
|
||||
auto& outValue = const_cast<float&>(outValues[index]);
|
||||
|
||||
float distance = shapeRequests->DistanceFromPoint(positions[index]);
|
||||
|
||||
// Since this is outer falloff, distance should give us values from 1.0 at the minimum distance to 0.0 at the maximum
|
||||
// distance. The statement is written specifically to handle the 0 falloff case as well. For 0 falloff, all points
|
||||
// inside the shape (0 distance) return 1.0, and all points outside the shape return 0. This works because division by 0
|
||||
// gives infinity, which gets clamped by the GetMax() to 0. However, if distance == 0, it would give us NaN, so we have
|
||||
// the separate conditional check to handle that case and clamp to 1.0.
|
||||
outValue = (distance <= 0.0f) ? 1.0f : AZ::GetMax(1.0f - (distance / falloffWidth), 0.0f);
|
||||
}
|
||||
});
|
||||
|
||||
// If there's no shape, there's no falloff.
|
||||
if (!shapeConnected)
|
||||
{
|
||||
for (size_t index = 0; index < positions.size(); index++)
|
||||
{
|
||||
// The const_cast is necessary for now since array_view currently only supports const entries.
|
||||
// If/when array_view is fixed to support non-const, or AZStd::span gets created, the const_cast can get removed.
|
||||
auto& outValue = const_cast<float&>(outValues[index]);
|
||||
outValue = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZ::EntityId ShapeAreaFalloffGradientComponent::GetShapeEntityId() const
|
||||
|
||||
Reference in New Issue
Block a user