diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ConstantGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ConstantGradientComponent.h index cb27914b8a..af896f0933 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ConstantGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ConstantGradientComponent.h @@ -62,6 +62,7 @@ namespace GradientSignal ////////////////////////////////////////////////////////////////////////// // GradientRequestBus float GetValue(const GradientSampleParams& sampleParams) const override; + void GetValues(AZStd::array_view positions, AZStd::array_view outValues) const override; protected: ////////////////////////////////////////////////////////////////////////// diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h index 8214436f83..9fc98124cb 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h @@ -69,6 +69,7 @@ namespace GradientSignal // GradientRequestBus overrides... float GetValue(const GradientSampleParams& sampleParams) const override; + void GetValues(AZStd::array_view positions, AZStd::array_view outValues) const override; // AZ::Data::AssetBus overrides... void OnAssetReady(AZ::Data::Asset asset) override; diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/PerlinGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/PerlinGradientComponent.h index ef171f5319..1b39f7f17b 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/PerlinGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/PerlinGradientComponent.h @@ -70,6 +70,7 @@ namespace GradientSignal // GradientRequestBus overrides... float GetValue(const GradientSampleParams& sampleParams) const override; + void GetValues(AZStd::array_view positions, AZStd::array_view outValues) const override; private: PerlinGradientConfig m_configuration; diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/RandomGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/RandomGradientComponent.h index b0dbd964a0..274442a2c0 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/RandomGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/RandomGradientComponent.h @@ -61,6 +61,7 @@ namespace GradientSignal // GradientRequestBus overrides... float GetValue(const GradientSampleParams& sampleParams) const override; + void GetValues(AZStd::array_view positions, AZStd::array_view 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; }; } diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ShapeAreaFalloffGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ShapeAreaFalloffGradientComponent.h index d7c2344fd2..df86c38069 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ShapeAreaFalloffGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ShapeAreaFalloffGradientComponent.h @@ -69,6 +69,7 @@ namespace GradientSignal ////////////////////////////////////////////////////////////////////////// // GradientRequestBus float GetValue(const GradientSampleParams& sampleParams) const override; + void GetValues(AZStd::array_view positions, AZStd::array_view outValues) const override; protected: ////////////////////////////////////////////////////////////////////////// diff --git a/Gems/GradientSignal/Code/Source/Components/ConstantGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/ConstantGradientComponent.cpp index 86e8a5abc6..2b1487f75e 100644 --- a/Gems/GradientSignal/Code/Source/Components/ConstantGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/ConstantGradientComponent.cpp @@ -134,6 +134,22 @@ namespace GradientSignal return m_configuration.m_value; } + void ConstantGradientComponent::GetValues( + [[maybe_unused]] AZStd::array_view positions, AZStd::array_view 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(outValue); + writableOutValue = m_configuration.m_value; + } + } + float ConstantGradientComponent::GetConstantValue() const { return m_configuration.m_value; diff --git a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp index 269bfbc2df..06a3674188 100644 --- a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp @@ -219,6 +219,39 @@ namespace GradientSignal return 0.0f; } + void ImageGradientComponent::GetValues(AZStd::array_view positions, AZStd::array_view 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 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(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; diff --git a/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp index 3096afa3dc..667d8c30a7 100644 --- a/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp @@ -203,6 +203,40 @@ namespace GradientSignal return 0.0f; } + void PerlinGradientComponent::GetValues(AZStd::array_view positions, AZStd::array_view 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 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(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; diff --git a/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp index 1b6753560c..ae51bdd4ac 100644 --- a/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp @@ -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(result, x * seed + y); + AZStd::hash_combine(result, y * seed + x); + AZStd::hash_combine(result, x * y * seed); + + // always returns [0.0,1.0] + return static_cast(result % std::numeric_limits::max()) / static_cast(std::numeric_limits::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(result, x * seed + y); - AZStd::hash_combine(result, y * seed + x); - AZStd::hash_combine(result, x * y * seed); - - //always returns [0.0,1.0] - return static_cast(result % std::numeric_limits::max()) / static_cast(std::numeric_limits::max()); + return GetRandomValue(uvw, seed); } return 0.0f; } + void RandomGradientComponent::GetValues(AZStd::array_view positions, AZStd::array_view 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 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(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; diff --git a/Gems/GradientSignal/Code/Source/Components/ShapeAreaFalloffGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/ShapeAreaFalloffGradientComponent.cpp index 9e1a250900..f2416f4fb6 100644 --- a/Gems/GradientSignal/Code/Source/Components/ShapeAreaFalloffGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/ShapeAreaFalloffGradientComponent.cpp @@ -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 positions, AZStd::array_view 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(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(outValues[index]); + outValue = 1.0f; + } + } } AZ::EntityId ShapeAreaFalloffGradientComponent::GetShapeEntityId() const