EditorNavigationMeshTest.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  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 <MockInterfaces.h>
  9. #include <RecastNavigationEditorSystemComponent.h>
  10. #include <API/EditorPythonConsoleBus.h>
  11. #include <AzCore/Component/ComponentApplication.h>
  12. #include <AzCore/Component/Entity.h>
  13. #include <AzCore/Console/Console.h>
  14. #include <AzCore/EBus/EventSchedulerSystemComponent.h>
  15. #include <AzCore/std/smart_ptr/unique_ptr.h>
  16. #include <AzCore/UnitTest/TestTypes.h>
  17. #include <AzCore/UnitTest/Mocks/MockITime.h>
  18. #include <AzFramework/Entity/EntityDebugDisplayBus.h>
  19. #include <AzFramework/Physics/PhysicsScene.h>
  20. #include <Components/DetourNavigationComponent.h>
  21. #include <Components/RecastNavigationMeshComponent.h>
  22. #include <Components/RecastNavigationPhysXProviderComponent.h>
  23. #include <EditorComponents/EditorDetourNavigationComponent.h>
  24. #include <EditorComponents/EditorRecastNavigationMeshComponent.h>
  25. #include <EditorComponents/EditorRecastNavigationPhysXProviderComponent.h>
  26. #include <PhysX/MockPhysicsShape.h>
  27. #include <PhysX/MockSceneInterface.h>
  28. #include <PhysX/MockSimulatedBody.h>
  29. namespace RecastNavigation
  30. {
  31. using testing::_;
  32. using testing::Invoke;
  33. using testing::NiceMock;
  34. using testing::Return;
  35. using AZStd::unique_ptr;
  36. using AZ::Entity;
  37. using AZ::EventSchedulerSystemComponent;
  38. using RecastNavigation::RecastNavigationMeshRequestBus;
  39. using RecastNavigation::RecastNavigationMeshRequests;
  40. using RecastNavigation::RecastNavigationDebugDraw;
  41. using RecastNavigation::DetourNavigationRequests;
  42. using RecastNavigation::NavMeshQuery;
  43. using RecastNavigation::DetourNavigationComponent;
  44. using RecastNavigation::DetourNavigationRequestBus;
  45. using RecastNavigationTests::Wait;
  46. using RecastNavigationTests::MockShapeComponent;
  47. class EditorNavigationTest
  48. : public UnitTest::LeakDetectionFixture
  49. {
  50. public:
  51. unique_ptr<AZ::SerializeContext> m_sc;
  52. unique_ptr<AZ::BehaviorContext> m_bc;
  53. unique_ptr<AZStd::vector<AZ::ComponentDescriptor*>> m_descriptors;
  54. unique_ptr<AZ::MockTimeSystem> m_timeSystem;
  55. unique_ptr<UnitTest::MockSceneInterface> m_mockSceneInterface;
  56. unique_ptr<AzPhysics::SceneQueryHit> m_hit;
  57. unique_ptr<UnitTest::MockPhysicsShape> m_mockPhysicsShape;
  58. unique_ptr<UnitTest::MockSimulatedBody> m_mockSimulatedBody;
  59. unique_ptr<AZ::Console> m_console;
  60. void SetUp() override
  61. {
  62. UnitTest::LeakDetectionFixture::SetUp();
  63. m_console.reset(aznew AZ::Console());
  64. AZ::Interface<AZ::IConsole>::Register(m_console.get());
  65. // register components involved in testing
  66. m_descriptors = AZStd::make_unique<AZStd::vector<AZ::ComponentDescriptor*>>();
  67. m_sc = AZStd::make_unique<AZ::SerializeContext>();
  68. m_sc->CreateEditContext();
  69. m_bc = AZStd::make_unique<AZ::BehaviorContext>();
  70. RegisterComponent<RecastNavigationMeshComponent>();
  71. RegisterComponent<EditorRecastNavigationMeshComponent>();
  72. RegisterComponent<RecastNavigationPhysXProviderComponent>();
  73. RegisterComponent<EditorRecastNavigationPhysXProviderComponent>();
  74. RegisterComponent<DetourNavigationComponent>();
  75. RegisterComponent<EditorDetourNavigationComponent>();
  76. RegisterComponent<MockShapeComponent>();
  77. RegisterComponent<EventSchedulerSystemComponent>();
  78. RegisterComponent<RecastNavigationEditorSystemComponent>();
  79. m_timeSystem = AZStd::make_unique<NiceMock<AZ::MockTimeSystem>>();
  80. m_mockSceneInterface = AZStd::make_unique<NiceMock<UnitTest::MockSceneInterface>>();
  81. m_hit = AZStd::make_unique<AzPhysics::SceneQueryHit>();
  82. m_mockPhysicsShape = AZStd::make_unique<NiceMock<UnitTest::MockPhysicsShape>>();
  83. m_mockSimulatedBody = AZStd::make_unique<NiceMock<UnitTest::MockSimulatedBody>>();
  84. }
  85. void TearDown() override
  86. {
  87. m_mockSimulatedBody.reset();
  88. m_mockPhysicsShape.reset();
  89. m_hit.reset();
  90. m_mockSceneInterface.reset();
  91. m_timeSystem.reset();
  92. for (AZ::ComponentDescriptor* descriptor : *m_descriptors)
  93. {
  94. delete descriptor;
  95. }
  96. m_descriptors.reset();
  97. m_sc.reset();
  98. m_bc.reset();
  99. AZ::Interface<AZ::IConsole>::Unregister(m_console.get());
  100. m_console.reset();
  101. UnitTest::LeakDetectionFixture::TearDown();
  102. }
  103. template <typename T>
  104. void RegisterComponent()
  105. {
  106. AZ::ComponentDescriptor* item = T::CreateDescriptor();
  107. item->Reflect(m_sc.get());
  108. item->Reflect(m_bc.get());
  109. m_descriptors->push_back(item);
  110. }
  111. // helper method
  112. void PopulateEntity(Entity& e)
  113. {
  114. e.SetId(AZ::EntityId{ 1 });
  115. e.CreateComponent<EventSchedulerSystemComponent>();
  116. e.CreateComponent<RecastNavigationEditorSystemComponent>();
  117. m_mockShapeComponent = e.CreateComponent<MockShapeComponent>();
  118. e.CreateComponent<EditorRecastNavigationPhysXProviderComponent>(RecastNavigationPhysXProviderConfig{});
  119. e.CreateComponent<EditorDetourNavigationComponent>();
  120. m_editorRecastNavigationMeshComponent = e.CreateComponent<EditorRecastNavigationMeshComponent>(RecastNavigationMeshConfig{});
  121. m_editorRecastNavigationMeshComponent->SetEditorPreview(true);
  122. }
  123. void SetupNavigationMesh()
  124. {
  125. m_hit->m_resultFlags = AzPhysics::SceneQuery::EntityId;
  126. m_hit->m_entityId = AZ::EntityId{ 1 };
  127. m_hit->m_shape = m_mockPhysicsShape.get();
  128. // Fake result when querying PhysX world.
  129. ON_CALL(*m_mockSceneInterface, QueryScene(_, _)).WillByDefault(Invoke([this]
  130. (AzPhysics::SceneHandle, const AzPhysics::SceneQueryRequest* request)
  131. {
  132. const AzPhysics::OverlapRequest* overlapRequest = static_cast<const AzPhysics::OverlapRequest*>(request);
  133. overlapRequest->m_unboundedOverlapHitCallback({ *m_hit });
  134. return AzPhysics::SceneQueryHits();
  135. }));
  136. // Fake a simulated body within query results.
  137. ON_CALL(*m_mockSceneInterface, GetSimulatedBodyFromHandle(_, _)).WillByDefault(Invoke([this]
  138. (AzPhysics::SceneHandle, AzPhysics::SimulatedBodyHandle)
  139. {
  140. return m_mockSimulatedBody.get();
  141. }));
  142. // Provide a position and an orientation of a simulated body.
  143. ON_CALL(*m_mockSimulatedBody, GetOrientation()).WillByDefault(Return(AZ::Quaternion::CreateIdentity()));
  144. ON_CALL(*m_mockSimulatedBody, GetPosition()).WillByDefault(Return(AZ::Vector3::CreateZero()));
  145. }
  146. void ActivateEntity(Entity& e)
  147. {
  148. // Bring the entity online
  149. e.Init();
  150. e.Activate();
  151. }
  152. MockShapeComponent* m_mockShapeComponent = nullptr;
  153. EditorRecastNavigationMeshComponent* m_editorRecastNavigationMeshComponent = nullptr;
  154. // Test data
  155. void AddTestGeometry(AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, bool indexed = true)
  156. {
  157. constexpr float size = 2.5f;
  158. const AZStd::vector<AZ::Vector3> boxVertices = {
  159. AZ::Vector3(-size, -size, -size),
  160. AZ::Vector3(size, -size, -size) ,
  161. AZ::Vector3(size, size, -size) ,
  162. AZ::Vector3(-size, size, -size) ,
  163. AZ::Vector3(-size, -size, size) ,
  164. AZ::Vector3(size, -size, size) ,
  165. AZ::Vector3(size, size, size) ,
  166. AZ::Vector3(-size, size, size)
  167. };
  168. vertices.clear();
  169. vertices.insert(vertices.begin(), boxVertices.begin(), boxVertices.end());
  170. indices.clear();
  171. if (indexed)
  172. {
  173. const AZStd::vector<AZ::u32> boxIndices = {
  174. /*0*/ 2, /*1*/ 1, /*2*/ 0,
  175. /*3*/ 0, /*4*/ 3, /*5*/ 2,
  176. /*6*/ 3, /*7*/ 0, /*8*/ 7,
  177. /*9*/ 0, /*10*/ 4, /*11*/ 7,
  178. /*12*/ 0, /*13*/ 1, /*14*/ 5,
  179. /*15*/ 0, /*16*/ 5, /*17*/ 4,
  180. /*18*/ 1, /*19*/ 2, /*20*/ 5,
  181. /*21*/ 6, /*22*/ 5, /*23*/ 2,
  182. /*24*/ 7, /*25*/ 2, /*26*/ 3,
  183. /*27*/ 7, /*28*/ 6, /*29*/ 2,
  184. /*30*/ 7, /*31*/ 4, /*32*/ 5,
  185. /*33*/ 7, /*34*/ 5, /*35*/ 6,
  186. };
  187. indices.insert(indices.begin(), boxIndices.begin(), boxIndices.end());
  188. indices.push_back(2);
  189. indices.push_back(1);
  190. indices.push_back(0);
  191. }
  192. }
  193. void SetEditorMeshConfig(EditorRecastNavigationMeshComponent* component, bool autoUpdate)
  194. {
  195. component->SetEditorPreview(autoUpdate);
  196. component->OnConfigurationChanged();
  197. }
  198. void Tick(float time = 0.1f)
  199. {
  200. AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, time, AZ::ScriptTimePoint{});
  201. }
  202. void AddTestGeometry(bool indexed)
  203. {
  204. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this, indexed]
  205. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  206. {
  207. AddTestGeometry(vertices, indices, indexed);
  208. }));
  209. }
  210. };
  211. TEST_F(EditorNavigationTest, InEditorUpdateTick)
  212. {
  213. Entity e;
  214. PopulateEntity(e);
  215. ActivateEntity(e);
  216. SetupNavigationMesh();
  217. AddTestGeometry(true);
  218. const Wait wait(AZ::EntityId(1));
  219. m_editorRecastNavigationMeshComponent->OnEditorUpdateTick();
  220. wait.BlockUntilCalled();
  221. EXPECT_EQ(wait.m_updatedCalls, 1);
  222. }
  223. TEST_F(EditorNavigationTest, InEditorDebugDrawTick)
  224. {
  225. Entity e;
  226. PopulateEntity(e);
  227. ActivateEntity(e);
  228. SetupNavigationMesh();
  229. AddTestGeometry(true);
  230. const Wait wait(AZ::EntityId(1));
  231. m_editorRecastNavigationMeshComponent->OnEditorUpdateTick();
  232. wait.BlockUntilCalled();
  233. EXPECT_EQ(wait.m_updatedCalls, 1);
  234. Tick();
  235. }
  236. TEST_F(EditorNavigationTest, InEditorDebugDrawTickStopDebugDraw)
  237. {
  238. Entity e;
  239. PopulateEntity(e);
  240. ActivateEntity(e);
  241. SetupNavigationMesh();
  242. AddTestGeometry(true);
  243. const Wait wait(AZ::EntityId(1));
  244. m_editorRecastNavigationMeshComponent->OnEditorUpdateTick();
  245. wait.BlockUntilCalled();
  246. EXPECT_EQ(wait.m_updatedCalls, 1);
  247. Tick();
  248. SetEditorMeshConfig(m_editorRecastNavigationMeshComponent, false);
  249. }
  250. TEST_F(EditorNavigationTest, InEditorSecondRun)
  251. {
  252. Entity e;
  253. PopulateEntity(e);
  254. ActivateEntity(e);
  255. SetupNavigationMesh();
  256. AddTestGeometry(true);
  257. ON_CALL(*m_timeSystem, GetElapsedTimeMs()).WillByDefault(Return(AZ::TimeMs{ 1500 }));
  258. {
  259. const Wait wait(AZ::EntityId(1));
  260. Tick();
  261. wait.BlockUntilCalled();
  262. EXPECT_EQ(wait.m_updatedCalls, 1);
  263. }
  264. // Advance time forward.
  265. ON_CALL(*m_timeSystem, GetElapsedTimeMs()).WillByDefault(Return(AZ::TimeMs{ 3500 }));
  266. {
  267. const Wait wait(AZ::EntityId(1));
  268. Tick();
  269. wait.BlockUntilCalled();
  270. EXPECT_EQ(wait.m_updatedCalls, 1);
  271. }
  272. }
  273. TEST_F(EditorNavigationTest, InEditorEmptyWorld)
  274. {
  275. Entity e;
  276. PopulateEntity(e);
  277. ActivateEntity(e);
  278. SetupNavigationMesh();
  279. const Wait wait(AZ::EntityId(1));
  280. m_editorRecastNavigationMeshComponent->OnEditorUpdateTick();
  281. wait.BlockUntilCalled();
  282. EXPECT_EQ(wait.m_updatedCalls, 1);
  283. }
  284. TEST_F(EditorNavigationTest, DeactivateRightAfterUpdateEvent)
  285. {
  286. Entity e;
  287. PopulateEntity(e);
  288. ActivateEntity(e);
  289. SetupNavigationMesh();
  290. AddTestGeometry(true);
  291. const Wait wait(AZ::EntityId(1));
  292. m_editorRecastNavigationMeshComponent->OnEditorUpdateTick();
  293. wait.BlockUntilNavigationMeshRecalculating(AZ::TimeMs{ 100 });
  294. EXPECT_EQ(wait.m_recalculatingCalls, 1);
  295. e.Deactivate(); // The expectation is that that update is running on a thread as we deactivate here.
  296. wait.BlockUntilCalled(AZ::TimeMs{ 100 });
  297. EXPECT_EQ(wait.m_updatedCalls, 0);
  298. }
  299. TEST_F(EditorNavigationTest, BuildGameEntityFromEditorRecastNavigationPhysXProviderComponent)
  300. {
  301. Entity inEntity;
  302. auto* inComponent = inEntity.CreateComponent<EditorRecastNavigationPhysXProviderComponent>();
  303. Entity outEntity;
  304. inComponent->BuildGameEntity(&outEntity);
  305. EXPECT_NE(outEntity.FindComponent<RecastNavigation::RecastNavigationPhysXProviderComponent>(), nullptr);
  306. }
  307. TEST_F(EditorNavigationTest, BuildGameEntityFromEditorRecastNavigationMeshComponent)
  308. {
  309. Entity inEntity;
  310. auto* inComponent = inEntity.CreateComponent<EditorRecastNavigationMeshComponent>();
  311. Entity outEntity;
  312. inComponent->BuildGameEntity(&outEntity);
  313. EXPECT_NE(outEntity.FindComponent<RecastNavigation::RecastNavigationMeshComponent>(), nullptr);
  314. }
  315. TEST_F(EditorNavigationTest, BuildGameEntityFromEditorDetourNavigationComponent)
  316. {
  317. Entity inEntity;
  318. auto* inComponent = inEntity.CreateComponent<EditorDetourNavigationComponent>();
  319. Entity outEntity;
  320. inComponent->BuildGameEntity(&outEntity);
  321. EXPECT_NE(outEntity.FindComponent<DetourNavigationComponent>(), nullptr);
  322. }
  323. TEST_F(EditorNavigationTest, ActivateDeactivateThenTickToPreviewEditor)
  324. {
  325. Entity e;
  326. PopulateEntity(e);
  327. ActivateEntity(e);
  328. SetupNavigationMesh();
  329. AddTestGeometry(true);
  330. e.Deactivate();
  331. e.Activate();
  332. ON_CALL(*m_timeSystem, GetElapsedTimeMs()).WillByDefault(Return(AZ::TimeMs{ 1500 }));
  333. {
  334. const Wait wait(AZ::EntityId(1));
  335. Tick();
  336. wait.BlockUntilCalled();
  337. EXPECT_EQ(wait.m_updatedCalls, 1);
  338. }
  339. }
  340. TEST_F(EditorNavigationTest, ActivateRunThenDeactivateThenTickToPreviewEditor)
  341. {
  342. Entity e;
  343. PopulateEntity(e);
  344. ActivateEntity(e);
  345. SetupNavigationMesh();
  346. AddTestGeometry(true);
  347. ON_CALL(*m_timeSystem, GetElapsedTimeMs()).WillByDefault(Return(AZ::TimeMs{ 1500 }));
  348. {
  349. const Wait wait(AZ::EntityId(1));
  350. Tick();
  351. wait.BlockUntilCalled();
  352. EXPECT_EQ(wait.m_updatedCalls, 1);
  353. }
  354. e.Deactivate();
  355. e.Activate();
  356. // Advance time forward.
  357. ON_CALL(*m_timeSystem, GetElapsedTimeMs()).WillByDefault(Return(AZ::TimeMs{ 3500 }));
  358. {
  359. const Wait wait(AZ::EntityId(1));
  360. Tick();
  361. wait.BlockUntilCalled();
  362. EXPECT_EQ(wait.m_updatedCalls, 1);
  363. }
  364. }
  365. TEST_F(EditorNavigationTest, DeactivateRightAfterRecalculatingEventThenActivateAndPreviewEditor)
  366. {
  367. Entity e;
  368. PopulateEntity(e);
  369. ActivateEntity(e);
  370. SetupNavigationMesh();
  371. AddTestGeometry(true);
  372. ON_CALL(*m_timeSystem, GetElapsedTimeMs()).WillByDefault(Return(AZ::TimeMs{ 1500 }));
  373. {
  374. const Wait wait(AZ::EntityId(1));
  375. Tick();
  376. wait.BlockUntilNavigationMeshRecalculating(AZ::TimeMs{ 100 });
  377. EXPECT_EQ(wait.m_recalculatingCalls, 1);
  378. }
  379. e.Deactivate(); // The expectation is that that update is running on a thread as we deactivate here.
  380. e.Activate();
  381. // Advance time forward.
  382. ON_CALL(*m_timeSystem, GetElapsedTimeMs()).WillByDefault(Return(AZ::TimeMs{ 3500 }));
  383. {
  384. const Wait wait(AZ::EntityId(1));
  385. Tick();
  386. wait.BlockUntilCalled();
  387. EXPECT_EQ(wait.m_updatedCalls, 1);
  388. }
  389. }
  390. TEST_F(EditorNavigationTest, StartAsyncThenChangedNavigationMeshSettings)
  391. {
  392. Entity e;
  393. PopulateEntity(e);
  394. ActivateEntity(e);
  395. SetupNavigationMesh();
  396. AddTestGeometry(true);
  397. ON_CALL(*m_timeSystem, GetElapsedTimeMs()).WillByDefault(Return(AZ::TimeMs{ 1500 }));
  398. {
  399. const Wait wait(AZ::EntityId(1));
  400. Tick();
  401. wait.BlockUntilNavigationMeshRecalculating(AZ::TimeMs{ 100 });
  402. EXPECT_EQ(wait.m_recalculatingCalls, 1);
  403. }
  404. // This forces a rebuild of the navigation mesh as the configuration changed.
  405. m_editorRecastNavigationMeshComponent->OnConfigurationChanged();
  406. e.Deactivate();
  407. }
  408. TEST_F(EditorNavigationTest, AsyncThenChangeSettingsThenAsyncAgain)
  409. {
  410. Entity e;
  411. PopulateEntity(e);
  412. ActivateEntity(e);
  413. SetupNavigationMesh();
  414. AddTestGeometry(true);
  415. ON_CALL(*m_timeSystem, GetElapsedTimeMs()).WillByDefault(Return(AZ::TimeMs{ 1500 }));
  416. {
  417. const Wait wait(AZ::EntityId(1));
  418. Tick();
  419. wait.BlockUntilNavigationMeshRecalculating(AZ::TimeMs{ 100 });
  420. EXPECT_EQ(wait.m_recalculatingCalls, 1);
  421. }
  422. // This forces a rebuild of the navigation mesh as the configuration changed.
  423. m_editorRecastNavigationMeshComponent->OnConfigurationChanged();
  424. {
  425. const Wait wait(AZ::EntityId(1));
  426. Tick();
  427. wait.BlockUntilCalled(AZ::TimeMs{ 100 });
  428. EXPECT_EQ(wait.m_updatedCalls, 0);
  429. }
  430. }
  431. }