Files
o3de/Code/Framework/Tests/OctreeTests.cpp
T
Tommy Walton 838202873a Fix for ATOM-15595 : OctreeNode silently evicts entries that are larger than the size of the root node (#870)
- Fixed a bug where an entry would get removed from the octree when being updated if it was too large to be fully contained by the root node (the desired behavior is that it just lives in the root node)
- Added a unit test to ensure that large entries can exist in the root node
- Updated the unit tests to manually count the number of entries instead of relying on GetEntryCount, since GetEntryCount was reporting an unreliable count before this bug was fixed.
2021-05-24 10:10:50 -07:00

428 lines
21 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/Console.h>
#include <AzCore/Name/NameDictionary.h>
#include <AzCore/Console/IConsole.h>
#include <AzFramework/Visibility/OctreeSystemComponent.h>
#include <random>
using namespace AzFramework;
namespace UnitTest
{
class OctreeTests
: public AllocatorsFixture
{
public:
void SetUp() override
{
// Create the SystemAllocator if not available
if (!AZ::AllocatorInstance<AZ::SystemAllocator>::IsReady())
{
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
m_ownsSystemAllocator = true;
}
m_console = aznew AZ::Console();
AZ::Interface<AZ::IConsole>::Register(m_console);
m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
m_console->GetCvarValue("bg_octreeNodeMaxEntries", m_savedMaxEntries);
m_console->GetCvarValue("bg_octreeNodeMinEntries", m_savedMinEntries);
m_console->GetCvarValue("bg_octreeMaxWorldExtents", m_savedBounds);
// To ease unit testing, configure the octreeSystemComponent to only allow one entry per node
m_console->PerformCommand("bg_octreeNodeMaxEntries 1");
m_console->PerformCommand("bg_octreeNodeMinEntries 1");
m_console->PerformCommand("bg_octreeMaxWorldExtents 1"); // Create a -1,-1,-1 to 1,1,1 world volume
if (!AZ::NameDictionary::IsReady())
{
AZ::NameDictionary::Create();
}
m_octreeSystemComponent = new OctreeSystemComponent;
IVisibilityScene* visScene = m_octreeSystemComponent->CreateVisibilityScene(AZ::Name("OctreeUnitTestScene"));
m_octreeScene = azdynamic_cast<OctreeScene*>(visScene);
}
void TearDown() override
{
//Restore octreeSystemComponent cvars for any future tests or benchmarks that might get executed
AZStd::string commandString;
commandString.format("bg_octreeNodeMaxEntries %u", m_savedMaxEntries);
m_console->PerformCommand(commandString.c_str());
commandString.format("bg_octreeNodeMinEntries %u", m_savedMinEntries);
m_console->PerformCommand(commandString.c_str());
commandString.format("bg_octreeMaxWorldExtents %f", m_savedBounds);
m_console->PerformCommand(commandString.c_str());
m_octreeSystemComponent->DestroyVisibilityScene(m_octreeScene);
delete m_octreeSystemComponent;
m_octreeSystemComponent = nullptr;
AZ::NameDictionary::Destroy();
AZ::Interface<AZ::IConsole>::Unregister(m_console);
delete m_console;
m_console = nullptr;
// Destroy system allocator only if it was created by this environment
if (m_ownsSystemAllocator)
{
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
m_ownsSystemAllocator = false;
}
}
bool m_ownsSystemAllocator = false;
OctreeSystemComponent* m_octreeSystemComponent = nullptr;
OctreeScene* m_octreeScene = nullptr;
uint32_t m_savedMaxEntries = 0;
uint32_t m_savedMinEntries = 0;
float m_savedBounds = 0.0f;
AZ::Console* m_console;
};
void ValidateEntryCountEqualsExpectedCount(const IVisibilityScene* visScene, uint32_t expectedEntryCount)
{
// InsertOrUpdateEntry assumes that updating an existing entry won't change the count
// so it doesn't modify the counter used by GetEntryCount.
// If an entry is removed from the octree as an unintended side effect of updating an existing entry,
// GetEntryCount can't be relied upon to report the actual entry count.
// So manually count the entries when using the entry count for validation.
uint32_t manualEntryCount = 0;
visScene->EnumerateNoCull([&manualEntryCount](const AzFramework::IVisibilityScene::NodeData& nodeData) { manualEntryCount += nodeData.m_entries.size(); });
EXPECT_EQ(manualEntryCount, expectedEntryCount);
EXPECT_EQ(visScene->GetEntryCount(), expectedEntryCount);
}
TEST_F(OctreeTests, InsertDeleteSingleEntry)
{
AzFramework::VisibilityEntry visEntry;
visEntry.m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3::CreateZero(), AZ::Vector3::CreateOne());
m_octreeScene->InsertOrUpdateEntry(visEntry);
EXPECT_TRUE(visEntry.m_internalNode != nullptr);
EXPECT_TRUE(visEntry.m_internalNodeIndex == 0);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
m_octreeScene->RemoveEntry(visEntry);
EXPECT_TRUE(visEntry.m_internalNode == nullptr);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 0);
EXPECT_TRUE(true); //TEST
}
TEST_F(OctreeTests, InsertDeleteSplitMerge)
{
AzFramework::VisibilityEntry visEntry[3];
visEntry[0].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.9f), AZ::Vector3(-0.6f));
visEntry[1].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.1f), AZ::Vector3( 0.4f));
visEntry[2].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.6f), AZ::Vector3( 0.9f));
m_octreeScene->InsertOrUpdateEntry(visEntry[0]);
EXPECT_TRUE(visEntry[0].m_internalNode != nullptr);
EXPECT_TRUE(visEntry[0].m_internalNodeIndex == 0);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
m_octreeScene->InsertOrUpdateEntry(visEntry[1]); // This should force a split of the root node
EXPECT_TRUE(visEntry[1].m_internalNode != nullptr);
EXPECT_TRUE(visEntry[1].m_internalNodeIndex == 0);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 2);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + m_octreeScene->GetChildNodeCount());
m_octreeScene->InsertOrUpdateEntry(visEntry[2]); // This should force a split of the roots +/+/+ child node
EXPECT_TRUE(visEntry[2].m_internalNode != nullptr);
EXPECT_TRUE(visEntry[2].m_internalNodeIndex == 0);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 3);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + (2 * m_octreeScene->GetChildNodeCount()));
m_octreeScene->RemoveEntry(visEntry[2]);
EXPECT_TRUE(visEntry[2].m_internalNode == nullptr);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 2);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + m_octreeScene->GetChildNodeCount());
m_octreeScene->RemoveEntry(visEntry[1]);
EXPECT_TRUE(visEntry[1].m_internalNode == nullptr);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
m_octreeScene->RemoveEntry(visEntry[0]);
EXPECT_TRUE(visEntry[0].m_internalNode == nullptr);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 0);
}
TEST_F(OctreeTests, UpdateSingleEntry)
{
AzFramework::VisibilityEntry visEntry;
visEntry.m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3::CreateZero(), AZ::Vector3::CreateOne());
m_octreeScene->InsertOrUpdateEntry(visEntry);
EXPECT_TRUE(visEntry.m_internalNode != nullptr);
EXPECT_TRUE(visEntry.m_internalNodeIndex == 0);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
visEntry.m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
m_octreeScene->InsertOrUpdateEntry(visEntry);
EXPECT_TRUE(visEntry.m_internalNode != nullptr);
EXPECT_TRUE(visEntry.m_internalNodeIndex == 0);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
m_octreeScene->RemoveEntry(visEntry);
EXPECT_TRUE(visEntry.m_internalNode == nullptr);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 0);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
}
TEST_F(OctreeTests, UpdateSplitMerge)
{
AzFramework::VisibilityEntry visEntry[3];
visEntry[0].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.9f), AZ::Vector3(-0.6f));
visEntry[1].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.1f), AZ::Vector3( 0.4f));
visEntry[2].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.6f), AZ::Vector3( 0.9f));
m_octreeScene->InsertOrUpdateEntry(visEntry[0]);
EXPECT_TRUE(visEntry[0].m_internalNode != nullptr);
EXPECT_TRUE(visEntry[0].m_internalNodeIndex == 0);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
m_octreeScene->InsertOrUpdateEntry(visEntry[1]); // This should force a split of the root node
EXPECT_TRUE(visEntry[1].m_internalNode != nullptr);
EXPECT_TRUE(visEntry[1].m_internalNodeIndex == 0);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 2);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + m_octreeScene->GetChildNodeCount());
m_octreeScene->InsertOrUpdateEntry(visEntry[2]); // This should force a split of the roots +/+/+ child node
EXPECT_TRUE(visEntry[2].m_internalNode != nullptr);
EXPECT_TRUE(visEntry[2].m_internalNodeIndex == 0);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 3);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + (2 * m_octreeScene->GetChildNodeCount()));
visEntry[1].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.9f), AZ::Vector3(-0.6f));
visEntry[2].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.1f), AZ::Vector3( 0.4f));
visEntry[0].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.6f), AZ::Vector3( 0.9f));
m_octreeScene->InsertOrUpdateEntry(visEntry[0]);
m_octreeScene->InsertOrUpdateEntry(visEntry[1]);
m_octreeScene->InsertOrUpdateEntry(visEntry[2]);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 3);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + (2 * m_octreeScene->GetChildNodeCount()));
m_octreeScene->RemoveEntry(visEntry[2]);
EXPECT_TRUE(visEntry[2].m_internalNode == nullptr);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 2);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + m_octreeScene->GetChildNodeCount());
m_octreeScene->RemoveEntry(visEntry[1]);
EXPECT_TRUE(visEntry[1].m_internalNode == nullptr);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
m_octreeScene->RemoveEntry(visEntry[0]);
EXPECT_TRUE(visEntry[0].m_internalNode == nullptr);
ValidateEntryCountEqualsExpectedCount(m_octreeScene, 0);
EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
}
void AppendEntries(AZStd::vector<VisibilityEntry*>& gatheredEntries, const AzFramework::IVisibilityScene::NodeData& nodeData)
{
gatheredEntries.insert(gatheredEntries.end(), nodeData.m_entries.begin(), nodeData.m_entries.end());
}
template <typename BoundType>
void EnumerateSingleEntryHelper(IVisibilityScene* visScene, const BoundType& bounds)
{
AzFramework::VisibilityEntry visEntry;
visEntry.m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3::CreateZero(), AZ::Vector3::CreateOne());
AZStd::vector<VisibilityEntry*> gatheredEntries;
visScene->Enumerate(bounds, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
EXPECT_TRUE(gatheredEntries.empty());
visScene->InsertOrUpdateEntry(visEntry);
visScene->Enumerate(bounds, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
EXPECT_TRUE(gatheredEntries.size() == 1);
EXPECT_TRUE(gatheredEntries[0] == &visEntry);
visEntry.m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
visScene->InsertOrUpdateEntry(visEntry);
gatheredEntries.clear();
visScene->Enumerate(bounds, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
EXPECT_TRUE(gatheredEntries.size() == 1);
EXPECT_TRUE(gatheredEntries[0] == &visEntry);
visScene->RemoveEntry(visEntry);
gatheredEntries.clear();
visScene->Enumerate(bounds, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
EXPECT_TRUE(gatheredEntries.empty());
}
TEST_F(OctreeTests, EnumerateSphereSingleEntry)
{
AZ::Sphere bounds = AZ::Sphere::CreateUnitSphere();
EnumerateSingleEntryHelper(m_octreeScene, bounds);
}
TEST_F(OctreeTests, EnumerateAabbSingleEntry)
{
AZ::Aabb bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3(1.0f));
EnumerateSingleEntryHelper(m_octreeScene, bounds);
}
TEST_F(OctreeTests, EnumerateFrustumSingleEntry)
{
AZ::Vector3 frustumOrigin = AZ::Vector3(0.0f, -2.0f, 0.0f);
AZ::Quaternion frustumDirection = AZ::Quaternion::CreateIdentity();
AZ::Transform frustumTransform = AZ::Transform::CreateFromQuaternionAndTranslation(frustumDirection, frustumOrigin);
AZ::Frustum bounds = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(0.5f), 1.0f, 3.0f));
EnumerateSingleEntryHelper(m_octreeScene, bounds);
}
// bound1 should cover the entire spatial hash
// bound2 should not cross into the positive Y-axis
// bound3 should only intersect the region inside 0.6, 0.6, 0.6 to 0.9, 0.9, 0.9
template <typename BoundType>
void EnumerateMultipleEntriesHelper(IVisibilityScene* visScene, const BoundType& bound1, const BoundType& bound2, const BoundType& bound3)
{
AZStd::vector<VisibilityEntry*> gatheredEntries;
AzFramework::VisibilityEntry visEntry[3];
visEntry[0].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.9f), AZ::Vector3(-0.6f));
visEntry[1].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.1f), AZ::Vector3( 0.4f));
visEntry[2].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.6f), AZ::Vector3( 0.9f));
visScene->InsertOrUpdateEntry(visEntry[0]);
visScene->InsertOrUpdateEntry(visEntry[1]);
visScene->InsertOrUpdateEntry(visEntry[2]);
gatheredEntries.clear();
visScene->Enumerate(bound1, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
EXPECT_TRUE(gatheredEntries.size() == 3);
gatheredEntries.clear();
visScene->Enumerate(bound2, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
EXPECT_TRUE(gatheredEntries.size() == 1);
EXPECT_TRUE(gatheredEntries[0] == &(visEntry[0]));
gatheredEntries.clear();
visScene->Enumerate(bound3, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
EXPECT_TRUE(gatheredEntries.size() == 1);
EXPECT_TRUE(gatheredEntries[0] == &(visEntry[2]));
visEntry[1].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.9f), AZ::Vector3(-0.6f));
visEntry[2].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.1f), AZ::Vector3( 0.4f));
visEntry[0].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.6f), AZ::Vector3( 0.9f));
visScene->InsertOrUpdateEntry(visEntry[0]);
visScene->InsertOrUpdateEntry(visEntry[1]);
visScene->InsertOrUpdateEntry(visEntry[2]);
gatheredEntries.clear();
visScene->Enumerate(bound1, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
EXPECT_TRUE(gatheredEntries.size() == 3);
gatheredEntries.clear();
visScene->Enumerate(bound2, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
EXPECT_TRUE(gatheredEntries.size() == 1);
EXPECT_TRUE(gatheredEntries[0] == &(visEntry[1]));
gatheredEntries.clear();
visScene->Enumerate(bound3, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
EXPECT_TRUE(gatheredEntries.size() == 1);
EXPECT_TRUE(gatheredEntries[0] == &(visEntry[0]));
visScene->RemoveEntry(visEntry[0]);
visScene->RemoveEntry(visEntry[1]);
visScene->RemoveEntry(visEntry[2]);
gatheredEntries.clear();
visScene->Enumerate(bound1, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
EXPECT_TRUE(gatheredEntries.empty());
}
TEST_F(OctreeTests, EnumerateSphereMultipleEntries)
{
AZ::Sphere bound1 = AZ::Sphere::CreateUnitSphere();
AZ::Sphere bound2 = AZ::Sphere(AZ::Vector3(-0.5f), 0.5f);
AZ::Sphere bound3 = AZ::Sphere(AZ::Vector3(0.75f), 0.2f);
EnumerateMultipleEntriesHelper(m_octreeScene, bound1, bound2, bound3);
}
TEST_F(OctreeTests, EnumerateAabbMultipleEntries)
{
AZ::Aabb bound1 = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3( 1.0f));
AZ::Aabb bound2 = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3(-0.5f));
AZ::Aabb bound3 = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.6f), AZ::Vector3( 0.9f));
EnumerateMultipleEntriesHelper(m_octreeScene, bound1, bound2, bound3);
}
TEST_F(OctreeTests, EnumerateFrustumMultipleEntries)
{
AZ::Vector3 frustumOrigin = AZ::Vector3(0.0f, -2.0f, 0.0f);
AZ::Quaternion frustumDirection = AZ::Quaternion::CreateIdentity();
AZ::Transform frustumTransform = AZ::Transform::CreateFromQuaternionAndTranslation(frustumDirection, frustumOrigin);
AZ::Frustum bound1 = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(0.5f), 1.0f, 3.0f));
AZ::Frustum bound2 = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(0.5f), 1.0f, 2.0f));
AZ::Frustum bound3 = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(0.5f), 2.6f, 2.9f));
EnumerateMultipleEntriesHelper(m_octreeScene, bound1, bound2, bound3);
}
TEST_F(OctreeTests, InsertOrUpdateEntry_OverFillRootNodeWithLargeEntries_EntriesAreNotLost)
{
// Validate that the octree works if you exceed the max entry count with large entries,
// which will overfill the root node since they can't be distributed to child nodes
// Get the max extents and entries-per-node for the octree
AZ::IConsole* console = AZ::Interface<AZ::IConsole>::Get();
EXPECT_TRUE(console);
float maxExtents = 0.0f;
AZ::GetValueResult getCvarResult = console->GetCvarValue("bg_octreeMaxWorldExtents", maxExtents);
EXPECT_EQ(getCvarResult, AZ::GetValueResult::Success);
uint32_t maxEntriesPerNode = 0;
getCvarResult = console->GetCvarValue("bg_octreeNodeMaxEntries", maxEntriesPerNode);
EXPECT_EQ(getCvarResult, AZ::GetValueResult::Success);
// Create root entries that would exceed the size of the root node
AZ::Aabb exceedMaxExtents = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-maxExtents - 1.0f), AZ::Vector3(maxExtents + 1.0f));
uint32_t exceedMaxEntriesPerNode = maxEntriesPerNode + 1;
AzFramework::VisibilityEntry visEntry;
visEntry.m_boundingVolume = exceedMaxExtents;
AZStd::vector<AzFramework::VisibilityEntry> visEntries(exceedMaxEntriesPerNode, visEntry);
// Insert them all into the scene
for (AzFramework::VisibilityEntry& entry : visEntries)
{
m_octreeScene->InsertOrUpdateEntry(entry);
}
// Expect all the entries to be in the scene
ValidateEntryCountEqualsExpectedCount(m_octreeScene, visEntries.size());
// Update them, without making any actual changes
for (AzFramework::VisibilityEntry& entry : visEntries)
{
m_octreeScene->InsertOrUpdateEntry(entry);
}
// Expect all the entries to be in the scene
ValidateEntryCountEqualsExpectedCount(m_octreeScene, visEntries.size());
}
}