OctreeTests.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/UnitTest/TestTypes.h>
  9. #include <AzCore/Console/IConsole.h>
  10. #include <AzCore/Console/Console.h>
  11. #include <AzCore/Name/NameDictionary.h>
  12. #include <AzCore/Console/IConsole.h>
  13. #include <AzCore/Math/MatrixUtils.h>
  14. #include <AzFramework/Visibility/OctreeSystemComponent.h>
  15. #include <random>
  16. using namespace AzFramework;
  17. namespace UnitTest
  18. {
  19. class OctreeTests
  20. : public LeakDetectionFixture
  21. {
  22. public:
  23. void SetUp() override
  24. {
  25. m_console = aznew AZ::Console();
  26. AZ::Interface<AZ::IConsole>::Register(m_console);
  27. m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
  28. m_console->GetCvarValue("bg_octreeNodeMaxEntries", m_savedMaxEntries);
  29. m_console->GetCvarValue("bg_octreeNodeMinEntries", m_savedMinEntries);
  30. m_console->GetCvarValue("bg_octreeMaxWorldExtents", m_savedBounds);
  31. // To ease unit testing, configure the octreeSystemComponent to only allow one entry per node
  32. m_console->PerformCommand("bg_octreeNodeMaxEntries 1");
  33. m_console->PerformCommand("bg_octreeNodeMinEntries 1");
  34. m_console->PerformCommand("bg_octreeMaxWorldExtents 1"); // Create a -1,-1,-1 to 1,1,1 world volume
  35. if (!AZ::NameDictionary::IsReady())
  36. {
  37. AZ::NameDictionary::Create();
  38. }
  39. m_octreeSystemComponent = new OctreeSystemComponent;
  40. IVisibilityScene* visScene = m_octreeSystemComponent->CreateVisibilityScene(AZ::Name("OctreeUnitTestScene"));
  41. m_octreeScene = azdynamic_cast<OctreeScene*>(visScene);
  42. }
  43. void TearDown() override
  44. {
  45. //Restore octreeSystemComponent cvars for any future tests or benchmarks that might get executed
  46. AZStd::string commandString;
  47. commandString.format("bg_octreeNodeMaxEntries %u", m_savedMaxEntries);
  48. m_console->PerformCommand(commandString.c_str());
  49. commandString.format("bg_octreeNodeMinEntries %u", m_savedMinEntries);
  50. m_console->PerformCommand(commandString.c_str());
  51. commandString.format("bg_octreeMaxWorldExtents %f", m_savedBounds);
  52. m_console->PerformCommand(commandString.c_str());
  53. m_octreeSystemComponent->DestroyVisibilityScene(m_octreeScene);
  54. delete m_octreeSystemComponent;
  55. m_octreeSystemComponent = nullptr;
  56. AZ::NameDictionary::Destroy();
  57. AZ::Interface<AZ::IConsole>::Unregister(m_console);
  58. delete m_console;
  59. m_console = nullptr;
  60. }
  61. OctreeSystemComponent* m_octreeSystemComponent = nullptr;
  62. OctreeScene* m_octreeScene = nullptr;
  63. uint32_t m_savedMaxEntries = 0;
  64. uint32_t m_savedMinEntries = 0;
  65. float m_savedBounds = 0.0f;
  66. AZ::Console* m_console;
  67. };
  68. void ValidateEntryCountEqualsExpectedCount(const IVisibilityScene* visScene, uint32_t expectedEntryCount)
  69. {
  70. // InsertOrUpdateEntry assumes that updating an existing entry won't change the count
  71. // so it doesn't modify the counter used by GetEntryCount.
  72. // If an entry is removed from the octree as an unintended side effect of updating an existing entry,
  73. // GetEntryCount can't be relied upon to report the actual entry count.
  74. // So manually count the entries when using the entry count for validation.
  75. size_t manualEntryCount = 0;
  76. visScene->EnumerateNoCull([&manualEntryCount](const AzFramework::IVisibilityScene::NodeData& nodeData) { manualEntryCount += nodeData.m_entries.size(); });
  77. EXPECT_EQ(manualEntryCount, expectedEntryCount);
  78. EXPECT_EQ(visScene->GetEntryCount(), expectedEntryCount);
  79. }
  80. TEST_F(OctreeTests, InsertDeleteSingleEntry)
  81. {
  82. AzFramework::VisibilityEntry visEntry;
  83. visEntry.m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3::CreateZero(), AZ::Vector3::CreateOne());
  84. m_octreeScene->InsertOrUpdateEntry(visEntry);
  85. EXPECT_TRUE(visEntry.m_internalNode != nullptr);
  86. EXPECT_TRUE(visEntry.m_internalNodeIndex == 0);
  87. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
  88. m_octreeScene->RemoveEntry(visEntry);
  89. EXPECT_TRUE(visEntry.m_internalNode == nullptr);
  90. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 0);
  91. EXPECT_TRUE(true); //TEST
  92. }
  93. TEST_F(OctreeTests, InsertDeleteSplitMerge)
  94. {
  95. AzFramework::VisibilityEntry visEntry[3];
  96. visEntry[0].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.9f), AZ::Vector3(-0.6f));
  97. visEntry[1].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.1f), AZ::Vector3( 0.4f));
  98. visEntry[2].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.6f), AZ::Vector3( 0.9f));
  99. m_octreeScene->InsertOrUpdateEntry(visEntry[0]);
  100. EXPECT_TRUE(visEntry[0].m_internalNode != nullptr);
  101. EXPECT_TRUE(visEntry[0].m_internalNodeIndex == 0);
  102. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
  103. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
  104. m_octreeScene->InsertOrUpdateEntry(visEntry[1]); // This should force a split of the root node
  105. EXPECT_TRUE(visEntry[1].m_internalNode != nullptr);
  106. EXPECT_TRUE(visEntry[1].m_internalNodeIndex == 0);
  107. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 2);
  108. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + m_octreeScene->GetChildNodeCount());
  109. m_octreeScene->InsertOrUpdateEntry(visEntry[2]); // This should force a split of the roots +/+/+ child node
  110. EXPECT_TRUE(visEntry[2].m_internalNode != nullptr);
  111. EXPECT_TRUE(visEntry[2].m_internalNodeIndex == 0);
  112. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 3);
  113. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + (2 * m_octreeScene->GetChildNodeCount()));
  114. m_octreeScene->RemoveEntry(visEntry[2]);
  115. EXPECT_TRUE(visEntry[2].m_internalNode == nullptr);
  116. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 2);
  117. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + m_octreeScene->GetChildNodeCount());
  118. m_octreeScene->RemoveEntry(visEntry[1]);
  119. EXPECT_TRUE(visEntry[1].m_internalNode == nullptr);
  120. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
  121. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
  122. m_octreeScene->RemoveEntry(visEntry[0]);
  123. EXPECT_TRUE(visEntry[0].m_internalNode == nullptr);
  124. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 0);
  125. }
  126. TEST_F(OctreeTests, UpdateSingleEntry)
  127. {
  128. AzFramework::VisibilityEntry visEntry;
  129. visEntry.m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3::CreateZero(), AZ::Vector3::CreateOne());
  130. m_octreeScene->InsertOrUpdateEntry(visEntry);
  131. EXPECT_TRUE(visEntry.m_internalNode != nullptr);
  132. EXPECT_TRUE(visEntry.m_internalNodeIndex == 0);
  133. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
  134. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
  135. visEntry.m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
  136. m_octreeScene->InsertOrUpdateEntry(visEntry);
  137. EXPECT_TRUE(visEntry.m_internalNode != nullptr);
  138. EXPECT_TRUE(visEntry.m_internalNodeIndex == 0);
  139. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
  140. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
  141. m_octreeScene->RemoveEntry(visEntry);
  142. EXPECT_TRUE(visEntry.m_internalNode == nullptr);
  143. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 0);
  144. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
  145. }
  146. TEST_F(OctreeTests, UpdateSplitMerge)
  147. {
  148. AzFramework::VisibilityEntry visEntry[3];
  149. visEntry[0].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.9f), AZ::Vector3(-0.6f));
  150. visEntry[1].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.1f), AZ::Vector3( 0.4f));
  151. visEntry[2].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.6f), AZ::Vector3( 0.9f));
  152. m_octreeScene->InsertOrUpdateEntry(visEntry[0]);
  153. EXPECT_TRUE(visEntry[0].m_internalNode != nullptr);
  154. EXPECT_TRUE(visEntry[0].m_internalNodeIndex == 0);
  155. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
  156. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
  157. m_octreeScene->InsertOrUpdateEntry(visEntry[1]); // This should force a split of the root node
  158. EXPECT_TRUE(visEntry[1].m_internalNode != nullptr);
  159. EXPECT_TRUE(visEntry[1].m_internalNodeIndex == 0);
  160. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 2);
  161. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + m_octreeScene->GetChildNodeCount());
  162. m_octreeScene->InsertOrUpdateEntry(visEntry[2]); // This should force a split of the roots +/+/+ child node
  163. EXPECT_TRUE(visEntry[2].m_internalNode != nullptr);
  164. EXPECT_TRUE(visEntry[2].m_internalNodeIndex == 0);
  165. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 3);
  166. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + (2 * m_octreeScene->GetChildNodeCount()));
  167. visEntry[1].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.9f), AZ::Vector3(-0.6f));
  168. visEntry[2].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.1f), AZ::Vector3( 0.4f));
  169. visEntry[0].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.6f), AZ::Vector3( 0.9f));
  170. m_octreeScene->InsertOrUpdateEntry(visEntry[0]);
  171. m_octreeScene->InsertOrUpdateEntry(visEntry[1]);
  172. m_octreeScene->InsertOrUpdateEntry(visEntry[2]);
  173. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 3);
  174. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + (2 * m_octreeScene->GetChildNodeCount()));
  175. m_octreeScene->RemoveEntry(visEntry[2]);
  176. EXPECT_TRUE(visEntry[2].m_internalNode == nullptr);
  177. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 2);
  178. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1 + m_octreeScene->GetChildNodeCount());
  179. m_octreeScene->RemoveEntry(visEntry[1]);
  180. EXPECT_TRUE(visEntry[1].m_internalNode == nullptr);
  181. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 1);
  182. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
  183. m_octreeScene->RemoveEntry(visEntry[0]);
  184. EXPECT_TRUE(visEntry[0].m_internalNode == nullptr);
  185. ValidateEntryCountEqualsExpectedCount(m_octreeScene, 0);
  186. EXPECT_TRUE(m_octreeScene->GetNodeCount() == 1);
  187. }
  188. void AppendEntries(AZStd::vector<VisibilityEntry*>& gatheredEntries, const AzFramework::IVisibilityScene::NodeData& nodeData)
  189. {
  190. gatheredEntries.insert(gatheredEntries.end(), nodeData.m_entries.begin(), nodeData.m_entries.end());
  191. }
  192. template <typename BoundType>
  193. void EnumerateSingleEntryHelper(IVisibilityScene* visScene, const BoundType& bounds)
  194. {
  195. AzFramework::VisibilityEntry visEntry;
  196. visEntry.m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3::CreateZero(), AZ::Vector3::CreateOne());
  197. AZStd::vector<VisibilityEntry*> gatheredEntries;
  198. visScene->Enumerate(bounds, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
  199. EXPECT_TRUE(gatheredEntries.empty());
  200. visScene->InsertOrUpdateEntry(visEntry);
  201. visScene->Enumerate(bounds, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
  202. EXPECT_TRUE(gatheredEntries.size() == 1);
  203. EXPECT_TRUE(gatheredEntries[0] == &visEntry);
  204. visEntry.m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
  205. visScene->InsertOrUpdateEntry(visEntry);
  206. gatheredEntries.clear();
  207. visScene->Enumerate(bounds, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
  208. EXPECT_TRUE(gatheredEntries.size() == 1);
  209. EXPECT_TRUE(gatheredEntries[0] == &visEntry);
  210. visScene->RemoveEntry(visEntry);
  211. gatheredEntries.clear();
  212. visScene->Enumerate(bounds, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
  213. EXPECT_TRUE(gatheredEntries.empty());
  214. }
  215. TEST_F(OctreeTests, EnumerateSphereSingleEntry)
  216. {
  217. AZ::Sphere bounds = AZ::Sphere::CreateUnitSphere();
  218. EnumerateSingleEntryHelper(m_octreeScene, bounds);
  219. }
  220. TEST_F(OctreeTests, EnumerateAabbSingleEntry)
  221. {
  222. AZ::Aabb bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3(1.0f));
  223. EnumerateSingleEntryHelper(m_octreeScene, bounds);
  224. }
  225. TEST_F(OctreeTests, EnumerateFrustumSingleEntry)
  226. {
  227. AZ::Vector3 frustumOrigin = AZ::Vector3(0.0f, -2.0f, 0.0f);
  228. AZ::Quaternion frustumDirection = AZ::Quaternion::CreateIdentity();
  229. AZ::Transform frustumTransform = AZ::Transform::CreateFromQuaternionAndTranslation(frustumDirection, frustumOrigin);
  230. AZ::Frustum bounds = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(0.5f), 1.0f, 3.0f));
  231. EnumerateSingleEntryHelper(m_octreeScene, bounds);
  232. }
  233. // bound1 should cover the entire spatial hash
  234. // bound2 should not cross into the positive Y-axis
  235. // bound3 should only intersect the region inside 0.6, 0.6, 0.6 to 0.9, 0.9, 0.9
  236. template <typename BoundType>
  237. void EnumerateMultipleEntriesHelper(IVisibilityScene* visScene, const BoundType& bound1, const BoundType& bound2, const BoundType& bound3)
  238. {
  239. AZStd::vector<VisibilityEntry*> gatheredEntries;
  240. AzFramework::VisibilityEntry visEntry[3];
  241. visEntry[0].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.9f), AZ::Vector3(-0.6f));
  242. visEntry[1].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.1f), AZ::Vector3( 0.4f));
  243. visEntry[2].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.6f), AZ::Vector3( 0.9f));
  244. visScene->InsertOrUpdateEntry(visEntry[0]);
  245. visScene->InsertOrUpdateEntry(visEntry[1]);
  246. visScene->InsertOrUpdateEntry(visEntry[2]);
  247. gatheredEntries.clear();
  248. visScene->Enumerate(bound1, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
  249. EXPECT_TRUE(gatheredEntries.size() == 3);
  250. gatheredEntries.clear();
  251. visScene->Enumerate(bound2, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
  252. EXPECT_TRUE(gatheredEntries.size() == 1);
  253. EXPECT_TRUE(gatheredEntries[0] == &(visEntry[0]));
  254. gatheredEntries.clear();
  255. visScene->Enumerate(bound3, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
  256. EXPECT_TRUE(gatheredEntries.size() == 1);
  257. EXPECT_TRUE(gatheredEntries[0] == &(visEntry[2]));
  258. visEntry[1].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.9f), AZ::Vector3(-0.6f));
  259. visEntry[2].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.1f), AZ::Vector3( 0.4f));
  260. visEntry[0].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.6f), AZ::Vector3( 0.9f));
  261. visScene->InsertOrUpdateEntry(visEntry[0]);
  262. visScene->InsertOrUpdateEntry(visEntry[1]);
  263. visScene->InsertOrUpdateEntry(visEntry[2]);
  264. gatheredEntries.clear();
  265. visScene->Enumerate(bound1, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
  266. EXPECT_TRUE(gatheredEntries.size() == 3);
  267. gatheredEntries.clear();
  268. visScene->Enumerate(bound2, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
  269. EXPECT_TRUE(gatheredEntries.size() == 1);
  270. EXPECT_TRUE(gatheredEntries[0] == &(visEntry[1]));
  271. gatheredEntries.clear();
  272. visScene->Enumerate(bound3, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
  273. EXPECT_TRUE(gatheredEntries.size() == 1);
  274. EXPECT_TRUE(gatheredEntries[0] == &(visEntry[0]));
  275. visScene->RemoveEntry(visEntry[0]);
  276. visScene->RemoveEntry(visEntry[1]);
  277. visScene->RemoveEntry(visEntry[2]);
  278. gatheredEntries.clear();
  279. visScene->Enumerate(bound1, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) { AppendEntries(gatheredEntries, nodeData); });
  280. EXPECT_TRUE(gatheredEntries.empty());
  281. }
  282. TEST_F(OctreeTests, EnumerateSphereMultipleEntries)
  283. {
  284. AZ::Sphere bound1 = AZ::Sphere::CreateUnitSphere();
  285. AZ::Sphere bound2 = AZ::Sphere(AZ::Vector3(-0.5f), 0.5f);
  286. AZ::Sphere bound3 = AZ::Sphere(AZ::Vector3(0.75f), 0.2f);
  287. EnumerateMultipleEntriesHelper(m_octreeScene, bound1, bound2, bound3);
  288. }
  289. TEST_F(OctreeTests, EnumerateAabbMultipleEntries)
  290. {
  291. AZ::Aabb bound1 = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3( 1.0f));
  292. AZ::Aabb bound2 = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3(-0.5f));
  293. AZ::Aabb bound3 = AZ::Aabb::CreateFromMinMax(AZ::Vector3( 0.6f), AZ::Vector3( 0.9f));
  294. EnumerateMultipleEntriesHelper(m_octreeScene, bound1, bound2, bound3);
  295. }
  296. TEST_F(OctreeTests, EnumerateFrustumMultipleEntries)
  297. {
  298. AZ::Vector3 frustumOrigin = AZ::Vector3(0.0f, -2.0f, 0.0f);
  299. AZ::Quaternion frustumDirection = AZ::Quaternion::CreateIdentity();
  300. AZ::Transform frustumTransform = AZ::Transform::CreateFromQuaternionAndTranslation(frustumDirection, frustumOrigin);
  301. AZ::Frustum bound1 = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(0.5f), 1.0f, 3.0f));
  302. AZ::Frustum bound2 = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(0.5f), 1.0f, 2.0f));
  303. AZ::Frustum bound3 = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(0.5f), 2.6f, 2.9f));
  304. EnumerateMultipleEntriesHelper(m_octreeScene, bound1, bound2, bound3);
  305. }
  306. TEST_F(OctreeTests, InsertOrUpdateEntry_OverFillRootNodeWithLargeEntries_EntriesAreNotLost)
  307. {
  308. // Validate that the octree works if you exceed the max entry count with large entries,
  309. // which will overfill the root node since they can't be distributed to child nodes
  310. // Get the max extents and entries-per-node for the octree
  311. AZ::IConsole* console = AZ::Interface<AZ::IConsole>::Get();
  312. EXPECT_TRUE(console);
  313. float maxExtents = 0.0f;
  314. AZ::GetValueResult getCvarResult = console->GetCvarValue("bg_octreeMaxWorldExtents", maxExtents);
  315. EXPECT_EQ(getCvarResult, AZ::GetValueResult::Success);
  316. uint32_t maxEntriesPerNode = 0;
  317. getCvarResult = console->GetCvarValue("bg_octreeNodeMaxEntries", maxEntriesPerNode);
  318. EXPECT_EQ(getCvarResult, AZ::GetValueResult::Success);
  319. // Create root entries that would exceed the size of the root node
  320. AZ::Aabb exceedMaxExtents = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-maxExtents - 1.0f), AZ::Vector3(maxExtents + 1.0f));
  321. uint32_t exceedMaxEntriesPerNode = maxEntriesPerNode + 1;
  322. AzFramework::VisibilityEntry visEntry;
  323. visEntry.m_boundingVolume = exceedMaxExtents;
  324. AZStd::vector<AzFramework::VisibilityEntry> visEntries(exceedMaxEntriesPerNode, visEntry);
  325. // Insert them all into the scene
  326. for (AzFramework::VisibilityEntry& entry : visEntries)
  327. {
  328. m_octreeScene->InsertOrUpdateEntry(entry);
  329. }
  330. // Expect all the entries to be in the scene
  331. ValidateEntryCountEqualsExpectedCount(m_octreeScene, static_cast<uint32_t>(visEntries.size()));
  332. // Update them, without making any actual changes
  333. for (AzFramework::VisibilityEntry& entry : visEntries)
  334. {
  335. m_octreeScene->InsertOrUpdateEntry(entry);
  336. }
  337. // Expect all the entries to be in the scene
  338. ValidateEntryCountEqualsExpectedCount(m_octreeScene, static_cast<uint32_t>(visEntries.size()));
  339. }
  340. TEST_F(OctreeTests, ExcludeFrustumTest)
  341. {
  342. // This test is made to be similar to EnumerateMultipleEntriesHelper, however needs to be
  343. // separate due to a different function signature.
  344. AZStd::vector<VisibilityEntry*> gatheredEntries;
  345. auto gatherEntries = [&](const AzFramework::IVisibilityScene::NodeData& nodeData)
  346. {
  347. AppendEntries(gatheredEntries, nodeData);
  348. };
  349. AzFramework::VisibilityEntry visEntry[3];
  350. visEntry[0].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.9f), AZ::Vector3(-0.6f));
  351. visEntry[1].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.1f), AZ::Vector3(0.4f));
  352. visEntry[2].m_boundingVolume = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.6f), AZ::Vector3(0.9f));
  353. m_octreeScene->InsertOrUpdateEntry(visEntry[0]);
  354. m_octreeScene->InsertOrUpdateEntry(visEntry[1]);
  355. m_octreeScene->InsertOrUpdateEntry(visEntry[2]);
  356. {
  357. // Covers entire -1 to 1 region of the octree
  358. AZ::Vector3 frustumOrigin = AZ::Vector3(0.0f, -2.0f, 0.0f);
  359. AZ::Quaternion frustumDirection = AZ::Quaternion::CreateIdentity();
  360. AZ::Transform frustumTransform = AZ::Transform::CreateFromQuaternionAndTranslation(frustumDirection, frustumOrigin);
  361. AZ::Frustum include = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(2.0f), 1.0f, 5.0f));
  362. AZ::Frustum exclude = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(0.5f), 3.0f, 5.0f));
  363. m_octreeScene->Enumerate(include, exclude, gatherEntries);
  364. EXPECT_EQ(gatheredEntries.size(), 3);
  365. gatheredEntries.clear();
  366. }
  367. {
  368. // Covers only on -y side
  369. AZ::Vector3 frustumOrigin = AZ::Vector3(0.0f, -2.0f, 0.0f);
  370. AZ::Quaternion frustumDirection = AZ::Quaternion::CreateIdentity();
  371. AZ::Transform frustumTransform = AZ::Transform::CreateFromQuaternionAndTranslation(frustumDirection, frustumOrigin);
  372. AZ::Frustum include = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(2.0f), 1.0f, 5.0f));
  373. AZ::Frustum exclude = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(0.5f), 2.0f, 5.0f));
  374. m_octreeScene->Enumerate(include, exclude, gatherEntries);
  375. EXPECT_EQ(gatheredEntries.size(), 1);
  376. gatheredEntries.clear();
  377. }
  378. {
  379. // Entire world inside exclusion frustum
  380. AZ::Vector3 frustumOrigin = AZ::Vector3(0.0f, -2.0f, 0.0f);
  381. AZ::Quaternion frustumDirection = AZ::Quaternion::CreateIdentity();
  382. AZ::Transform frustumTransform = AZ::Transform::CreateFromQuaternionAndTranslation(frustumDirection, frustumOrigin);
  383. AZ::Frustum include = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(2.0f), 1.0f, 5.0f));
  384. AZ::Frustum exclude = AZ::Frustum(AZ::ViewFrustumAttributes(frustumTransform, 1.0f, 2.0f * atanf(1.0f), 0.5f, 4.0f));
  385. m_octreeScene->Enumerate(include, exclude, gatherEntries);
  386. EXPECT_EQ(gatheredEntries.size(), 0);
  387. gatheredEntries.clear();
  388. }
  389. }
  390. }