Optimize SurfaceData bulk queries (#7593)
* Add comparison operators to SurfaceTagWeight. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Changed AddSurfaceTagWeight to always combine weights. This simplifies the API a bit and defines the behavior if someone ever tries to add a duplicate tag. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Added benchmarks for measuring the performance-critical APIs. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Changed SurfaceTagWeights to a fixed_vector. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Add inPosition to AddSurfacePoint. This will be used to detect which input the surface point is associated with. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Add inPositionIndex to the appropriate APIs. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Switched Gradient Surface benchmarks to use actual surface components. The gradient unit tests and benchmarks were previously using a mock surface data system, which led to misleading benchmark results. Now, the actual SurfaceData system gets constructed, and the tests use a mock provider, but the benchmarks use actual shape providers for more realistic benchmarking. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed unit tests to have better query ranges. Half of each previous range was querying outside the surface provider's data. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * First attempt at removing SurfacePointLists. This currently runs significantly slower than the previous code but passes the unit tests. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Another attempt at optimization. This one runs faster than the previous, but still slow. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fix the cmake dependency so that the gradient tests rebuild SurfaceData.dll when run. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Switch SurfaceAltitudeGradient over to the new bulk API. Also, optimized the non-bulk API by having it reuse the SurfacePointList to avoid the repeated allocation / deallocation cost. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Switched to using an indirect index so that all allocations are consecutive in our reserved buffer. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Switched back to SurfaceTagWeight again. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Added runtime dependency to LmbrCentral for unit tests. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Switched code over to use the full EnumeratePoints in most cases. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Added knowledge of max surface point creation into the system. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Add generic GetSurfacePointsFromList API implementation for surface providers. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed implementation to use the correct maximum number of input points based on the surface providers being queried. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fix out-of-bounds references on empty lists. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fix memory allocation that caused benchmark runs to crash. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Starting to clean up the API. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Move SurfacePointList into separate files for easier maintainability. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed bug where too many points were filtered out due to using the position Z as a part of the AABB check. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Made FilterPoints an internal part of SurfacePointList so we can choose when and how to perform the filtering. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Final cleanup / comments. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Added includes for non-unity builds. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Changed how unit tests initialize the mock lists to try and fix the linux errors. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed compile error. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>
This commit is contained in:
@@ -237,7 +237,7 @@ namespace SurfaceData
|
||||
|
||||
if (DoRayTrace(inPosition, queryPointOnly, hitPosition, hitNormal))
|
||||
{
|
||||
surfacePointList.AddSurfacePoint(GetEntityId(), hitPosition, hitNormal, m_newPointWeights);
|
||||
surfacePointList.AddSurfacePoint(GetEntityId(), inPosition, hitPosition, hitNormal, m_newPointWeights);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,10 +317,11 @@ namespace SurfaceData
|
||||
providerRegistryEntry.m_entityId = GetEntityId();
|
||||
providerRegistryEntry.m_bounds = m_colliderBounds;
|
||||
providerRegistryEntry.m_tags = m_configuration.m_providerTags;
|
||||
providerRegistryEntry.m_maxPointsCreatedPerInput = 1;
|
||||
|
||||
SurfaceDataRegistryEntry modifierRegistryEntry(providerRegistryEntry);
|
||||
modifierRegistryEntry.m_tags = m_configuration.m_modifierTags;
|
||||
|
||||
modifierRegistryEntry.m_maxPointsCreatedPerInput = 0;
|
||||
|
||||
if (!colliderValidBeforeUpdate && !colliderValidAfterUpdate)
|
||||
{
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace SurfaceData
|
||||
{
|
||||
AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex);
|
||||
|
||||
if (m_shapeBoundsIsValid)
|
||||
if (m_shapeBoundsIsValid && SurfaceData::AabbContains2D(m_shapeBounds, inPosition))
|
||||
{
|
||||
const AZ::Vector3 rayOrigin = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_shapeBounds.GetMax().GetZ());
|
||||
const AZ::Vector3 rayDirection = -AZ::Vector3::CreateAxisZ();
|
||||
@@ -156,7 +156,7 @@ namespace SurfaceData
|
||||
if (hitShape)
|
||||
{
|
||||
AZ::Vector3 position = rayOrigin + intersectionDistance * rayDirection;
|
||||
surfacePointList.AddSurfacePoint(GetEntityId(), position, AZ::Vector3::CreateAxisZ(), m_newPointWeights);
|
||||
surfacePointList.AddSurfacePoint(GetEntityId(), inPosition, position, AZ::Vector3::CreateAxisZ(), m_newPointWeights);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,9 +238,11 @@ namespace SurfaceData
|
||||
providerRegistryEntry.m_entityId = GetEntityId();
|
||||
providerRegistryEntry.m_bounds = m_shapeBounds;
|
||||
providerRegistryEntry.m_tags = m_configuration.m_providerTags;
|
||||
providerRegistryEntry.m_maxPointsCreatedPerInput = 1;
|
||||
|
||||
SurfaceDataRegistryEntry modifierRegistryEntry(providerRegistryEntry);
|
||||
modifierRegistryEntry.m_tags = m_configuration.m_modifierTags;
|
||||
modifierRegistryEntry.m_maxPointsCreatedPerInput = 0;
|
||||
|
||||
if (shapeValidBeforeUpdate && shapeValidAfterUpdate)
|
||||
{
|
||||
|
||||
@@ -205,54 +205,12 @@ namespace SurfaceData
|
||||
|
||||
void SurfaceDataSystemComponent::GetSurfacePoints(const AZ::Vector3& inPosition, const SurfaceTagVector& desiredTags, SurfacePointList& surfacePointList) const
|
||||
{
|
||||
const bool useTagFilters = HasValidTags(desiredTags);
|
||||
const bool hasModifierTags = useTagFilters && HasAnyMatchingTags(desiredTags, m_registeredModifierTags);
|
||||
|
||||
AZStd::shared_lock<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
|
||||
|
||||
surfacePointList.Clear();
|
||||
surfacePointList.ReserveSpace(m_registeredSurfaceDataProviders.size());
|
||||
|
||||
//gather all intersecting points
|
||||
for (const auto& entryPair : m_registeredSurfaceDataProviders)
|
||||
{
|
||||
const AZ::u32 entryAddress = entryPair.first;
|
||||
const SurfaceDataRegistryEntry& entry = entryPair.second;
|
||||
if (!entry.m_bounds.IsValid() || AabbContains2D(entry.m_bounds, inPosition))
|
||||
{
|
||||
if (!useTagFilters || hasModifierTags || HasAnyMatchingTags(desiredTags, entry.m_tags))
|
||||
{
|
||||
SurfaceDataProviderRequestBus::Event(entryAddress, &SurfaceDataProviderRequestBus::Events::GetSurfacePoints, inPosition, surfacePointList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!surfacePointList.IsEmpty())
|
||||
{
|
||||
//modify or annotate reported points
|
||||
for (const auto& entryPair : m_registeredSurfaceDataModifiers)
|
||||
{
|
||||
const AZ::u32 entryAddress = entryPair.first;
|
||||
const SurfaceDataRegistryEntry& entry = entryPair.second;
|
||||
if (!entry.m_bounds.IsValid() || AabbContains2D(entry.m_bounds, inPosition))
|
||||
{
|
||||
SurfaceDataModifierRequestBus::Event(entryAddress, &SurfaceDataModifierRequestBus::Events::ModifySurfacePoints, surfacePointList);
|
||||
}
|
||||
}
|
||||
|
||||
// After we've finished creating and annotating all the surface points, combine any points together that have effectively the
|
||||
// same XY coordinates and extremely similar Z values. This produces results that are sorted in decreasing Z order.
|
||||
// Also, this filters out any remaining points that don't match the desired tag list. This can happen when a surface provider
|
||||
// doesn't add a desired tag, and a surface modifier has the *potential* to add it, but then doesn't.
|
||||
if (useTagFilters)
|
||||
{
|
||||
surfacePointList.FilterPoints(desiredTags);
|
||||
}
|
||||
}
|
||||
GetSurfacePointsFromListInternal(
|
||||
AZStd::span<const AZ::Vector3>(&inPosition, 1), AZ::Aabb::CreateFromPoint(inPosition), desiredTags, surfacePointList);
|
||||
}
|
||||
|
||||
void SurfaceDataSystemComponent::GetSurfacePointsFromRegion(const AZ::Aabb& inRegion, const AZ::Vector2 stepSize,
|
||||
const SurfaceTagVector& desiredTags, SurfacePointLists& surfacePointLists) const
|
||||
const SurfaceTagVector& desiredTags, SurfacePointList& surfacePointLists) const
|
||||
{
|
||||
const size_t totalQueryPositions = aznumeric_cast<size_t>(ceil(inRegion.GetXExtent() / stepSize.GetX())) *
|
||||
aznumeric_cast<size_t>(ceil(inRegion.GetYExtent() / stepSize.GetY()));
|
||||
@@ -270,47 +228,89 @@ namespace SurfaceData
|
||||
}
|
||||
}
|
||||
|
||||
GetSurfacePointsFromList(inPositions, desiredTags, surfacePointLists);
|
||||
GetSurfacePointsFromListInternal(inPositions, inRegion, desiredTags, surfacePointLists);
|
||||
}
|
||||
|
||||
void SurfaceDataSystemComponent::GetSurfacePointsFromList(
|
||||
AZStd::span<const AZ::Vector3> inPositions, const SurfaceTagVector& desiredTags, SurfacePointLists& surfacePointLists) const
|
||||
AZStd::span<const AZ::Vector3> inPositions,
|
||||
const SurfaceTagVector& desiredTags,
|
||||
SurfacePointList& surfacePointLists) const
|
||||
{
|
||||
AZ::Aabb inBounds = AZ::Aabb::CreateNull();
|
||||
for (auto& position : inPositions)
|
||||
{
|
||||
inBounds.AddPoint(position);
|
||||
}
|
||||
|
||||
GetSurfacePointsFromListInternal(inPositions, inBounds, desiredTags, surfacePointLists);
|
||||
}
|
||||
|
||||
void SurfaceDataSystemComponent::GetSurfacePointsFromListInternal(
|
||||
AZStd::span<const AZ::Vector3> inPositions, const AZ::Aabb& inPositionBounds,
|
||||
const SurfaceTagVector& desiredTags, SurfacePointList& surfacePointLists) const
|
||||
{
|
||||
AZStd::shared_lock<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
|
||||
|
||||
const size_t totalQueryPositions = inPositions.size();
|
||||
|
||||
surfacePointLists.clear();
|
||||
surfacePointLists.resize(totalQueryPositions);
|
||||
|
||||
for (auto& surfacePointList : surfacePointLists)
|
||||
{
|
||||
surfacePointList.ReserveSpace(m_registeredSurfaceDataProviders.size());
|
||||
}
|
||||
|
||||
const bool useTagFilters = HasValidTags(desiredTags);
|
||||
const bool hasModifierTags = useTagFilters && HasAnyMatchingTags(desiredTags, m_registeredModifierTags);
|
||||
|
||||
// Loop through each data provider, and query all the points for each one. This allows us to check the tags and the overall
|
||||
// AABB bounds just once per provider, instead of once per point. It also allows for an eventual optimization in which we could
|
||||
// send the list of points directly into each SurfaceDataProvider.
|
||||
for (const auto& [providerHandle, provider] : m_registeredSurfaceDataProviders)
|
||||
// Clear our output structure.
|
||||
surfacePointLists.Clear();
|
||||
|
||||
auto ProviderIsApplicable = [useTagFilters, hasModifierTags, &desiredTags, &inPositionBounds]
|
||||
(const SurfaceDataRegistryEntry& provider) -> bool
|
||||
{
|
||||
bool hasInfiniteBounds = !provider.m_bounds.IsValid();
|
||||
|
||||
// Only allow surface providers that match our tag filters. However, if we aren't using tag filters,
|
||||
// or if there's at least one surface modifier that can *add* a filtered tag to a created point, then
|
||||
// allow all the surface providers.
|
||||
if (!useTagFilters || hasModifierTags || HasAnyMatchingTags(desiredTags, provider.m_tags))
|
||||
{
|
||||
for (size_t index = 0; index < totalQueryPositions; index++)
|
||||
// Only allow surface providers that overlap the input position area.
|
||||
if (hasInfiniteBounds || AabbOverlaps2D(provider.m_bounds, inPositionBounds))
|
||||
{
|
||||
bool inBounds = hasInfiniteBounds || AabbContains2D(provider.m_bounds, inPositions[index]);
|
||||
if (inBounds)
|
||||
{
|
||||
SurfaceDataProviderRequestBus::Event(
|
||||
providerHandle, &SurfaceDataProviderRequestBus::Events::GetSurfacePoints,
|
||||
inPositions[index], surfacePointLists[index]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Gather up the subset of surface providers that overlap the input positions.
|
||||
size_t maxPointsCreatedPerInput = 0;
|
||||
for (const auto& [providerHandle, provider] : m_registeredSurfaceDataProviders)
|
||||
{
|
||||
if (ProviderIsApplicable(provider))
|
||||
{
|
||||
maxPointsCreatedPerInput += provider.m_maxPointsCreatedPerInput;
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have any surface providers that will create any new surface points, then there's nothing more to do.
|
||||
if (maxPointsCreatedPerInput == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify our output structure that we're starting to build up the list of output points.
|
||||
// This will reserve memory and allocate temporary structures to help build up the list efficiently.
|
||||
AZStd::span<const SurfaceTag> tagFilters;
|
||||
if (useTagFilters)
|
||||
{
|
||||
tagFilters = desiredTags;
|
||||
}
|
||||
surfacePointLists.StartListConstruction(inPositions, maxPointsCreatedPerInput, tagFilters);
|
||||
|
||||
// Loop through each data provider and generate surface points from the set of input positions.
|
||||
// Any generated points that have the same XY coordinates and extremely similar Z values will get combined together.
|
||||
for (const auto& [providerHandle, provider] : m_registeredSurfaceDataProviders)
|
||||
{
|
||||
if (ProviderIsApplicable(provider))
|
||||
{
|
||||
SurfaceDataProviderRequestBus::Event(
|
||||
providerHandle, &SurfaceDataProviderRequestBus::Events::GetSurfacePointsFromList, inPositions, surfacePointLists);
|
||||
}
|
||||
}
|
||||
|
||||
// Once we have our list of surface points created, run through the list of surface data modifiers to potentially add
|
||||
@@ -318,43 +318,28 @@ namespace SurfaceData
|
||||
// create new surface points, but surface data *modifiers* simply annotate points that have already been created. The modifiers
|
||||
// are used to annotate points that occur within a volume. A common example is marking points as "underwater" for points that occur
|
||||
// within a water volume.
|
||||
for (const auto& entryPair : m_registeredSurfaceDataModifiers)
|
||||
for (const auto& [modifierHandle, modifier] : m_registeredSurfaceDataModifiers)
|
||||
{
|
||||
const SurfaceDataRegistryEntry& entry = entryPair.second;
|
||||
bool hasInfiniteBounds = !entry.m_bounds.IsValid();
|
||||
bool hasInfiniteBounds = !modifier.m_bounds.IsValid();
|
||||
|
||||
for (size_t index = 0; index < totalQueryPositions; index++)
|
||||
if (hasInfiniteBounds || AabbOverlaps2D(modifier.m_bounds, surfacePointLists.GetSurfacePointAabb()))
|
||||
{
|
||||
const auto& inPosition = inPositions[index];
|
||||
SurfacePointList& surfacePointList = surfacePointLists[index];
|
||||
if (!surfacePointList.IsEmpty())
|
||||
{
|
||||
if (hasInfiniteBounds || AabbContains2D(entry.m_bounds, inPosition))
|
||||
{
|
||||
SurfaceDataModifierRequestBus::Event(
|
||||
entryPair.first, &SurfaceDataModifierRequestBus::Events::ModifySurfacePoints,
|
||||
surfacePointList);
|
||||
}
|
||||
}
|
||||
SurfaceDataModifierRequestBus::Event(
|
||||
modifierHandle, &SurfaceDataModifierRequestBus::Events::ModifySurfacePoints,
|
||||
surfacePointLists);
|
||||
}
|
||||
}
|
||||
|
||||
// After we've finished creating and annotating all the surface points, combine any points together that have effectively the
|
||||
// same XY coordinates and extremely similar Z values. This produces results that are sorted in decreasing Z order.
|
||||
// Also, this filters out any remaining points that don't match the desired tag list. This can happen when a surface provider
|
||||
// Notify the output structure that we're done building up the list.
|
||||
// This will filter out any remaining points that don't match the desired tag list. This can happen when a surface provider
|
||||
// doesn't add a desired tag, and a surface modifier has the *potential* to add it, but then doesn't.
|
||||
if (useTagFilters)
|
||||
{
|
||||
for (auto& surfacePointList : surfacePointLists)
|
||||
{
|
||||
surfacePointList.FilterPoints(desiredTags);
|
||||
}
|
||||
}
|
||||
|
||||
// It may also compact the memory and free any temporary structures.
|
||||
surfacePointLists.EndListConstruction();
|
||||
}
|
||||
|
||||
SurfaceDataRegistryHandle SurfaceDataSystemComponent::RegisterSurfaceDataProviderInternal(const SurfaceDataRegistryEntry& entry)
|
||||
{
|
||||
AZ_Assert(entry.m_maxPointsCreatedPerInput > 0, "Surface data providers should always create at least 1 point.");
|
||||
AZStd::unique_lock<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
|
||||
SurfaceDataRegistryHandle handle = ++m_registeredSurfaceDataProviderHandleCounter;
|
||||
m_registeredSurfaceDataProviders[handle] = entry;
|
||||
@@ -376,6 +361,7 @@ namespace SurfaceData
|
||||
|
||||
bool SurfaceDataSystemComponent::UpdateSurfaceDataProviderInternal(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry, AZ::Aabb& oldBounds)
|
||||
{
|
||||
AZ_Assert(entry.m_maxPointsCreatedPerInput > 0, "Surface data providers should always create at least 1 point.");
|
||||
AZStd::unique_lock<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
|
||||
auto entryItr = m_registeredSurfaceDataProviders.find(handle);
|
||||
if (entryItr != m_registeredSurfaceDataProviders.end())
|
||||
@@ -389,6 +375,7 @@ namespace SurfaceData
|
||||
|
||||
SurfaceDataRegistryHandle SurfaceDataSystemComponent::RegisterSurfaceDataModifierInternal(const SurfaceDataRegistryEntry& entry)
|
||||
{
|
||||
AZ_Assert(entry.m_maxPointsCreatedPerInput == 0, "Surface data modifiers cannot create any points.");
|
||||
AZStd::unique_lock<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
|
||||
SurfaceDataRegistryHandle handle = ++m_registeredSurfaceDataModifierHandleCounter;
|
||||
m_registeredSurfaceDataModifiers[handle] = entry;
|
||||
@@ -411,6 +398,7 @@ namespace SurfaceData
|
||||
|
||||
bool SurfaceDataSystemComponent::UpdateSurfaceDataModifierInternal(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry, AZ::Aabb& oldBounds)
|
||||
{
|
||||
AZ_Assert(entry.m_maxPointsCreatedPerInput == 0, "Surface data modifiers cannot create any points.");
|
||||
AZStd::unique_lock<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
|
||||
auto entryItr = m_registeredSurfaceDataModifiers.find(handle);
|
||||
if (entryItr != m_registeredSurfaceDataModifiers.end())
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
#include <SurfaceData/Utility/SurfaceDataUtility.h>
|
||||
#include <Atom/RPI.Reflect/Model/ModelAssetCreator.h>
|
||||
|
||||
namespace SurfaceData
|
||||
{
|
||||
@@ -118,7 +117,7 @@ namespace SurfaceData
|
||||
return FindTag(sampleTag) != m_weights.end();
|
||||
}
|
||||
|
||||
bool SurfaceTagWeights::HasAnyMatchingTags(const SurfaceTagVector& sampleTags) const
|
||||
bool SurfaceTagWeights::HasAnyMatchingTags(AZStd::span<const SurfaceTag> sampleTags) const
|
||||
{
|
||||
for (const auto& sampleTag : sampleTags)
|
||||
{
|
||||
@@ -137,7 +136,7 @@ namespace SurfaceData
|
||||
return weightEntry != m_weights.end() && weightMin <= weightEntry->m_weight && weightMax >= weightEntry->m_weight;
|
||||
}
|
||||
|
||||
bool SurfaceTagWeights::HasAnyMatchingTags(const SurfaceTagVector& sampleTags, float weightMin, float weightMax) const
|
||||
bool SurfaceTagWeights::HasAnyMatchingTags(AZStd::span<const SurfaceTag> sampleTags, float weightMin, float weightMax) const
|
||||
{
|
||||
for (const auto& sampleTag : sampleTags)
|
||||
{
|
||||
@@ -169,151 +168,4 @@ namespace SurfaceData
|
||||
// The tag wasn't found, so return end().
|
||||
return m_weights.end();
|
||||
}
|
||||
|
||||
|
||||
SurfacePointList::SurfacePointList(AZStd::initializer_list<const AzFramework::SurfaceData::SurfacePoint> surfacePoints)
|
||||
{
|
||||
ReserveSpace(surfacePoints.size());
|
||||
|
||||
for (auto& point : surfacePoints)
|
||||
{
|
||||
SurfaceTagWeights weights(point.m_surfaceTags);
|
||||
AddSurfacePoint(AZ::EntityId(), point.m_position, point.m_normal, weights);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfacePointList::AddSurfacePoint(const AZ::EntityId& entityId,
|
||||
const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceTagWeights& masks)
|
||||
{
|
||||
// When adding a surface point, we'll either merge it with a similar existing point, or else add it in order of
|
||||
// decreasing Z, so that our final results are sorted.
|
||||
|
||||
for (size_t index = 0; index < m_surfacePositionList.size(); ++index)
|
||||
{
|
||||
// (Someday we should add a configurable tolerance for comparison)
|
||||
if (m_surfacePositionList[index].IsClose(position) && m_surfaceNormalList[index].IsClose(normal))
|
||||
{
|
||||
// consolidate points with similar attributes by adding masks/weights to the similar point instead of adding a new one.
|
||||
m_surfaceWeightsList[index].AddSurfaceTagWeights(masks);
|
||||
return;
|
||||
}
|
||||
else if (m_surfacePositionList[index].GetZ() < position.GetZ())
|
||||
{
|
||||
m_pointBounds.AddPoint(position);
|
||||
m_surfacePositionList.insert(m_surfacePositionList.begin() + index, position);
|
||||
m_surfaceNormalList.insert(m_surfaceNormalList.begin() + index, normal);
|
||||
m_surfaceWeightsList.insert(m_surfaceWeightsList.begin() + index, masks);
|
||||
m_surfaceCreatorIdList.insert(m_surfaceCreatorIdList.begin() + index, entityId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The point wasn't merged and the sort puts it at the end, so just add the point to the end of the list.
|
||||
m_pointBounds.AddPoint(position);
|
||||
m_surfacePositionList.emplace_back(position);
|
||||
m_surfaceNormalList.emplace_back(normal);
|
||||
m_surfaceWeightsList.emplace_back(masks);
|
||||
m_surfaceCreatorIdList.emplace_back(entityId);
|
||||
}
|
||||
|
||||
void SurfacePointList::Clear()
|
||||
{
|
||||
m_surfacePositionList.clear();
|
||||
m_surfaceNormalList.clear();
|
||||
m_surfaceWeightsList.clear();
|
||||
m_surfaceCreatorIdList.clear();
|
||||
}
|
||||
|
||||
void SurfacePointList::ReserveSpace(size_t maxPointsPerInput)
|
||||
{
|
||||
AZ_Assert(m_surfacePositionList.empty(), "Trying to reserve space on a list that is already being used.");
|
||||
|
||||
m_surfaceCreatorIdList.reserve(maxPointsPerInput);
|
||||
m_surfacePositionList.reserve(maxPointsPerInput);
|
||||
m_surfaceNormalList.reserve(maxPointsPerInput);
|
||||
m_surfaceWeightsList.reserve(maxPointsPerInput);
|
||||
}
|
||||
|
||||
bool SurfacePointList::IsEmpty() const
|
||||
{
|
||||
return m_surfacePositionList.empty();
|
||||
}
|
||||
|
||||
size_t SurfacePointList::GetSize() const
|
||||
{
|
||||
return m_surfacePositionList.size();
|
||||
}
|
||||
|
||||
void SurfacePointList::EnumeratePoints(
|
||||
AZStd::function<bool(const AZ::Vector3&, const AZ::Vector3&, const SurfaceData::SurfaceTagWeights&)>
|
||||
pointCallback) const
|
||||
{
|
||||
for (size_t index = 0; index < m_surfacePositionList.size(); index++)
|
||||
{
|
||||
if (!pointCallback(m_surfacePositionList[index], m_surfaceNormalList[index], m_surfaceWeightsList[index]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SurfacePointList::ModifySurfaceWeights(
|
||||
const AZ::EntityId& currentEntityId,
|
||||
AZStd::function<void(const AZ::Vector3& position, SurfaceData::SurfaceTagWeights& surfaceWeights)> modificationWeightCallback)
|
||||
{
|
||||
for (size_t index = 0; index < m_surfacePositionList.size(); index++)
|
||||
{
|
||||
if (m_surfaceCreatorIdList[index] != currentEntityId)
|
||||
{
|
||||
modificationWeightCallback(m_surfacePositionList[index], m_surfaceWeightsList[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint SurfacePointList::GetHighestSurfacePoint() const
|
||||
{
|
||||
AzFramework::SurfaceData::SurfacePoint point;
|
||||
point.m_position = m_surfacePositionList.front();
|
||||
point.m_normal = m_surfaceNormalList.front();
|
||||
point.m_surfaceTags = m_surfaceWeightsList.front().GetSurfaceTagWeightList();
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
void SurfacePointList::FilterPoints(const SurfaceTagVector& desiredTags)
|
||||
{
|
||||
// Filter out any points that don't match our search tags.
|
||||
// This has to be done after the Surface Modifiers have processed the points, not at point insertion time, because
|
||||
// Surface Modifiers add tags to existing points.
|
||||
size_t listSize = m_surfacePositionList.size();
|
||||
size_t index = 0;
|
||||
for (; index < listSize; index++)
|
||||
{
|
||||
if (!m_surfaceWeightsList[index].HasAnyMatchingTags(desiredTags))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index != listSize)
|
||||
{
|
||||
size_t next = index + 1;
|
||||
for (; next < listSize; ++next)
|
||||
{
|
||||
if (m_surfaceWeightsList[index].HasAnyMatchingTags(desiredTags))
|
||||
{
|
||||
m_surfaceCreatorIdList[index] = m_surfaceCreatorIdList[next];
|
||||
m_surfacePositionList[index] = m_surfacePositionList[next];
|
||||
m_surfaceNormalList[index] = m_surfaceNormalList[next];
|
||||
m_surfaceWeightsList[index] = m_surfaceWeightsList[next];
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
m_surfaceCreatorIdList.resize(index);
|
||||
m_surfacePositionList.resize(index);
|
||||
m_surfaceNormalList.resize(index);
|
||||
m_surfaceWeightsList.resize(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <SurfaceData/Utility/SurfaceDataUtility.h>
|
||||
#include <SurfaceData/SurfacePointList.h>
|
||||
|
||||
namespace SurfaceData
|
||||
{
|
||||
size_t SurfacePointList::GetInPositionIndexFromPosition(const AZ::Vector3& inPosition) const
|
||||
{
|
||||
// Given an input position, find the input position index that's associated with it.
|
||||
// We'll bias towards always having a position that's the same or further in our input list than before,
|
||||
// so we'll do a linear search that starts with the last input position we used, and goes forward (and wraps around)
|
||||
// until we've searched them all.
|
||||
// Our expectation is that most of the time, we'll only have to compare 0-1 input positions.
|
||||
|
||||
size_t inPositionIndex = m_lastInputPositionIndex;
|
||||
bool foundMatch = false;
|
||||
for (size_t indexCounter = 0; indexCounter < m_inputPositions.size(); indexCounter++)
|
||||
{
|
||||
if (m_inputPositions[inPositionIndex] == inPosition)
|
||||
{
|
||||
foundMatch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
inPositionIndex = (inPositionIndex + 1) % m_inputPositions.size();
|
||||
}
|
||||
|
||||
AZ_Assert(foundMatch, "Couldn't find input position!");
|
||||
m_lastInputPositionIndex = inPositionIndex;
|
||||
return inPositionIndex;
|
||||
}
|
||||
|
||||
size_t SurfacePointList::GetSurfacePointStartIndexFromInPositionIndex(size_t inPositionIndex) const
|
||||
{
|
||||
// Index to the first output surface point for this input position.
|
||||
return inPositionIndex * m_maxSurfacePointsPerInput;
|
||||
}
|
||||
|
||||
SurfacePointList::SurfacePointList(AZStd::span<const AzFramework::SurfaceData::SurfacePoint> surfacePoints)
|
||||
{
|
||||
// Construct and finalize the list with the set of passed-in surface points.
|
||||
// This is primarily a convenience for unit tests.
|
||||
StartListConstruction(surfacePoints);
|
||||
EndListConstruction();
|
||||
}
|
||||
|
||||
void SurfacePointList::StartListConstruction(AZStd::span<const AzFramework::SurfaceData::SurfacePoint> surfacePoints)
|
||||
{
|
||||
// Construct the list with the set of passed-in surface points but don't finalize it.
|
||||
// This is primarily a convenience for unit tests that want to test surface modifiers with specific inputs.
|
||||
|
||||
surfacePoints.begin();
|
||||
StartListConstruction(AZStd::span<const AZ::Vector3>(&(surfacePoints.begin()->m_position), 1), surfacePoints.size(), {});
|
||||
|
||||
for (auto& point : surfacePoints)
|
||||
{
|
||||
SurfaceTagWeights weights(point.m_surfaceTags);
|
||||
AddSurfacePoint(AZ::EntityId(), point.m_position, point.m_position, point.m_normal, weights);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfacePointList::StartListConstruction(
|
||||
AZStd::span<const AZ::Vector3> inPositions, size_t maxPointsPerInput, AZStd::span<const SurfaceTag> filterTags)
|
||||
{
|
||||
AZ_Assert(!m_listIsBeingConstructed, "Trying to start list construction on a list currently under construction.");
|
||||
AZ_Assert(m_surfacePositionList.empty(), "Trying to reserve space on a list that is already being used.");
|
||||
|
||||
Clear();
|
||||
|
||||
m_listIsBeingConstructed = true;
|
||||
|
||||
// Save off working references to the data we'll need during list construction.
|
||||
// These references need to remain valid during construction, but not afterwards.
|
||||
m_filterTags = filterTags;
|
||||
m_inputPositions = inPositions;
|
||||
m_inputPositionSize = inPositions.size();
|
||||
m_maxSurfacePointsPerInput = maxPointsPerInput;
|
||||
|
||||
size_t outputReserveSize = inPositions.size() * m_maxSurfacePointsPerInput;
|
||||
|
||||
// Reserve enough space to have one value per input position, and initialize it to 0.
|
||||
m_numSurfacePointsPerInput.resize(m_inputPositionSize);
|
||||
|
||||
// Reserve enough space to have maxSurfacePointsPerInput entries per input position, and initialize them all to 0.
|
||||
m_sortedSurfacePointIndices.resize(outputReserveSize);
|
||||
|
||||
// Reserve enough space for all our possible output surface points, but don't initialize them.
|
||||
m_surfaceCreatorIdList.reserve(outputReserveSize);
|
||||
m_surfacePositionList.reserve(outputReserveSize);
|
||||
m_surfaceNormalList.reserve(outputReserveSize);
|
||||
m_surfaceWeightsList.reserve(outputReserveSize);
|
||||
}
|
||||
|
||||
void SurfacePointList::Clear()
|
||||
{
|
||||
m_listIsBeingConstructed = false;
|
||||
|
||||
m_lastInputPositionIndex = 0;
|
||||
m_inputPositionSize = 0;
|
||||
m_maxSurfacePointsPerInput = 0;
|
||||
|
||||
m_filterTags = {};
|
||||
m_inputPositions = {};
|
||||
|
||||
m_sortedSurfacePointIndices.clear();
|
||||
m_numSurfacePointsPerInput.clear();
|
||||
m_surfacePositionList.clear();
|
||||
m_surfaceNormalList.clear();
|
||||
m_surfaceWeightsList.clear();
|
||||
m_surfaceCreatorIdList.clear();
|
||||
|
||||
m_surfacePointBounds = AZ::Aabb::CreateNull();
|
||||
}
|
||||
|
||||
void SurfacePointList::AddSurfacePoint(
|
||||
const AZ::EntityId& entityId, const AZ::Vector3& inPosition,
|
||||
const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceTagWeights& masks)
|
||||
{
|
||||
AZ_Assert(m_listIsBeingConstructed, "Trying to add surface points to a SurfacePointList that isn't under construction.");
|
||||
|
||||
// Find the inPositionIndex that matches the inPosition.
|
||||
size_t inPositionIndex = GetInPositionIndexFromPosition(inPosition);
|
||||
|
||||
// Find the first SurfacePoint that either matches the inPosition, or that starts the range for the next inPosition after this one.
|
||||
size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inPositionIndex);
|
||||
|
||||
// When adding a surface point, we'll either merge it with a similar existing point, or else add it in order of
|
||||
// decreasing Z, so that our final results are sorted.
|
||||
size_t surfacePointInsertIndex = surfacePointStartIndex;
|
||||
|
||||
for (; surfacePointInsertIndex < (surfacePointStartIndex + m_numSurfacePointsPerInput[inPositionIndex]); ++surfacePointInsertIndex)
|
||||
{
|
||||
// (Someday we should add a configurable tolerance for comparison)
|
||||
if (m_surfacePositionList[m_sortedSurfacePointIndices[surfacePointInsertIndex]].IsClose(position) &&
|
||||
m_surfaceNormalList[m_sortedSurfacePointIndices[surfacePointInsertIndex]].IsClose(normal))
|
||||
{
|
||||
// consolidate points with similar attributes by adding masks/weights to the similar point instead of adding a new one.
|
||||
m_surfaceWeightsList[m_sortedSurfacePointIndices[surfacePointInsertIndex]].AddSurfaceTagWeights(masks);
|
||||
return;
|
||||
}
|
||||
else if (m_surfacePositionList[m_sortedSurfacePointIndices[surfacePointInsertIndex]].GetZ() < position.GetZ())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we've made it here, we're adding the point, not merging it.
|
||||
|
||||
// Verify we aren't adding more points than expected.
|
||||
AZ_Assert(m_numSurfacePointsPerInput[inPositionIndex] < m_maxSurfacePointsPerInput, "Adding too many surface points.");
|
||||
|
||||
// Expand our output AABB to include this point.
|
||||
m_surfacePointBounds.AddPoint(position);
|
||||
|
||||
// If this isn't the first output for this input position, shift our sorted indices for this input position to make room for
|
||||
// the new entry.
|
||||
if (m_numSurfacePointsPerInput[inPositionIndex] > 0)
|
||||
{
|
||||
size_t startIndex = surfacePointInsertIndex;
|
||||
size_t endIndex = surfacePointStartIndex + m_numSurfacePointsPerInput[inPositionIndex];
|
||||
|
||||
AZStd::move_backward(
|
||||
m_sortedSurfacePointIndices.begin() + startIndex, m_sortedSurfacePointIndices.begin() + endIndex,
|
||||
m_sortedSurfacePointIndices.begin() + endIndex + 1);
|
||||
}
|
||||
|
||||
m_numSurfacePointsPerInput[inPositionIndex]++;
|
||||
|
||||
// Insert the new sorted index that references into our storage vectors.
|
||||
m_sortedSurfacePointIndices[surfacePointInsertIndex] = m_surfacePositionList.size();
|
||||
|
||||
// Add the new point to the back of our storage vectors.
|
||||
m_surfacePositionList.emplace_back(position);
|
||||
m_surfaceNormalList.emplace_back(normal);
|
||||
m_surfaceWeightsList.emplace_back(masks);
|
||||
m_surfaceCreatorIdList.emplace_back(entityId);
|
||||
}
|
||||
|
||||
void SurfacePointList::ModifySurfaceWeights(
|
||||
const AZ::EntityId& currentEntityId,
|
||||
AZStd::function<void(const AZ::Vector3& position, SurfaceData::SurfaceTagWeights& surfaceWeights)> modificationWeightCallback)
|
||||
{
|
||||
AZ_Assert(m_listIsBeingConstructed, "Trying to modify surface weights on a SurfacePointList that isn't under construction.");
|
||||
|
||||
// For every valid output point, call the modification callback only if it doesn't match the entity that created the point.
|
||||
for (size_t inputIndex = 0; (inputIndex < m_inputPositionSize); inputIndex++)
|
||||
{
|
||||
size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputIndex);
|
||||
for (size_t index = surfacePointStartIndex; (index < (surfacePointStartIndex + m_numSurfacePointsPerInput[inputIndex]));
|
||||
index++)
|
||||
{
|
||||
if (m_surfaceCreatorIdList[m_sortedSurfacePointIndices[index]] != currentEntityId)
|
||||
{
|
||||
modificationWeightCallback(
|
||||
m_surfacePositionList[m_sortedSurfacePointIndices[index]],
|
||||
m_surfaceWeightsList[m_sortedSurfacePointIndices[index]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SurfacePointList::FilterPoints(AZStd::span<const SurfaceTag> desiredTags)
|
||||
{
|
||||
AZ_Assert(m_listIsBeingConstructed, "Trying to filter a SurfacePointList that isn't under construction.");
|
||||
|
||||
// Filter out any points that don't match our search tags.
|
||||
// This has to be done after the Surface Modifiers have processed the points, not at point insertion time, because
|
||||
// Surface Modifiers add tags to existing points.
|
||||
// The algorithm below is basically an "erase_if" that's operating across multiple storage vectors and using one level of
|
||||
// indirection to keep our sorted indices valid.
|
||||
// At some point we might want to consider modifying this to compact the final storage to the minimum needed.
|
||||
for (size_t inputIndex = 0; (inputIndex < m_inputPositionSize); inputIndex++)
|
||||
{
|
||||
size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputIndex);
|
||||
size_t listSize = (surfacePointStartIndex + m_numSurfacePointsPerInput[inputIndex]);
|
||||
size_t index = surfacePointStartIndex;
|
||||
for (; index < listSize; index++)
|
||||
{
|
||||
if (!m_surfaceWeightsList[m_sortedSurfacePointIndices[index]].HasAnyMatchingTags(desiredTags))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index != listSize)
|
||||
{
|
||||
size_t next = index + 1;
|
||||
for (; next < listSize; ++next)
|
||||
{
|
||||
if (m_surfaceWeightsList[m_sortedSurfacePointIndices[index]].HasAnyMatchingTags(desiredTags))
|
||||
{
|
||||
m_sortedSurfacePointIndices[index] = AZStd::move(m_sortedSurfacePointIndices[next]);
|
||||
m_surfaceCreatorIdList[m_sortedSurfacePointIndices[index]] =
|
||||
AZStd::move(m_surfaceCreatorIdList[m_sortedSurfacePointIndices[next]]);
|
||||
m_surfacePositionList[m_sortedSurfacePointIndices[index]] =
|
||||
AZStd::move(m_surfacePositionList[m_sortedSurfacePointIndices[next]]);
|
||||
m_surfaceNormalList[m_sortedSurfacePointIndices[index]] =
|
||||
AZStd::move(m_surfaceNormalList[m_sortedSurfacePointIndices[next]]);
|
||||
m_surfaceWeightsList[m_sortedSurfacePointIndices[index]] =
|
||||
AZStd::move(m_surfaceWeightsList[m_sortedSurfacePointIndices[next]]);
|
||||
|
||||
m_numSurfacePointsPerInput[inputIndex]--;
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SurfacePointList::EndListConstruction()
|
||||
{
|
||||
AZ_Assert(m_listIsBeingConstructed, "Trying to end list construction on a SurfacePointList that isn't under construction.");
|
||||
|
||||
// Now that we've finished adding and modifying points, filter out any points that don't match the filterTags list, if we have one.
|
||||
if (!m_filterTags.empty())
|
||||
{
|
||||
FilterPoints(m_filterTags);
|
||||
}
|
||||
|
||||
m_listIsBeingConstructed = false;
|
||||
m_inputPositions = {};
|
||||
m_filterTags = {};
|
||||
}
|
||||
|
||||
bool SurfacePointList::IsEmpty() const
|
||||
{
|
||||
AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
|
||||
|
||||
return m_surfacePositionList.empty();
|
||||
}
|
||||
|
||||
bool SurfacePointList::IsEmpty(size_t inputPositionIndex) const
|
||||
{
|
||||
AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
|
||||
|
||||
return (m_inputPositionSize == 0) || (m_numSurfacePointsPerInput[inputPositionIndex] == 0);
|
||||
}
|
||||
|
||||
size_t SurfacePointList::GetSize() const
|
||||
{
|
||||
AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
|
||||
|
||||
return m_surfacePositionList.size();
|
||||
}
|
||||
|
||||
size_t SurfacePointList::GetSize(size_t inputPositionIndex) const
|
||||
{
|
||||
AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
|
||||
|
||||
return (m_inputPositionSize == 0) ? 0 : (m_numSurfacePointsPerInput[inputPositionIndex]);
|
||||
}
|
||||
|
||||
void SurfacePointList::EnumeratePoints(
|
||||
size_t inputPositionIndex,
|
||||
AZStd::function<bool(const AZ::Vector3&, const AZ::Vector3&, const SurfaceData::SurfaceTagWeights&)>
|
||||
pointCallback) const
|
||||
{
|
||||
AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
|
||||
|
||||
size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputPositionIndex);
|
||||
for (size_t index = surfacePointStartIndex;
|
||||
(index < (surfacePointStartIndex + m_numSurfacePointsPerInput[inputPositionIndex])); index++)
|
||||
{
|
||||
if (!pointCallback(
|
||||
m_surfacePositionList[m_sortedSurfacePointIndices[index]], m_surfaceNormalList[m_sortedSurfacePointIndices[index]],
|
||||
m_surfaceWeightsList[m_sortedSurfacePointIndices[index]]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SurfacePointList::EnumeratePoints(
|
||||
AZStd::function<bool(size_t inputPositionIndex, const AZ::Vector3&, const AZ::Vector3&, const SurfaceData::SurfaceTagWeights&)>
|
||||
pointCallback) const
|
||||
{
|
||||
AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
|
||||
|
||||
for (size_t inputIndex = 0; (inputIndex < m_inputPositionSize); inputIndex++)
|
||||
{
|
||||
size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputIndex);
|
||||
for (size_t index = surfacePointStartIndex; (index < (surfacePointStartIndex + m_numSurfacePointsPerInput[inputIndex]));
|
||||
index++)
|
||||
{
|
||||
if (!pointCallback(
|
||||
inputIndex, m_surfacePositionList[m_sortedSurfacePointIndices[index]],
|
||||
m_surfaceNormalList[m_sortedSurfacePointIndices[index]], m_surfaceWeightsList[m_sortedSurfacePointIndices[index]]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint SurfacePointList::GetHighestSurfacePoint([[maybe_unused]] size_t inputPositionIndex) const
|
||||
{
|
||||
AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
|
||||
|
||||
if (m_numSurfacePointsPerInput[inputPositionIndex] == 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputPositionIndex);
|
||||
AzFramework::SurfaceData::SurfacePoint point;
|
||||
point.m_position = m_surfacePositionList[m_sortedSurfacePointIndices[surfacePointStartIndex]];
|
||||
point.m_normal = m_surfaceNormalList[m_sortedSurfacePointIndices[surfacePointStartIndex]];
|
||||
point.m_surfaceTags = m_surfaceWeightsList[m_sortedSurfacePointIndices[surfacePointStartIndex]].GetSurfaceTagWeightList();
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user