SurfacePoint data structure encapsulations (#7413)

* First pass at encapsulating SurfacePointList.
The biggest challenge in optimizing SurfacePointList(s) usage is the overall memory management associated with it. There are M surface points with N surface mask entries created for every input point, which leads to a lot of container reallocation and memory shuffling when processing multiple input points. By encapsulating the list, it should become easier to preallocate the entries, as well as keep "helper data" around for managing the bookkeeping to associate the input points with the output points.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Small fixes and TODO reminders.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Encapsulate surface point creation and separate EnumeratePoints out from modifications.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Start removing SurfacePoint from the exposed API.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Changed SurfacePointList to split out the surface point storage to allow for span<> usage over time.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Removed entity id

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Removed SurfacePoint from SurfaceData, changed all remaining uses to AzFramework::SurfaceData::SurfacePoint.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Encapsulated SurfaceTagWeightMap and renamed to SurfaceTagWeights.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Fixed make file.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Better commenting and parameter naming.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Renamed methods to be more descriptive.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>
This commit is contained in:
Mike Balfour
2022-02-04 11:27:59 -06:00
committed by GitHub
parent ff4412db7c
commit d9ba0af645
39 changed files with 949 additions and 543 deletions
@@ -26,7 +26,8 @@ namespace UnitTest
: public AzPhysics::SimulatedBodyComponentRequestsBus::Handler
{
public:
MockPhysicsWorldBusProvider(const AZ::EntityId& id, AZ::Vector3 inPosition, bool setHitResult, const SurfaceData::SurfacePoint& hitResult)
MockPhysicsWorldBusProvider(
const AZ::EntityId& id, AZ::Vector3 inPosition, bool setHitResult, const AzFramework::SurfaceData::SurfacePoint& hitResult)
{
AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(id);
@@ -77,51 +78,36 @@ namespace UnitTest
{
protected:
// Create a new SurfacePoint with the given fields.
SurfaceData::SurfacePoint CreateSurfacePoint(AZ::EntityId id, AZ::Vector3 position, AZ::Vector3 normal, AZStd::vector<AZStd::pair<AZStd::string, float>> tags)
AzFramework::SurfaceData::SurfacePoint CreateSurfacePoint(
AZ::Vector3 position, AZ::Vector3 normal, AZStd::vector<AZStd::pair<AZStd::string, float>> tags)
{
SurfaceData::SurfacePoint point;
point.m_entityId = id;
AzFramework::SurfaceData::SurfacePoint point;
point.m_position = position;
point.m_normal = normal;
for (auto& tag : tags)
{
point.m_masks[SurfaceData::SurfaceTag(tag.first)] = tag.second;
point.m_surfaceTags.emplace_back(SurfaceData::SurfaceTag(tag.first), tag.second);
}
return point;
}
// Compare two surface points.
bool SurfacePointsAreEqual(const SurfaceData::SurfacePoint& lhs, const SurfaceData::SurfacePoint& rhs)
bool SurfacePointsAreEqual(
const AZ::Vector3& lhsPosition,
const AZ::Vector3& lhsNormal,
const SurfaceData::SurfaceTagWeights& lhsMasks,
const AzFramework::SurfaceData::SurfacePoint& rhs)
{
if ((lhs.m_entityId != rhs.m_entityId)
|| (lhs.m_position != rhs.m_position)
|| (lhs.m_normal != rhs.m_normal)
|| (lhs.m_masks.size() != rhs.m_masks.size()))
{
return false;
}
for (auto& mask : lhs.m_masks)
{
auto maskEntry = rhs.m_masks.find(mask.first);
if (maskEntry == rhs.m_masks.end())
{
return false;
}
if (maskEntry->second != mask.second)
{
return false;
}
}
return true;
return ((lhsPosition == rhs.m_position)
&& (lhsNormal == rhs.m_normal)
&& (lhsMasks.SurfaceWeightsAreEqual(rhs.m_surfaceTags)));
}
// Common test function for testing the "Provider" functionality of the component.
// Given a set of tags and an expected output, check to see if the component provides the
// expected output point.
void TestSurfaceDataColliderProvider(AZStd::vector<AZStd::string> providerTags, bool pointOnProvider,
AZ::Vector3 queryPoint, const SurfaceData::SurfacePoint& expectedOutput)
AZ::Vector3 queryPoint, const AzFramework::SurfaceData::SurfacePoint& expectedOutput)
{
// This lets our component register with surfaceData successfully.
MockSurfaceDataSystem mockSurfaceDataSystem;
@@ -135,8 +121,6 @@ namespace UnitTest
// Create the test entity with the SurfaceDataCollider component and the required physics collider dependency
auto entity = CreateEntity();
// Initialize our Entity ID to the one passed in on the expectedOutput
entity->SetId(expectedOutput.m_entityId);
// Create the components
CreateComponent<MockPhysicsColliderComponent>(entity.get());
CreateComponent<SurfaceData::SurfaceDataColliderComponent>(entity.get(), config);
@@ -155,17 +139,25 @@ namespace UnitTest
queryPoint, pointList);
if (pointOnProvider)
{
ASSERT_TRUE(pointList.size() == 1);
EXPECT_TRUE(SurfacePointsAreEqual(pointList[0], expectedOutput));
ASSERT_EQ(pointList.GetSize(), 1);
pointList.EnumeratePoints(
[this, expectedOutput](
const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool
{
EXPECT_TRUE(SurfacePointsAreEqual(position, normal, masks, expectedOutput));
return true;
});
}
else
{
EXPECT_TRUE(pointList.empty());
EXPECT_TRUE(pointList.IsEmpty());
}
}
void TestSurfaceDataColliderModifier(AZStd::vector<AZStd::string> modifierTags,
const SurfaceData::SurfacePoint& input, bool pointInCollider, const SurfaceData::SurfacePoint& expectedOutput)
const AzFramework::SurfaceData::SurfacePoint& input,
bool pointInCollider,
const AzFramework::SurfaceData::SurfacePoint& expectedOutput)
{
// This lets our component register with surfaceData successfully.
MockSurfaceDataSystem mockSurfaceDataSystem;
@@ -191,11 +183,18 @@ namespace UnitTest
EXPECT_TRUE(modifierHandle != SurfaceData::InvalidSurfaceDataRegistryHandle);
// Call ModifySurfacePoints and verify the results
SurfaceData::SurfacePointList pointList;
pointList.emplace_back(input);
// Add the surface point with a different entity ID than the entity doing the modification, so that the point doesn't get
// filtered out.
SurfaceData::SurfacePointList pointList = { input };
SurfaceData::SurfaceDataModifierRequestBus::Event(modifierHandle, &SurfaceData::SurfaceDataModifierRequestBus::Events::ModifySurfacePoints, pointList);
ASSERT_TRUE(pointList.size() == 1);
EXPECT_TRUE(SurfacePointsAreEqual(pointList[0], expectedOutput));
ASSERT_EQ(pointList.GetSize(), 1);
pointList.EnumeratePoints(
[this, expectedOutput](
const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool
{
EXPECT_TRUE(SurfacePointsAreEqual(position, normal, masks, expectedOutput));
return true;
});
}
};
@@ -232,7 +231,8 @@ namespace UnitTest
// Set the expected output to an arbitrary entity ID, position, and normal.
// We'll use this to initialize the mock physics, so the output of the query should match.
const char* tag = "test_mask";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3::CreateAxisZ(),
AzFramework::SurfaceData::SurfacePoint expectedOutput =
CreateSurfacePoint(AZ::Vector3(1.0f), AZ::Vector3::CreateAxisZ(),
{ AZStd::make_pair<AZStd::string, float>(tag, 1.0f) });
// Query from the same XY, but one unit higher on Z, just so we can verify that the output returns the collision
@@ -248,7 +248,8 @@ namespace UnitTest
// Set the expected output to an arbitrary entity ID, position, and normal.
// We'll use this to initialize the mock physics.
const char* tag = "test_mask";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3::CreateAxisZ(),
AzFramework::SurfaceData::SurfacePoint expectedOutput =
CreateSurfacePoint(AZ::Vector3(1.0f), AZ::Vector3::CreateAxisZ(),
{ AZStd::make_pair<AZStd::string, float>(tag, 1.0f) });
// Query from the same XY, but one unit higher on Z. However, we're also telling our test to provide
@@ -266,9 +267,9 @@ namespace UnitTest
// We'll use this to initialize the mock physics.
const char* tag1 = "test_mask1";
const char* tag2 = "test_mask2";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3::CreateAxisZ(),
{ AZStd::make_pair<AZStd::string, float>(tag1, 1.0f),
AZStd::make_pair<AZStd::string, float>(tag2, 1.0f) });
AzFramework::SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(
AZ::Vector3(1.0f), AZ::Vector3::CreateAxisZ(),
{ AZStd::make_pair<AZStd::string, float>(tag1, 1.0f), AZStd::make_pair<AZStd::string, float>(tag2, 1.0f) });
// Query from the same XY, but one unit higher on Z, just so we can verify that the output returns the collision
// result, not the input point.
@@ -281,11 +282,12 @@ namespace UnitTest
// Verify that for a point inside the collider, the output point contains the correct tag and value.
// Set arbitrary input data
SurfaceData::SurfacePoint input = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3(0.0f), {});
AzFramework::SurfaceData::SurfacePoint input = CreateSurfacePoint(AZ::Vector3(1.0f), AZ::Vector3(0.0f), {});
// Output should match the input, but with an added tag / value
const char* tag = "test_mask";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(input.m_entityId, input.m_position, input.m_normal,
{ AZStd::make_pair<AZStd::string, float>(tag, 1.0f) });
AzFramework::SurfaceData::SurfacePoint expectedOutput =
CreateSurfacePoint(input.m_position, input.m_normal,
{ AZStd::make_pair<AZStd::string, float>(tag, 1.0f) });
constexpr bool pointInCollider = true;
TestSurfaceDataColliderModifier({ tag }, input, pointInCollider, expectedOutput);
@@ -296,10 +298,10 @@ namespace UnitTest
// Verify that for a point outside the collider, the output point contains no tags / values.
// Set arbitrary input data
SurfaceData::SurfacePoint input = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3(0.0f), {});
AzFramework::SurfaceData::SurfacePoint input = CreateSurfacePoint(AZ::Vector3(1.0f), AZ::Vector3(0.0f), {});
// Output should match the input - no extra tags / values should be added.
const char* tag = "test_mask";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(input.m_entityId, input.m_position, input.m_normal, {});
AzFramework::SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(input.m_position, input.m_normal, {});
constexpr bool pointInCollider = true;
TestSurfaceDataColliderModifier({ tag }, input, !pointInCollider, expectedOutput);
@@ -310,11 +312,12 @@ namespace UnitTest
// Verify that if the component has multiple tags, all of them get put on the output with the same value.
// Set arbitrary input data
SurfaceData::SurfacePoint input = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3(0.0f), {});
AzFramework::SurfaceData::SurfacePoint input = CreateSurfacePoint(AZ::Vector3(1.0f), AZ::Vector3(0.0f), {});
// Output should match the input, but with two added tags
const char* tag1 = "test_mask1";
const char* tag2 = "test_mask2";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(input.m_entityId, input.m_position, input.m_normal,
AzFramework::SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(
input.m_position, input.m_normal,
{ AZStd::make_pair<AZStd::string, float>(tag1, 1.0f), AZStd::make_pair<AZStd::string, float>(tag2, 1.0f) });
constexpr bool pointInCollider = true;
@@ -328,11 +331,13 @@ namespace UnitTest
// Set arbitrary input data
const char* preservedTag = "preserved_tag";
SurfaceData::SurfacePoint input = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3(0.0f),
AzFramework::SurfaceData::SurfacePoint input =
CreateSurfacePoint(AZ::Vector3(1.0f), AZ::Vector3(0.0f),
{ AZStd::make_pair<AZStd::string, float>(preservedTag, 1.0f) });
// Output should match the input, but with two added tags
const char* modifierTag = "modifier_tag";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(input.m_entityId, input.m_position, input.m_normal,
AzFramework::SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(
input.m_position, input.m_normal,
{ AZStd::make_pair<AZStd::string, float>(preservedTag, 1.0f), AZStd::make_pair<AZStd::string, float>(modifierTag, 1.0f) });
constexpr bool pointInCollider = true;
@@ -349,10 +354,12 @@ namespace UnitTest
float inputValue = 0.25f;
// Set arbitrary input data
SurfaceData::SurfacePoint input = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3(0.0f),
AzFramework::SurfaceData::SurfacePoint input =
CreateSurfacePoint(AZ::Vector3(1.0f), AZ::Vector3(0.0f),
{ AZStd::make_pair<AZStd::string, float>(tag, inputValue) });
// Output should match the input, except that the value on the tag gets the higher modifier value
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(input.m_entityId, input.m_position, input.m_normal,
AzFramework::SurfaceData::SurfacePoint expectedOutput =
CreateSurfacePoint(input.m_position, input.m_normal,
{ AZStd::make_pair<AZStd::string, float>(tag, 1.0f) });
constexpr bool pointInCollider = true;