[Terrain] First pass of the ProcessList and ProcessRegion APIs for retrieving surface data (#6729)
* [Terrain] First pass of the ProcessList and ProcessRegion APIs for retrieving surface data Signed-off-by: amzn-sj <srikkant@amazon.com> * Add a couple of more tests. The expected values were plugged in based on the values generated by the brute force approach. Signed-off-by: amzn-sj <srikkant@amazon.com> * Move some declarations out of loops since they can be reused. Signed-off-by: amzn-sj <srikkant@amazon.com> * Update all the per position callbacks to pass SurfacePoint refs. Construct only one SurfacePoint object outside the loop which can be reused. Signed-off-by: amzn-sj <srikkant@amazon.com> * Update tests to use the new per position callbacks Signed-off-by: amzn-sj <srikkant@amazon.com> * Add ProcessRegion functions to the terrain benchmark. Signed-off-by: amzn-sj <srikkant@amazon.com> * Change C style static casts to aznumeric_cast. Add maybe_unused to unused params in benchmarks. Signed-off-by: amzn-sj <srikkant@amazon.com> * Update the ProcessList API functions to use array_view instead of a vector. This includes some additional changes to satisfy build dependencies. Signed-off-by: amzn-sj <srikkant@amazon.com> * Add ProcessList API functions to benchmarks Signed-off-by: amzn-sj <srikkant@amazon.com> * Update the ProcessList API functions to take Vector2 as input positions Signed-off-by: amzn-sj <srikkant@amazon.com> * Revert changes to AtomCore library split. Add partial implementation of span(mostly just copied over from array_view) to AzCore std containers. Signed-off-by: amzn-sj <srikkant@amazon.com> * Adding some const/non-const overloads that were missing in span Signed-off-by: amzn-sj <srikkant@amazon.com> * Move input position list generation to a function Signed-off-by: amzn-sj <srikkant@amazon.com> * Bring back Vector3 version of ProcessList functions. Rename Vector2 version to follow similar pattern as the Get functions. Signed-off-by: amzn-sj <srikkant@amazon.com> * Split span.h into .h/.inl files Signed-off-by: amzn-sj <srikkant@amazon.com> * Add [mayby_unused] for unused parameters to fix build errors Signed-off-by: amzn-sj <srikkant@amazon.com>
This commit is contained in:
@@ -64,6 +64,8 @@ set(FILES
|
||||
containers/rbtree.h
|
||||
containers/ring_buffer.h
|
||||
containers/set.h
|
||||
containers/span.h
|
||||
containers/span.inl
|
||||
containers/stack.h
|
||||
containers/unordered_map.h
|
||||
containers/unordered_set.h
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/containers/fixed_vector.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
/**
|
||||
* First pass partial implementation of span copied over from array_view. It
|
||||
* returns non-const iterator/pointers. first(), last(), and subspan()
|
||||
* are yet to be implemented. It does not maintain storage for the data,
|
||||
* but just holds pointers to mark the beginning and end of the array.
|
||||
* It can be conveniently constructed from a variety of other container
|
||||
* types like array, vector, and fixed_vector.
|
||||
*
|
||||
* Example:
|
||||
* Given "void Func(AZStd::span<int> a) {...}" you can call...
|
||||
* - Func({1,2,3});
|
||||
* - AZStd::array<int,3> a = {1,2,3};
|
||||
* Func(a);
|
||||
* - AZStd::vector<int> v = {1,2,3};
|
||||
* Func(v);
|
||||
* - AZStd::fixed_vector<int,10> fv = {1,2,3};
|
||||
* Func(fv);
|
||||
*
|
||||
* Since the span does not copy and store any data, it is only valid as long as the data used to create it is valid.
|
||||
*/
|
||||
template <class Element>
|
||||
class span final
|
||||
{
|
||||
public:
|
||||
using value_type = Element;
|
||||
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
|
||||
using size_type = AZStd::size_t;
|
||||
using difference_type = AZStd::ptrdiff_t;
|
||||
|
||||
using iterator = value_type*;
|
||||
using const_iterator = const value_type*;
|
||||
using reverse_iterator = AZStd::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = AZStd::reverse_iterator<const_iterator>;
|
||||
|
||||
constexpr span();
|
||||
|
||||
~span() = default;
|
||||
|
||||
constexpr span(pointer s, size_type length);
|
||||
|
||||
constexpr span(pointer first, const_pointer last);
|
||||
|
||||
// We explicitly delete this constructor because it's too easy to accidentally
|
||||
// create a span to just the first element instead of an entire array.
|
||||
constexpr span(const_pointer s) = delete;
|
||||
|
||||
template<AZStd::size_t N>
|
||||
constexpr span(AZStd::array<value_type, N>& data);
|
||||
|
||||
constexpr span(AZStd::vector<value_type>& data);
|
||||
|
||||
template<AZStd::size_t N>
|
||||
constexpr span(AZStd::fixed_vector<value_type, N>& data);
|
||||
|
||||
template<AZStd::size_t N>
|
||||
constexpr span(const AZStd::array<value_type, N>& data);
|
||||
|
||||
constexpr span(const AZStd::vector<value_type>& data);
|
||||
|
||||
template<AZStd::size_t N>
|
||||
constexpr span(const AZStd::fixed_vector<value_type, N>& data);
|
||||
|
||||
constexpr span(const span&) = default;
|
||||
|
||||
constexpr span(span&& other);
|
||||
|
||||
constexpr span& operator=(const span& other) = default;
|
||||
|
||||
constexpr span& operator=(span&& other);
|
||||
|
||||
constexpr size_type size() const;
|
||||
|
||||
constexpr bool empty() const;
|
||||
|
||||
constexpr pointer data();
|
||||
constexpr const_pointer data() const;
|
||||
|
||||
constexpr const_reference operator[](size_type index) const;
|
||||
constexpr reference operator[](size_type index);
|
||||
|
||||
constexpr void erase();
|
||||
|
||||
constexpr iterator begin();
|
||||
constexpr iterator end();
|
||||
constexpr const_iterator begin() const;
|
||||
constexpr const_iterator end() const;
|
||||
|
||||
constexpr const_iterator cbegin() const;
|
||||
constexpr const_iterator cend() const;
|
||||
|
||||
constexpr reverse_iterator rbegin();
|
||||
constexpr reverse_iterator rend();
|
||||
constexpr const_reverse_iterator rbegin() const;
|
||||
constexpr const_reverse_iterator rend() const;
|
||||
|
||||
constexpr const_reverse_iterator crbegin() const;
|
||||
constexpr const_reverse_iterator crend() const;
|
||||
|
||||
friend bool operator==(span lhs, span rhs)
|
||||
{
|
||||
return lhs.m_begin == rhs.m_begin && lhs.m_end == rhs.m_end;
|
||||
}
|
||||
|
||||
friend bool operator!=(span lhs, span rhs) { return !(lhs == rhs); }
|
||||
friend bool operator< (span lhs, span rhs) { return lhs.m_begin < rhs.m_begin || lhs.m_begin == rhs.m_begin && lhs.m_end < rhs.m_end; }
|
||||
friend bool operator> (span lhs, span rhs) { return lhs.m_begin > rhs.m_begin || lhs.m_begin == rhs.m_begin && lhs.m_end > rhs.m_end; }
|
||||
friend bool operator<=(span lhs, span rhs) { return lhs == rhs || lhs < rhs; }
|
||||
friend bool operator>=(span lhs, span rhs) { return lhs == rhs || lhs > rhs; }
|
||||
|
||||
private:
|
||||
pointer m_begin;
|
||||
pointer m_end;
|
||||
};
|
||||
} // namespace AZStd
|
||||
|
||||
#include <AzCore/std/containers/span.inl>
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
template <class Element>
|
||||
inline constexpr span<Element>::span()
|
||||
: m_begin(nullptr)
|
||||
, m_end(nullptr)
|
||||
{ }
|
||||
|
||||
template <class Element>
|
||||
inline constexpr span<Element>::span(pointer s, size_type length)
|
||||
: m_begin(s)
|
||||
, m_end(m_begin + length)
|
||||
{
|
||||
if (length == 0) erase();
|
||||
}
|
||||
|
||||
template <class Element>
|
||||
inline constexpr span<Element>::span(pointer first, const_pointer last)
|
||||
: m_begin(first)
|
||||
, m_end(last)
|
||||
{ }
|
||||
|
||||
template <class Element>
|
||||
template<AZStd::size_t N>
|
||||
inline constexpr span<Element>::span(AZStd::array<Element, N>& data)
|
||||
: m_begin(data.data())
|
||||
, m_end(m_begin + data.size())
|
||||
{ }
|
||||
|
||||
template <class Element>
|
||||
inline constexpr span<Element>::span(AZStd::vector<Element>& data)
|
||||
: m_begin(data.data())
|
||||
, m_end(m_begin + data.size())
|
||||
{ }
|
||||
|
||||
template <class Element>
|
||||
template<AZStd::size_t N>
|
||||
inline constexpr span<Element>::span(AZStd::fixed_vector<Element, N>& data)
|
||||
: m_begin(data.data())
|
||||
, m_end(m_begin + data.size())
|
||||
{ }
|
||||
|
||||
template <class Element>
|
||||
template<AZStd::size_t N>
|
||||
inline constexpr span<Element>::span(const AZStd::array<Element, N>& data)
|
||||
: m_begin(data.data())
|
||||
, m_end(m_begin + data.size())
|
||||
{ }
|
||||
|
||||
template <class Element>
|
||||
inline constexpr span<Element>::span(const AZStd::vector<Element>& data)
|
||||
: m_begin(data.data())
|
||||
, m_end(m_begin + data.size())
|
||||
{ }
|
||||
|
||||
template <class Element>
|
||||
template<AZStd::size_t N>
|
||||
inline constexpr span<Element>::span(const AZStd::fixed_vector<Element, N>& data)
|
||||
: m_begin(data.data())
|
||||
, m_end(m_begin + data.size())
|
||||
{ }
|
||||
|
||||
template <class Element>
|
||||
inline constexpr span<Element>::span(span&& other)
|
||||
: span(other.m_begin, other.m_end)
|
||||
{
|
||||
#if AZ_DEBUG_BUILD // Clearing the original pointers isn't necessary, but is good for debugging
|
||||
other.m_begin = nullptr;
|
||||
other.m_end = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class Element>
|
||||
inline constexpr AZStd::size_t span<Element>::size() const { return m_end - m_begin; }
|
||||
|
||||
template <class Element>
|
||||
inline constexpr bool span<Element>::empty() const { return m_end == m_begin; }
|
||||
|
||||
template <class Element>
|
||||
inline constexpr Element* span<Element>::data() { return m_begin; }
|
||||
|
||||
template <class Element>
|
||||
inline constexpr const Element* span<Element>::data() const { return m_begin; }
|
||||
|
||||
template <class Element>
|
||||
inline constexpr span<Element>& span<Element>::operator=(span<Element>&& other)
|
||||
{
|
||||
m_begin = other.m_begin;
|
||||
m_end = other.m_end;
|
||||
#if AZ_DEBUG_BUILD // Clearing the original pointers isn't necessary, but is good for debugging
|
||||
other.m_begin = nullptr;
|
||||
other.m_end = nullptr;
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Element>
|
||||
inline constexpr const Element& span<Element>::operator[](AZStd::size_t index) const
|
||||
{
|
||||
AZ_Assert(index < size(), "index value is out of range");
|
||||
return m_begin[index];
|
||||
}
|
||||
|
||||
template <class Element>
|
||||
inline constexpr Element& span<Element>::operator[](AZStd::size_t index)
|
||||
{
|
||||
AZ_Assert(index < size(), "index value is out of range");
|
||||
return m_begin[index];
|
||||
}
|
||||
|
||||
template <class Element>
|
||||
inline constexpr void span<Element>::erase() { m_begin = m_end = nullptr; }
|
||||
|
||||
template <class Element>
|
||||
inline constexpr Element* span<Element>::begin() { return m_begin; }
|
||||
template <class Element>
|
||||
inline constexpr Element* span<Element>::end() { return m_end; }
|
||||
template <class Element>
|
||||
inline constexpr const Element* span<Element>::begin() const { return m_begin; }
|
||||
template <class Element>
|
||||
inline constexpr const Element* span<Element>::end() const { return m_end; }
|
||||
|
||||
template <class Element>
|
||||
inline constexpr const Element* span<Element>::cbegin() const { return m_begin; }
|
||||
template <class Element>
|
||||
inline constexpr const Element* span<Element>::cend() const { return m_end; }
|
||||
|
||||
template <class Element>
|
||||
inline constexpr AZStd::reverse_iterator<Element*> span<Element>::rbegin() { return AZStd::reverse_iterator<Element*>(m_end); }
|
||||
template <class Element>
|
||||
inline constexpr AZStd::reverse_iterator<Element*> span<Element>::rend() { return AZStd::reverse_iterator<Element*>(m_begin); }
|
||||
template <class Element>
|
||||
inline constexpr AZStd::reverse_iterator<const Element*> span<Element>::rbegin() const { return AZStd::reverse_iterator<const Element*>(m_end); }
|
||||
template <class Element>
|
||||
inline constexpr AZStd::reverse_iterator<const Element*> span<Element>::rend() const { return AZStd::reverse_iterator<const Element*>(m_begin); }
|
||||
|
||||
template <class Element>
|
||||
inline constexpr AZStd::reverse_iterator<const Element*> span<Element>::crbegin() const { return AZStd::reverse_iterator<const Element*>(cend()); }
|
||||
template <class Element>
|
||||
inline constexpr AZStd::reverse_iterator<const Element*> span<Element>::crend() const { return AZStd::reverse_iterator<const Element*>(cbegin()); }
|
||||
} // namespace AZStd
|
||||
@@ -11,12 +11,15 @@
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
#include <AzCore/std/containers/span.h>
|
||||
#include <AzFramework/SurfaceData/SurfaceData.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
namespace Terrain
|
||||
{
|
||||
typedef AZStd::function<void(size_t xIndex, size_t yIndex, const SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)> SurfacePointRegionFillCallback;
|
||||
typedef AZStd::function<void(const SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)> SurfacePointListFillCallback;
|
||||
|
||||
//! Shared interface for terrain system implementations
|
||||
class TerrainDataRequests
|
||||
@@ -131,6 +134,53 @@ namespace AzFramework
|
||||
Sampler sampleFilter = Sampler::DEFAULT,
|
||||
bool* terrainExistsPtr = nullptr) const = 0;
|
||||
|
||||
//! Given a list of XY coordinates, call the provided callback function with surface data corresponding to each
|
||||
//! XY coordinate in the list.
|
||||
virtual void ProcessHeightsFromList(const AZStd::span<AZ::Vector3>& inPositions,
|
||||
SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
|
||||
virtual void ProcessNormalsFromList(const AZStd::span<AZ::Vector3>& inPositions,
|
||||
SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
|
||||
virtual void ProcessSurfaceWeightsFromList(const AZStd::span<AZ::Vector3>& inPositions,
|
||||
SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
|
||||
virtual void ProcessSurfacePointsFromList(const AZStd::span<AZ::Vector3>& inPositions,
|
||||
SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
|
||||
virtual void ProcessHeightsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions,
|
||||
SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
|
||||
virtual void ProcessNormalsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions,
|
||||
SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
|
||||
virtual void ProcessSurfaceWeightsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions,
|
||||
SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
|
||||
virtual void ProcessSurfacePointsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions,
|
||||
SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
|
||||
|
||||
//! Given a region(aabb) and a step size, call the provided callback function with surface data corresponding to the
|
||||
//! coordinates in the region.
|
||||
virtual void ProcessHeightsFromRegion(const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize,
|
||||
SurfacePointRegionFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
|
||||
virtual void ProcessNormalsFromRegion(const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize,
|
||||
SurfacePointRegionFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
|
||||
virtual void ProcessSurfaceWeightsFromRegion(const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize,
|
||||
SurfacePointRegionFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
|
||||
virtual void ProcessSurfacePointsFromRegion(const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize,
|
||||
SurfacePointRegionFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
|
||||
|
||||
|
||||
private:
|
||||
// Private variations of the GetSurfacePoint API exposed to BehaviorContext that returns a value instead of
|
||||
// using an "out" parameter. The "out" parameter is useful for reusing memory allocated in SurfacePoint when
|
||||
|
||||
@@ -76,5 +76,29 @@ namespace UnitTest
|
||||
GetSurfacePointFromVector2, void(const AZ::Vector2&, AzFramework::SurfaceData::SurfacePoint&, Sampler, bool*));
|
||||
MOCK_CONST_METHOD5(
|
||||
GetSurfacePointFromFloats, void(float, float, AzFramework::SurfaceData::SurfacePoint&, Sampler, bool*));
|
||||
MOCK_CONST_METHOD3(
|
||||
ProcessHeightsFromList, void(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
|
||||
MOCK_CONST_METHOD3(
|
||||
ProcessNormalsFromList, void(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
|
||||
MOCK_CONST_METHOD3(
|
||||
ProcessSurfaceWeightsFromList, void(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
|
||||
MOCK_CONST_METHOD3(
|
||||
ProcessSurfacePointsFromList, void(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
|
||||
MOCK_CONST_METHOD3(
|
||||
ProcessHeightsFromListOfVector2, void(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
|
||||
MOCK_CONST_METHOD3(
|
||||
ProcessNormalsFromListOfVector2, void(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
|
||||
MOCK_CONST_METHOD3(
|
||||
ProcessSurfaceWeightsFromListOfVector2, void(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
|
||||
MOCK_CONST_METHOD3(
|
||||
ProcessSurfacePointsFromListOfVector2, void(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
|
||||
MOCK_CONST_METHOD4(
|
||||
ProcessHeightsFromRegion, void(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler));
|
||||
MOCK_CONST_METHOD4(
|
||||
ProcessNormalsFromRegion, void(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler));
|
||||
MOCK_CONST_METHOD4(
|
||||
ProcessSurfaceWeightsFromRegion, void(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler));
|
||||
MOCK_CONST_METHOD4(
|
||||
ProcessSurfacePointsFromRegion, void(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler));
|
||||
};
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -530,9 +530,171 @@ const char* TerrainSystem::GetMaxSurfaceName(
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
void TerrainSystem::ProcessHeightsFromList(
|
||||
const AZStd::span<AZ::Vector3>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter) const
|
||||
{
|
||||
if (!perPositionCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void TerrainSystem::ProcessHeightsFromRegion(const AZ::Aabb& inRegion, const AZ::Vector2 stepSize, Sampler sampleFilter, SurfacePointRegionFillCallback perPositionCallback, TerrainDataReadyCallback onComplete)
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (const auto& position : inPositions)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
surfacePoint.m_position = position;
|
||||
surfacePoint.m_position.SetZ(GetHeight(position, sampleFilter, &terrainExists));
|
||||
perPositionCallback(surfacePoint, terrainExists);
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainSystem::ProcessNormalsFromList(
|
||||
const AZStd::span<AZ::Vector3>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter) const
|
||||
{
|
||||
if (!perPositionCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (const auto& position : inPositions)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
surfacePoint.m_position = position;
|
||||
surfacePoint.m_normal = GetNormal(position, sampleFilter, &terrainExists);
|
||||
perPositionCallback(surfacePoint, terrainExists);
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainSystem::ProcessSurfaceWeightsFromList(
|
||||
const AZStd::span<AZ::Vector3>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter) const
|
||||
{
|
||||
if (!perPositionCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (const auto& position : inPositions)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
surfacePoint.m_position = position;
|
||||
GetSurfaceWeights(position, surfacePoint.m_surfaceTags, sampleFilter, &terrainExists);
|
||||
perPositionCallback(surfacePoint, terrainExists);
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainSystem::ProcessSurfacePointsFromList(
|
||||
const AZStd::span<AZ::Vector3>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter) const
|
||||
{
|
||||
if (!perPositionCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (const auto& position : inPositions)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
surfacePoint.m_position = position;
|
||||
GetSurfacePoint(position, surfacePoint, sampleFilter, &terrainExists);
|
||||
perPositionCallback(surfacePoint, terrainExists);
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainSystem::ProcessHeightsFromListOfVector2(
|
||||
const AZStd::span<AZ::Vector2>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter) const
|
||||
{
|
||||
if (!perPositionCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (const auto& position : inPositions)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
surfacePoint.m_position.Set(position.GetX(), position.GetY(), 0.0f);
|
||||
surfacePoint.m_position.SetZ(GetHeightFromVector2(position, sampleFilter, &terrainExists));
|
||||
perPositionCallback(surfacePoint, terrainExists);
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainSystem::ProcessNormalsFromListOfVector2(
|
||||
const AZStd::span<AZ::Vector2>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter) const
|
||||
{
|
||||
if (!perPositionCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (const auto& position : inPositions)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
surfacePoint.m_position.Set(position.GetX(), position.GetY(), 0.0f);
|
||||
surfacePoint.m_normal = GetNormalFromVector2(position, sampleFilter, &terrainExists);
|
||||
perPositionCallback(surfacePoint, terrainExists);
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainSystem::ProcessSurfaceWeightsFromListOfVector2(
|
||||
const AZStd::span<AZ::Vector2>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter) const
|
||||
{
|
||||
if (!perPositionCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (const auto& position : inPositions)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
surfacePoint.m_position.Set(position.GetX(), position.GetY(), 0.0f);
|
||||
GetSurfaceWeightsFromVector2(position, surfacePoint.m_surfaceTags, sampleFilter, &terrainExists);
|
||||
perPositionCallback(surfacePoint, terrainExists);
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainSystem::ProcessSurfacePointsFromListOfVector2(
|
||||
const AZStd::span<AZ::Vector2>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter) const
|
||||
{
|
||||
if (!perPositionCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (const auto& position : inPositions)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
surfacePoint.m_position.Set(position.GetX(), position.GetY(), 0.0f);
|
||||
GetSurfacePointFromVector2(position, surfacePoint, sampleFilter, &terrainExists);
|
||||
perPositionCallback(surfacePoint, terrainExists);
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainSystem::ProcessHeightsFromRegion(
|
||||
const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize,
|
||||
AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback,
|
||||
Sampler sampleFilter) const
|
||||
{
|
||||
// Don't bother processing if we don't have a callback
|
||||
if (!perPositionCallback)
|
||||
@@ -540,30 +702,29 @@ void TerrainSystem::ProcessHeightsFromRegion(const AZ::Aabb& inRegion, const AZ:
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t numSamplesX = static_cast<uint32_t>((inRegion.GetMax().GetX() - inRegion.GetMin().GetX()) / stepSize.GetX());
|
||||
uint32_t numSamplesY = static_cast<uint32_t>((inRegion.GetMax().GetY() - inRegion.GetMin().GetY()) / stepSize.GetY());
|
||||
|
||||
for (uint32_t y = 0; y < numSamplesY; y++)
|
||||
const size_t numSamplesX = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetX() / stepSize.GetX()));
|
||||
const size_t numSamplesY = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetY() / stepSize.GetY()));
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (size_t y = 0; y < numSamplesY; y++)
|
||||
{
|
||||
for (uint32_t x = 0; x < numSamplesX; x++)
|
||||
float fy = aznumeric_cast<float>(inRegion.GetMin().GetY() + (y * stepSize.GetY()));
|
||||
for (size_t x = 0; x < numSamplesX; x++)
|
||||
{
|
||||
float fx = (float)(inRegion.GetMin().GetX() + (x * stepSize.GetX()));
|
||||
float fy = (float)(inRegion.GetMin().GetY() + (y * stepSize.GetY()));
|
||||
|
||||
SurfaceData::SurfacePoint surfacePoint;
|
||||
GetHeight(AZ::Vector3(fx, fy, 0.0f), sampleFilter, surfacePoint.m_position);
|
||||
perPositionCallback(surfacePoint, x, y);
|
||||
bool terrainExists = false;
|
||||
float fx = aznumeric_cast<float>(inRegion.GetMin().GetX() + (x * stepSize.GetX()));
|
||||
surfacePoint.m_position.Set(fx, fy, 0.0f);
|
||||
surfacePoint.m_position.SetZ(GetHeight(surfacePoint.m_position, sampleFilter, &terrainExists));
|
||||
perPositionCallback(x, y, surfacePoint, terrainExists);
|
||||
}
|
||||
}
|
||||
|
||||
if (onComplete)
|
||||
{
|
||||
onComplete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TerrainSystem::ProcessSurfacePointsFromRegion(const AZ::Aabb& inRegion, const AZ::Vector2 stepSize, Sampler sampleFilter, SurfacePointRegionFillCallback perPositionCallback, TerrainDataReadyCallback onComplete)
|
||||
void TerrainSystem::ProcessNormalsFromRegion(
|
||||
const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize,
|
||||
AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback,
|
||||
Sampler sampleFilter) const
|
||||
{
|
||||
// Don't bother processing if we don't have a callback
|
||||
if (!perPositionCallback)
|
||||
@@ -571,28 +732,83 @@ void TerrainSystem::ProcessSurfacePointsFromRegion(const AZ::Aabb& inRegion, con
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t numSamplesX = static_cast<uint32_t>((inRegion.GetMax().GetX() - inRegion.GetMin().GetX()) / stepSize.GetX());
|
||||
uint32_t numSamplesY = static_cast<uint32_t>((inRegion.GetMax().GetY() - inRegion.GetMin().GetY()) / stepSize.GetY());
|
||||
const size_t numSamplesX = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetX() / stepSize.GetX()));
|
||||
const size_t numSamplesY = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetY() / stepSize.GetY()));
|
||||
|
||||
for (uint32_t y = 0; y < numSamplesY; y++)
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (size_t y = 0; y < numSamplesY; y++)
|
||||
{
|
||||
for (uint32_t x = 0; x < numSamplesX; x++)
|
||||
float fy = aznumeric_cast<float>(inRegion.GetMin().GetY() + (y * stepSize.GetY()));
|
||||
for (size_t x = 0; x < numSamplesX; x++)
|
||||
{
|
||||
float fx = (float)(inRegion.GetMin().GetX() + (x * stepSize.GetX()));
|
||||
float fy = (float)(inRegion.GetMin().GetY() + (y * stepSize.GetY()));
|
||||
|
||||
SurfaceData::SurfacePoint surfacePoint;
|
||||
GetSurfacePoint(AZ::Vector3(fx, fy, inRegion.GetMin().GetZ()), sampleFilter, surfacePoint);
|
||||
perPositionCallback(surfacePoint, x, y);
|
||||
bool terrainExists = false;
|
||||
float fx = aznumeric_cast<float>(inRegion.GetMin().GetX() + (x * stepSize.GetX()));
|
||||
surfacePoint.m_position.Set(fx, fy, 0.0f);
|
||||
surfacePoint.m_normal = GetNormal(surfacePoint.m_position, sampleFilter, &terrainExists);
|
||||
perPositionCallback(x, y, surfacePoint, terrainExists);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (onComplete)
|
||||
void TerrainSystem::ProcessSurfaceWeightsFromRegion(
|
||||
const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize,
|
||||
AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback,
|
||||
Sampler sampleFilter) const
|
||||
{
|
||||
// Don't bother processing if we don't have a callback
|
||||
if (!perPositionCallback)
|
||||
{
|
||||
onComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t numSamplesX = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetX() / stepSize.GetX()));
|
||||
const size_t numSamplesY = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetY() / stepSize.GetY()));
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (size_t y = 0; y < numSamplesY; y++)
|
||||
{
|
||||
float fy = aznumeric_cast<float>(inRegion.GetMin().GetY() + (y * stepSize.GetY()));
|
||||
for (size_t x = 0; x < numSamplesX; x++)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
float fx = aznumeric_cast<float>(inRegion.GetMin().GetX() + (x * stepSize.GetX()));
|
||||
surfacePoint.m_position.Set(fx, fy, 0.0f);
|
||||
GetSurfaceWeights(surfacePoint.m_position, surfacePoint.m_surfaceTags, sampleFilter, &terrainExists);
|
||||
perPositionCallback(x, y, surfacePoint, terrainExists);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainSystem::ProcessSurfacePointsFromRegion(
|
||||
const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize,
|
||||
AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback,
|
||||
Sampler sampleFilter) const
|
||||
{
|
||||
// Don't bother processing if we don't have a callback
|
||||
if (!perPositionCallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t numSamplesX = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetX() / stepSize.GetX()));
|
||||
const size_t numSamplesY = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetY() / stepSize.GetY()));
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (size_t y = 0; y < numSamplesY; y++)
|
||||
{
|
||||
float fy = aznumeric_cast<float>(inRegion.GetMin().GetY() + (y * stepSize.GetY()));
|
||||
for (size_t x = 0; x < numSamplesX; x++)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
float fx = aznumeric_cast<float>(inRegion.GetMin().GetX() + (x * stepSize.GetX()));
|
||||
surfacePoint.m_position.Set(fx, fy, 0.0f);
|
||||
GetSurfacePoint(surfacePoint.m_position, surfacePoint, sampleFilter, &terrainExists);
|
||||
perPositionCallback(x, y, surfacePoint, terrainExists);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void TerrainSystem::RegisterArea(AZ::EntityId areaId)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/std/parallel/shared_mutex.h>
|
||||
#include <AzCore/std/containers/map.h>
|
||||
#include <AzCore/std/containers/span.h>
|
||||
#include <AzCore/Math/Color.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
|
||||
@@ -135,6 +136,52 @@ namespace Terrain
|
||||
Sampler sampleFilter = Sampler::DEFAULT,
|
||||
bool* terrainExistsPtr = nullptr) const override;
|
||||
|
||||
//! Given a list of XY coordinates, call the provided callback function with surface data corresponding to each
|
||||
//! XY coordinate in the list.
|
||||
virtual void ProcessHeightsFromList(const AZStd::span<AZ::Vector3>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const override;
|
||||
virtual void ProcessNormalsFromList(const AZStd::span<AZ::Vector3>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const override;
|
||||
virtual void ProcessSurfaceWeightsFromList(const AZStd::span<AZ::Vector3>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const override;
|
||||
virtual void ProcessSurfacePointsFromList(const AZStd::span<AZ::Vector3>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const override;
|
||||
virtual void ProcessHeightsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const override;
|
||||
virtual void ProcessNormalsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const override;
|
||||
virtual void ProcessSurfaceWeightsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const override;
|
||||
virtual void ProcessSurfacePointsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions,
|
||||
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const override;
|
||||
|
||||
//! Given a region(aabb) and a step size, call the provided callback function with surface data corresponding to the
|
||||
//! coordinates in the region.
|
||||
virtual void ProcessHeightsFromRegion(const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize,
|
||||
AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const override;
|
||||
virtual void ProcessNormalsFromRegion(const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize,
|
||||
AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const override;
|
||||
virtual void ProcessSurfaceWeightsFromRegion(const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize,
|
||||
AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const override;
|
||||
virtual void ProcessSurfacePointsFromRegion(const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize,
|
||||
AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback,
|
||||
Sampler sampleFilter = Sampler::DEFAULT) const override;
|
||||
|
||||
|
||||
private:
|
||||
void ClampPosition(float x, float y, AZ::Vector2& outPosition, AZ::Vector2& normalizedDelta) const;
|
||||
|
||||
@@ -281,6 +281,22 @@ namespace UnitTest
|
||||
surfaceGradientShapeRequests.clear();
|
||||
}
|
||||
|
||||
void GenerateInputPositionsList(const AZ::Vector2& queryResolution, const AZ::Aabb& worldBounds, AZStd::vector<AZ::Vector3>& positions)
|
||||
{
|
||||
const size_t numSamplesX = aznumeric_cast<size_t>(ceil(worldBounds.GetExtents().GetX() / queryResolution.GetX()));
|
||||
const size_t numSamplesY = aznumeric_cast<size_t>(ceil(worldBounds.GetExtents().GetY() / queryResolution.GetY()));
|
||||
|
||||
for (size_t y = 0; y < numSamplesY; y++)
|
||||
{
|
||||
float fy = aznumeric_cast<float>(worldBounds.GetMin().GetY() + (y * queryResolution.GetY()));
|
||||
for (size_t x = 0; x < numSamplesX; x++)
|
||||
{
|
||||
float fx = aznumeric_cast<float>(worldBounds.GetMin().GetX() + (x * queryResolution.GetX()));
|
||||
positions.emplace_back(fx, fy, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
AZStd::unique_ptr<AZ::ComponentApplication> m_app;
|
||||
};
|
||||
@@ -322,6 +338,72 @@ namespace UnitTest
|
||||
->Args({ 4096, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessHeightsRegion)(benchmark::State& state)
|
||||
{
|
||||
// Run the benchmark
|
||||
RunTerrainApiBenchmark(
|
||||
state,
|
||||
[]([[maybe_unused]] const AZ::Vector2& queryResolution, const AZ::Aabb& worldBounds,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler sampler)
|
||||
{
|
||||
auto perPositionCallback = []([[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex,
|
||||
const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists)
|
||||
{
|
||||
benchmark::DoNotOptimize(surfacePoint.m_position.GetZ());
|
||||
};
|
||||
|
||||
AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
|
||||
&AzFramework::Terrain::TerrainDataRequests::ProcessHeightsFromRegion, worldBounds, queryResolution, perPositionCallback, sampler);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessHeightsRegion)
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 4096, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 4096, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 4096, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessHeightsList)(benchmark::State& state)
|
||||
{
|
||||
// Run the benchmark
|
||||
RunTerrainApiBenchmark(
|
||||
state,
|
||||
[this]([[maybe_unused]] const AZ::Vector2& queryResolution, const AZ::Aabb& worldBounds,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler sampler)
|
||||
{
|
||||
AZStd::vector<AZ::Vector3> inPositions;
|
||||
GenerateInputPositionsList(queryResolution, worldBounds, inPositions);
|
||||
|
||||
auto perPositionCallback = [](const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists)
|
||||
{
|
||||
benchmark::DoNotOptimize(surfacePoint.m_position.GetZ());
|
||||
};
|
||||
|
||||
AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
|
||||
&AzFramework::Terrain::TerrainDataRequests::ProcessHeightsFromList, inPositions, perPositionCallback, sampler);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessHeightsList)
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 4096, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 4096, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 4096, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_GetNormal)(benchmark::State& state)
|
||||
{
|
||||
// Run the benchmark
|
||||
@@ -353,6 +435,66 @@ namespace UnitTest
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessNormalsRegion)(benchmark::State& state)
|
||||
{
|
||||
// Run the benchmark
|
||||
RunTerrainApiBenchmark(
|
||||
state,
|
||||
[]([[maybe_unused]] const AZ::Vector2& queryResolution, const AZ::Aabb& worldBounds,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler sampler)
|
||||
{
|
||||
auto perPositionCallback = []([[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex,
|
||||
const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists)
|
||||
{
|
||||
benchmark::DoNotOptimize(surfacePoint.m_normal);
|
||||
};
|
||||
|
||||
AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
|
||||
&AzFramework::Terrain::TerrainDataRequests::ProcessNormalsFromRegion, worldBounds, queryResolution, perPositionCallback, sampler);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessNormalsRegion)
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessNormalsList)(benchmark::State& state)
|
||||
{
|
||||
// Run the benchmark
|
||||
RunTerrainApiBenchmark(
|
||||
state,
|
||||
[this]([[maybe_unused]] const AZ::Vector2& queryResolution, const AZ::Aabb& worldBounds,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler sampler)
|
||||
{
|
||||
AZStd::vector<AZ::Vector3> inPositions;
|
||||
GenerateInputPositionsList(queryResolution, worldBounds, inPositions);
|
||||
|
||||
auto perPositionCallback = [](const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists)
|
||||
{
|
||||
benchmark::DoNotOptimize(surfacePoint.m_normal);
|
||||
};
|
||||
|
||||
AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
|
||||
&AzFramework::Terrain::TerrainDataRequests::ProcessNormalsFromList, inPositions, perPositionCallback, sampler);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessNormalsList)
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_GetSurfaceWeights)(benchmark::State& state)
|
||||
{
|
||||
// Run the benchmark
|
||||
@@ -385,6 +527,66 @@ namespace UnitTest
|
||||
->Args({ 2048, 4, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfaceWeightsRegion)(benchmark::State& state)
|
||||
{
|
||||
// Run the benchmark
|
||||
RunTerrainApiBenchmark(
|
||||
state,
|
||||
[]([[maybe_unused]] const AZ::Vector2& queryResolution, const AZ::Aabb& worldBounds,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler sampler)
|
||||
{
|
||||
auto perPositionCallback = []([[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex,
|
||||
const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists)
|
||||
{
|
||||
benchmark::DoNotOptimize(surfacePoint.m_surfaceTags);
|
||||
};
|
||||
|
||||
AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
|
||||
&AzFramework::Terrain::TerrainDataRequests::ProcessSurfaceWeightsFromRegion, worldBounds, queryResolution, perPositionCallback, sampler);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfaceWeightsRegion)
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 1024, 2, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 2, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 1024, 4, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 4, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfaceWeightsList)(benchmark::State& state)
|
||||
{
|
||||
// Run the benchmark
|
||||
RunTerrainApiBenchmark(
|
||||
state,
|
||||
[this]([[maybe_unused]] const AZ::Vector2& queryResolution, const AZ::Aabb& worldBounds,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler sampler)
|
||||
{
|
||||
AZStd::vector<AZ::Vector3> inPositions;
|
||||
GenerateInputPositionsList(queryResolution, worldBounds, inPositions);
|
||||
|
||||
auto perPositionCallback = [](const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists)
|
||||
{
|
||||
benchmark::DoNotOptimize(surfacePoint.m_surfaceTags);
|
||||
};
|
||||
|
||||
AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
|
||||
&AzFramework::Terrain::TerrainDataRequests::ProcessSurfaceWeightsFromList, inPositions, perPositionCallback, sampler);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfaceWeightsList)
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 1024, 2, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 2, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 1024, 4, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 4, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_GetSurfacePoints)(benchmark::State& state)
|
||||
{
|
||||
// Run the benchmark
|
||||
@@ -416,6 +618,66 @@ namespace UnitTest
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfacePointsRegion)(benchmark::State& state)
|
||||
{
|
||||
// Run the benchmark
|
||||
RunTerrainApiBenchmark(
|
||||
state,
|
||||
[]([[maybe_unused]] const AZ::Vector2& queryResolution, const AZ::Aabb& worldBounds,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler sampler)
|
||||
{
|
||||
auto perPositionCallback = []([[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex,
|
||||
const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists)
|
||||
{
|
||||
benchmark::DoNotOptimize(surfacePoint);
|
||||
};
|
||||
|
||||
AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
|
||||
&AzFramework::Terrain::TerrainDataRequests::ProcessSurfacePointsFromRegion, worldBounds, queryResolution, perPositionCallback, sampler);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfacePointsRegion)
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfacePointsList)(benchmark::State& state)
|
||||
{
|
||||
// Run the benchmark
|
||||
RunTerrainApiBenchmark(
|
||||
state,
|
||||
[this]([[maybe_unused]] const AZ::Vector2& queryResolution, const AZ::Aabb& worldBounds,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler sampler)
|
||||
{
|
||||
AZStd::vector<AZ::Vector3> inPositions;
|
||||
GenerateInputPositionsList(queryResolution, worldBounds, inPositions);
|
||||
|
||||
auto perPositionCallback = [](const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists)
|
||||
{
|
||||
benchmark::DoNotOptimize(surfacePoint);
|
||||
};
|
||||
|
||||
AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
|
||||
&AzFramework::Terrain::TerrainDataRequests::ProcessSurfacePointsFromList, inPositions, perPositionCallback, sampler);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfacePointsList)
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) })
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) })
|
||||
->Args({ 1024, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Args({ 2048, 1, static_cast<int>(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) })
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -41,6 +41,28 @@ namespace UnitTest
|
||||
float m_expectedHeight = 0.0f;
|
||||
};
|
||||
|
||||
struct NormalTestPoint
|
||||
{
|
||||
AZ::Vector2 m_testLocation = AZ::Vector2::CreateZero();
|
||||
AZ::Vector3 m_expectedNormal = AZ::Vector3::CreateZero();
|
||||
};
|
||||
|
||||
struct HeightTestRegionPoints
|
||||
{
|
||||
size_t m_xIndex;
|
||||
size_t m_yIndex;
|
||||
float m_expectedHeight;
|
||||
AZ::Vector2 m_testLocation = AZ::Vector2::CreateZero();
|
||||
};
|
||||
|
||||
struct NormalTestRegionPoints
|
||||
{
|
||||
size_t m_xIndex;
|
||||
size_t m_yIndex;
|
||||
AZ::Vector3 m_expectedNormal = AZ::Vector3::CreateZero();
|
||||
AZ::Vector2 m_testLocation = AZ::Vector2::CreateZero();
|
||||
};
|
||||
|
||||
AZ::ComponentApplication m_app;
|
||||
|
||||
AZStd::unique_ptr<NiceMock<UnitTest::MockBoxShapeComponentRequests>> m_boxShapeRequests;
|
||||
@@ -572,4 +594,312 @@ namespace UnitTest
|
||||
EXPECT_EQ(tagWeight.m_surfaceType, tagWeight1.m_surfaceType);
|
||||
EXPECT_NEAR(tagWeight.m_weight, tagWeight1.m_weight, 0.01f);
|
||||
}
|
||||
|
||||
TEST_F(TerrainSystemTest, TerrainProcessHeightsFromListWithBilinearSamplers)
|
||||
{
|
||||
// This repeats the same test as TerrainHeightQueriesWithBilinearSamplersUseQueryGridToInterpolate
|
||||
// The difference is that it tests the ProcessHeightsFromList variation.
|
||||
|
||||
const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f);
|
||||
const float amplitudeMeters = 10.0f;
|
||||
const float frequencyMeters = 1.0f;
|
||||
auto entity = CreateAndActivateMockTerrainLayerSpawner(
|
||||
spawnerBox,
|
||||
[amplitudeMeters, frequencyMeters](AZ::Vector3& position, bool& terrainExists)
|
||||
{
|
||||
// Our generated height will be X + Y.
|
||||
float expectedHeight = position.GetX() + position.GetY();
|
||||
|
||||
// If either X or Y aren't evenly divisible by the query frequency, add a scaled value to our generated height.
|
||||
// This will show up as an unexpected height "spike" if it gets used in any bilinear filter queries.
|
||||
float unexpectedVariance =
|
||||
amplitudeMeters * (fmodf(position.GetX(), frequencyMeters) + fmodf(position.GetY(), frequencyMeters));
|
||||
position.SetZ(expectedHeight + unexpectedVariance);
|
||||
terrainExists = true;
|
||||
});
|
||||
|
||||
// Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 1 meter intervals.
|
||||
const AZ::Vector2 queryResolution(frequencyMeters);
|
||||
auto terrainSystem = CreateAndActivateTerrainSystem(queryResolution);
|
||||
|
||||
// Test some points and verify that the results are the expected bilinear filtered result,
|
||||
// whether they're in positive or negative space.
|
||||
// (Z contains the the expected result for convenience).
|
||||
const HeightTestPoint testPoints[] = {
|
||||
|
||||
// Queries directly on grid points. These should return values of X + Y.
|
||||
{ AZ::Vector2(0.0f, 0.0f), 0.0f }, // Should return a height of 0 + 0
|
||||
{ AZ::Vector2(1.0f, 0.0f), 1.0f }, // Should return a height of 1 + 0
|
||||
{ AZ::Vector2(0.0f, 1.0f), 1.0f }, // Should return a height of 0 + 1
|
||||
{ AZ::Vector2(1.0f, 1.0f), 2.0f }, // Should return a height of 1 + 1
|
||||
{ AZ::Vector2(3.0f, 5.0f), 8.0f }, // Should return a height of 3 + 5
|
||||
|
||||
{ AZ::Vector2(-1.0f, 0.0f), -1.0f }, // Should return a height of -1 + 0
|
||||
{ AZ::Vector2(0.0f, -1.0f), -1.0f }, // Should return a height of 0 + -1
|
||||
{ AZ::Vector2(-1.0f, -1.0f), -2.0f }, // Should return a height of -1 + -1
|
||||
{ AZ::Vector2(-3.0f, -5.0f), -8.0f }, // Should return a height of -3 + -5
|
||||
|
||||
// Queries that are on a grid edge (one axis on the grid, the other somewhere in-between).
|
||||
// These should just be a linear interpolation of the points, so it should still be X + Y.
|
||||
|
||||
{ AZ::Vector2(0.25f, 0.0f), 0.25f }, // Should return a height of -0.25 + 0
|
||||
{ AZ::Vector2(3.75f, 0.0f), 3.75f }, // Should return a height of -3.75 + 0
|
||||
{ AZ::Vector2(0.0f, 0.25f), 0.25f }, // Should return a height of 0 + -0.25
|
||||
{ AZ::Vector2(0.0f, 3.75f), 3.75f }, // Should return a height of 0 + -3.75
|
||||
|
||||
{ AZ::Vector2(2.0f, 3.75f), 5.75f }, // Should return a height of -2 + -3.75
|
||||
{ AZ::Vector2(2.25f, 4.0f), 6.25f }, // Should return a height of -2.25 + -4
|
||||
|
||||
{ AZ::Vector2(-0.25f, 0.0f), -0.25f }, // Should return a height of -0.25 + 0
|
||||
{ AZ::Vector2(-3.75f, 0.0f), -3.75f }, // Should return a height of -3.75 + 0
|
||||
{ AZ::Vector2(0.0f, -0.25f), -0.25f }, // Should return a height of 0 + -0.25
|
||||
{ AZ::Vector2(0.0f, -3.75f), -3.75f }, // Should return a height of 0 + -3.75
|
||||
|
||||
{ AZ::Vector2(-2.0f, -3.75f), -5.75f }, // Should return a height of -2 + -3.75
|
||||
{ AZ::Vector2(-2.25f, -4.0f), -6.25f }, // Should return a height of -2.25 + -4
|
||||
|
||||
// Queries inside a grid square (both axes are in-between grid points)
|
||||
// This is a full bilinear interpolation, but because we're using X + Y for our heights, the interpolated values
|
||||
// should *still* be X + Y assuming the points were sampled correctly from the grid points.
|
||||
|
||||
{ AZ::Vector2(3.25f, 5.25f), 8.5f }, // Should return a height of 3.25 + 5.25
|
||||
{ AZ::Vector2(7.71f, 9.74f), 17.45f }, // Should return a height of 7.71 + 9.74
|
||||
|
||||
{ AZ::Vector2(-3.25f, -5.25f), -8.5f }, // Should return a height of -3.25 + -5.25
|
||||
{ AZ::Vector2(-7.71f, -9.74f), -17.45f }, // Should return a height of -7.71 + -9.74
|
||||
};
|
||||
|
||||
auto perPositionCallback = [&testPoints](const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists){
|
||||
bool found = false;
|
||||
for (auto& testPoint : testPoints)
|
||||
{
|
||||
if (testPoint.m_testLocation.GetX() == surfacePoint.m_position.GetX() && testPoint.m_testLocation.GetY() == surfacePoint.m_position.GetY())
|
||||
{
|
||||
constexpr float epsilon = 0.0001f;
|
||||
EXPECT_NEAR(surfacePoint.m_position.GetZ(), testPoint.m_expectedHeight, epsilon);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(found, true);
|
||||
};
|
||||
|
||||
AZStd::vector<AZ::Vector3> inPositions;
|
||||
for (auto& testPoint : testPoints)
|
||||
{
|
||||
AZ::Vector3 position(testPoint.m_testLocation.GetX(), testPoint.m_testLocation.GetY(), 0.0f);
|
||||
inPositions.push_back(position);
|
||||
}
|
||||
|
||||
terrainSystem->ProcessHeightsFromList(inPositions, perPositionCallback, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR);
|
||||
}
|
||||
|
||||
TEST_F(TerrainSystemTest, TerrainProcessNormalsFromListWithBilinearSamplers)
|
||||
{
|
||||
// Similar to TerrainProcessHeightsFromListWithBilinearSamplers but for normals
|
||||
|
||||
const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f);
|
||||
const float amplitudeMeters = 10.0f;
|
||||
const float frequencyMeters = 1.0f;
|
||||
auto entity = CreateAndActivateMockTerrainLayerSpawner(
|
||||
spawnerBox,
|
||||
[amplitudeMeters, frequencyMeters](AZ::Vector3& position, bool& terrainExists)
|
||||
{
|
||||
// Our generated height will be X + Y.
|
||||
float expectedHeight = position.GetX() + position.GetY();
|
||||
|
||||
// If either X or Y aren't evenly divisible by the query frequency, add a scaled value to our generated height.
|
||||
// This will show up as an unexpected height "spike" if it gets used in any bilinear filter queries.
|
||||
float unexpectedVariance =
|
||||
amplitudeMeters * (fmodf(position.GetX(), frequencyMeters) + fmodf(position.GetY(), frequencyMeters));
|
||||
position.SetZ(expectedHeight + unexpectedVariance);
|
||||
terrainExists = true;
|
||||
});
|
||||
|
||||
// Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 1 meter intervals.
|
||||
const AZ::Vector2 queryResolution(frequencyMeters);
|
||||
auto terrainSystem = CreateAndActivateTerrainSystem(queryResolution);
|
||||
|
||||
const NormalTestPoint testPoints[] = {
|
||||
|
||||
{ AZ::Vector2(0.0f, 0.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(1.0f, 0.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(0.0f, 1.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(1.0f, 1.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(3.0f, 5.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
|
||||
{ AZ::Vector2(-1.0f, 0.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(0.0f, -1.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(-1.0f, -1.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(-3.0f, -5.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
|
||||
{ AZ::Vector2(0.25f, 0.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(3.75f, 0.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(0.0f, 0.25f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(0.0f, 3.75f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
|
||||
{ AZ::Vector2(2.0f, 3.75f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(2.25f, 4.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
|
||||
{ AZ::Vector2(-0.25f, 0.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(-3.75f, 0.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(0.0f, -0.25f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(0.0f, -3.75f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
|
||||
{ AZ::Vector2(-2.0f, -3.75f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(-2.25f, -4.0f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
|
||||
{ AZ::Vector2(3.25f, 5.25f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(7.71f, 9.74f), AZ::Vector3(-0.0292f, 0.9991f, 0.0292f) },
|
||||
|
||||
{ AZ::Vector2(-3.25f, -5.25f), AZ::Vector3(-0.5773f, -0.5773f, 0.5773f) },
|
||||
{ AZ::Vector2(-7.71f, -9.74f), AZ::Vector3(-0.0366f, -0.9986f, 0.0366f) },
|
||||
};
|
||||
|
||||
auto perPositionCallback = [&testPoints](const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists){
|
||||
bool found = false;
|
||||
for (auto& testPoint : testPoints)
|
||||
{
|
||||
if (testPoint.m_testLocation.GetX() == surfacePoint.m_position.GetX() && testPoint.m_testLocation.GetY() == surfacePoint.m_position.GetY())
|
||||
{
|
||||
constexpr float epsilon = 0.0001f;
|
||||
EXPECT_NEAR(surfacePoint.m_normal.GetX(), testPoint.m_expectedNormal.GetX(), epsilon);
|
||||
EXPECT_NEAR(surfacePoint.m_normal.GetY(), testPoint.m_expectedNormal.GetY(), epsilon);
|
||||
EXPECT_NEAR(surfacePoint.m_normal.GetZ(), testPoint.m_expectedNormal.GetZ(), epsilon);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(found, true);
|
||||
};
|
||||
|
||||
AZStd::vector<AZ::Vector3> inPositions;
|
||||
for (auto& testPoint : testPoints)
|
||||
{
|
||||
AZ::Vector3 position(testPoint.m_testLocation.GetX(), testPoint.m_testLocation.GetY(), 0.0f);
|
||||
inPositions.push_back(position);
|
||||
}
|
||||
|
||||
terrainSystem->ProcessNormalsFromList(inPositions, perPositionCallback, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR);
|
||||
}
|
||||
|
||||
TEST_F(TerrainSystemTest, TerrainProcessHeightsFromRegionWithBilinearSamplers)
|
||||
{
|
||||
// This repeats the same test as TerrainHeightQueriesWithBilinearSamplersUseQueryGridToInterpolate
|
||||
// The difference is that it tests the ProcessHeightsFromList variation.
|
||||
|
||||
const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f);
|
||||
const float amplitudeMeters = 10.0f;
|
||||
const float frequencyMeters = 1.0f;
|
||||
auto entity = CreateAndActivateMockTerrainLayerSpawner(
|
||||
spawnerBox,
|
||||
[amplitudeMeters, frequencyMeters](AZ::Vector3& position, bool& terrainExists)
|
||||
{
|
||||
// Our generated height will be X + Y.
|
||||
float expectedHeight = position.GetX() + position.GetY();
|
||||
|
||||
// If either X or Y aren't evenly divisible by the query frequency, add a scaled value to our generated height.
|
||||
// This will show up as an unexpected height "spike" if it gets used in any bilinear filter queries.
|
||||
float unexpectedVariance =
|
||||
amplitudeMeters * (fmodf(position.GetX(), frequencyMeters) + fmodf(position.GetY(), frequencyMeters));
|
||||
position.SetZ(expectedHeight + unexpectedVariance);
|
||||
terrainExists = true;
|
||||
});
|
||||
|
||||
// Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 1 meter intervals.
|
||||
const AZ::Vector2 queryResolution(frequencyMeters);
|
||||
auto terrainSystem = CreateAndActivateTerrainSystem(queryResolution);
|
||||
|
||||
const AZ::Aabb testRegionBox = AZ::Aabb::CreateFromMinMaxValues(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f);
|
||||
const AZ::Vector2 stepSize(1.0f);
|
||||
|
||||
const HeightTestRegionPoints testPoints[] = {
|
||||
{ 0, 0, -2.0f, AZ::Vector2(-1.0f, -1.0f) },
|
||||
{ 1, 0, -1.0f, AZ::Vector2(0.0f, -1.0f) },
|
||||
{ 0, 1, -1.0f, AZ::Vector2(-1.0f, 0.0f) },
|
||||
{ 1, 1, 0.0f, AZ::Vector2(0.0f, 0.0f) },
|
||||
};
|
||||
|
||||
auto perPositionCallback = [&testPoints](size_t xIndex, size_t yIndex,
|
||||
const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists)
|
||||
{
|
||||
bool found = false;
|
||||
for (auto& testPoint : testPoints)
|
||||
{
|
||||
if (testPoint.m_xIndex == xIndex && testPoint.m_yIndex == yIndex
|
||||
&& testPoint.m_testLocation.GetX() == surfacePoint.m_position.GetX()
|
||||
&& testPoint.m_testLocation.GetY() == surfacePoint.m_position.GetY())
|
||||
{
|
||||
constexpr float epsilon = 0.0001f;
|
||||
EXPECT_NEAR(surfacePoint.m_position.GetZ(), testPoint.m_expectedHeight, epsilon);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(found, true);
|
||||
};
|
||||
|
||||
terrainSystem->ProcessHeightsFromRegion(testRegionBox, stepSize, perPositionCallback, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR);
|
||||
}
|
||||
|
||||
TEST_F(TerrainSystemTest, TerrainProcessNormalsFromRegionWithBilinearSamplers)
|
||||
{
|
||||
// This repeats the same test as TerrainHeightQueriesWithBilinearSamplersUseQueryGridToInterpolate
|
||||
// The difference is that it tests the ProcessHeightsFromList variation.
|
||||
|
||||
const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f);
|
||||
const float amplitudeMeters = 10.0f;
|
||||
const float frequencyMeters = 1.0f;
|
||||
auto entity = CreateAndActivateMockTerrainLayerSpawner(
|
||||
spawnerBox,
|
||||
[amplitudeMeters, frequencyMeters](AZ::Vector3& position, bool& terrainExists)
|
||||
{
|
||||
// Our generated height will be X + Y.
|
||||
float expectedHeight = position.GetX() + position.GetY();
|
||||
|
||||
// If either X or Y aren't evenly divisible by the query frequency, add a scaled value to our generated height.
|
||||
// This will show up as an unexpected height "spike" if it gets used in any bilinear filter queries.
|
||||
float unexpectedVariance =
|
||||
amplitudeMeters * (fmodf(position.GetX(), frequencyMeters) + fmodf(position.GetY(), frequencyMeters));
|
||||
position.SetZ(expectedHeight + unexpectedVariance);
|
||||
terrainExists = true;
|
||||
});
|
||||
|
||||
// Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 1 meter intervals.
|
||||
const AZ::Vector2 queryResolution(frequencyMeters);
|
||||
auto terrainSystem = CreateAndActivateTerrainSystem(queryResolution);
|
||||
|
||||
const AZ::Aabb testRegionBox = AZ::Aabb::CreateFromMinMaxValues(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f);
|
||||
const AZ::Vector2 stepSize(1.0f);
|
||||
|
||||
const NormalTestRegionPoints testPoints[] = {
|
||||
{ 0, 0, AZ::Vector3(-0.5773f, -0.5773f, 0.5773f), AZ::Vector2(-1.0f, -1.0f) },
|
||||
{ 1, 0, AZ::Vector3(-0.5773f, -0.5773f, 0.5773f), AZ::Vector2(0.0f, -1.0f) },
|
||||
{ 0, 1, AZ::Vector3(-0.5773f, -0.5773f, 0.5773f), AZ::Vector2(-1.0f, 0.0f) },
|
||||
{ 1, 1, AZ::Vector3(-0.5773f, -0.5773f, 0.5773f), AZ::Vector2(0.0f, 0.0f) },
|
||||
};
|
||||
|
||||
auto perPositionCallback = [&testPoints](size_t xIndex, size_t yIndex,
|
||||
const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists)
|
||||
{
|
||||
bool found = false;
|
||||
for (auto& testPoint : testPoints)
|
||||
{
|
||||
if (testPoint.m_xIndex == xIndex && testPoint.m_yIndex == yIndex
|
||||
&& testPoint.m_testLocation.GetX() == surfacePoint.m_position.GetX()
|
||||
&& testPoint.m_testLocation.GetY() == surfacePoint.m_position.GetY())
|
||||
{
|
||||
constexpr float epsilon = 0.0001f;
|
||||
EXPECT_NEAR(surfacePoint.m_normal.GetX(), testPoint.m_expectedNormal.GetX(), epsilon);
|
||||
EXPECT_NEAR(surfacePoint.m_normal.GetY(), testPoint.m_expectedNormal.GetY(), epsilon);
|
||||
EXPECT_NEAR(surfacePoint.m_normal.GetZ(), testPoint.m_expectedNormal.GetZ(), epsilon);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(found, true);
|
||||
};
|
||||
|
||||
terrainSystem->ProcessNormalsFromRegion(testRegionBox, stepSize, perPositionCallback, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR);
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
Reference in New Issue
Block a user