NavigationMeshTest.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940
  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 <RecastNavigationSystemComponent.h>
  10. #include <AzCore/Component/ComponentApplication.h>
  11. #include <AzCore/Component/Entity.h>
  12. #include <AzCore/Console/Console.h>
  13. #include <AzCore/EBus/EventSchedulerSystemComponent.h>
  14. #include <AzCore/Name/NameDictionary.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 <PhysX/MockPhysicsShape.h>
  24. #include <PhysX/MockSceneInterface.h>
  25. #include <PhysX/MockSimulatedBody.h>
  26. namespace RecastNavigationTests
  27. {
  28. using testing::_;
  29. using testing::Invoke;
  30. using testing::NiceMock;
  31. using testing::Return;
  32. using AZStd::unique_ptr;
  33. using AZ::Entity;
  34. using AZ::EventSchedulerSystemComponent;
  35. using RecastNavigation::RecastNavigationMeshRequestBus;
  36. using RecastNavigation::RecastNavigationMeshRequests;
  37. using RecastNavigation::RecastNavigationDebugDraw;
  38. using RecastNavigation::DetourNavigationRequests;
  39. using RecastNavigation::NavMeshQuery;
  40. using RecastNavigation::DetourNavigationComponent;
  41. using RecastNavigation::DetourNavigationRequestBus;
  42. class NavigationTest
  43. : public ::UnitTest::LeakDetectionFixture
  44. {
  45. public:
  46. unique_ptr<AZ::SerializeContext> m_sc;
  47. unique_ptr<AZ::BehaviorContext> m_bc;
  48. unique_ptr<AZStd::vector<AZ::ComponentDescriptor*>> m_descriptors;
  49. unique_ptr<AZ::MockTimeSystem> m_timeSystem;
  50. unique_ptr<UnitTest::MockSceneInterface> m_mockSceneInterface;
  51. unique_ptr<AzPhysics::SceneQueryHit> m_hit;
  52. unique_ptr<UnitTest::MockPhysicsShape> m_mockPhysicsShape;
  53. unique_ptr<UnitTest::MockSimulatedBody> m_mockSimulatedBody;
  54. unique_ptr<AZ::Console> m_console;
  55. unique_ptr<AZ::NameDictionary> m_nameDictionary;
  56. void SetUp() override
  57. {
  58. ::UnitTest::LeakDetectionFixture::SetUp();
  59. m_console.reset(aznew AZ::Console());
  60. AZ::Interface<AZ::IConsole>::Register(m_console.get());
  61. m_nameDictionary = AZStd::make_unique<AZ::NameDictionary>();
  62. AZ::Interface<AZ::NameDictionary>::Register(m_nameDictionary.get());
  63. // register components involved in testing
  64. m_descriptors = AZStd::make_unique<AZStd::vector<AZ::ComponentDescriptor*>>();
  65. m_sc = AZStd::make_unique<AZ::SerializeContext>();
  66. m_sc->CreateEditContext();
  67. m_bc = AZStd::make_unique<AZ::BehaviorContext>();
  68. RegisterComponent<RecastNavigation::RecastNavigationMeshComponent>();
  69. RegisterComponent<RecastNavigation::RecastNavigationPhysXProviderComponent>();
  70. RegisterComponent<MockShapeComponent>();
  71. RegisterComponent<AZ::EventSchedulerSystemComponent>();
  72. RegisterComponent<RecastNavigation::RecastNavigationSystemComponent>();
  73. RegisterComponent<RecastNavigation::DetourNavigationComponent>();
  74. m_timeSystem = AZStd::make_unique<NiceMock<AZ::MockTimeSystem>>();
  75. m_mockSceneInterface = AZStd::make_unique<NiceMock<UnitTest::MockSceneInterface>>();
  76. m_hit = AZStd::make_unique<AzPhysics::SceneQueryHit>();
  77. m_mockPhysicsShape = AZStd::make_unique<NiceMock<UnitTest::MockPhysicsShape>>();
  78. m_mockSimulatedBody = AZStd::make_unique<NiceMock<UnitTest::MockSimulatedBody>>();
  79. }
  80. void TearDown() override
  81. {
  82. m_mockSimulatedBody = {};
  83. m_mockPhysicsShape = {};
  84. m_hit = {};
  85. m_mockSceneInterface = {};
  86. m_timeSystem = {};
  87. for (AZ::ComponentDescriptor* descriptor : *m_descriptors)
  88. {
  89. delete descriptor;
  90. }
  91. m_descriptors = {};
  92. m_sc = {};
  93. m_bc = {};
  94. AZ::Interface<AZ::NameDictionary>::Unregister(m_nameDictionary.get());
  95. m_nameDictionary.reset();
  96. AZ::Interface<AZ::IConsole>::Unregister(m_console.get());
  97. m_console = {};
  98. ::UnitTest::LeakDetectionFixture::TearDown();
  99. }
  100. template <typename T>
  101. void RegisterComponent()
  102. {
  103. AZ::ComponentDescriptor* item = T::CreateDescriptor();
  104. item->Reflect(m_sc.get());
  105. item->Reflect(m_bc.get());
  106. m_descriptors->push_back(item);
  107. }
  108. // helper method
  109. void PopulateEntity(AZ::Entity& e)
  110. {
  111. e.SetId(AZ::EntityId{ 1 });
  112. e.CreateComponent<AZ::EventSchedulerSystemComponent>();
  113. e.CreateComponent<RecastNavigation::RecastNavigationSystemComponent>();
  114. m_mockShapeComponent = e.CreateComponent<MockShapeComponent>();
  115. e.CreateComponent<RecastNavigation::RecastNavigationPhysXProviderComponent>();
  116. e.CreateComponent<RecastNavigation::RecastNavigationMeshComponent>(RecastNavigation::RecastNavigationMeshConfig{});
  117. }
  118. void SetupNavigationMesh()
  119. {
  120. m_hit->m_resultFlags = AzPhysics::SceneQuery::EntityId;
  121. m_hit->m_entityId = AZ::EntityId{ 1 };
  122. m_hit->m_shape = m_mockPhysicsShape.get();
  123. // Fake result when querying PhysX world.
  124. ON_CALL(*m_mockSceneInterface, QueryScene(_, _)).WillByDefault(Invoke([this]
  125. (AzPhysics::SceneHandle, const AzPhysics::SceneQueryRequest* request)
  126. {
  127. const AzPhysics::OverlapRequest* overlapRequest = static_cast<const AzPhysics::OverlapRequest*>(request);
  128. overlapRequest->m_unboundedOverlapHitCallback({ *m_hit });
  129. return AzPhysics::SceneQueryHits();
  130. }));
  131. // Fake a simulated body within query results.
  132. ON_CALL(*m_mockSceneInterface, GetSimulatedBodyFromHandle(_, _)).WillByDefault(Invoke([this]
  133. (AzPhysics::SceneHandle, AzPhysics::SimulatedBodyHandle)
  134. {
  135. return m_mockSimulatedBody.get();
  136. }));
  137. // Provide a position and an orientation of a simulated body.
  138. ON_CALL(*m_mockSimulatedBody, GetOrientation()).WillByDefault(Return(AZ::Quaternion::CreateIdentity()));
  139. ON_CALL(*m_mockSimulatedBody, GetPosition()).WillByDefault(Return(AZ::Vector3::CreateZero()));
  140. }
  141. void ActivateEntity(Entity& e)
  142. {
  143. // Bring the entity online
  144. e.Init();
  145. e.Activate();
  146. }
  147. MockShapeComponent* m_mockShapeComponent = nullptr;
  148. // Test data
  149. void AddTestGeometry(AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, bool indexed = true)
  150. {
  151. constexpr float size = 2.5f;
  152. const AZStd::vector<AZ::Vector3> boxVertices = {
  153. AZ::Vector3(-size, -size, -size),
  154. AZ::Vector3(size, -size, -size) ,
  155. AZ::Vector3(size, size, -size) ,
  156. AZ::Vector3(-size, size, -size) ,
  157. AZ::Vector3(-size, -size, size) ,
  158. AZ::Vector3(size, -size, size) ,
  159. AZ::Vector3(size, size, size) ,
  160. AZ::Vector3(-size, size, size)
  161. };
  162. vertices.clear();
  163. vertices.insert(vertices.begin(), boxVertices.begin(), boxVertices.end());
  164. indices.clear();
  165. if (indexed)
  166. {
  167. const AZStd::vector<AZ::u32> boxIndices = {
  168. /*0*/ 2, /*1*/ 1, /*2*/ 0,
  169. /*3*/ 0, /*4*/ 3, /*5*/ 2,
  170. /*6*/ 3, /*7*/ 0, /*8*/ 7,
  171. /*9*/ 0, /*10*/ 4, /*11*/ 7,
  172. /*12*/ 0, /*13*/ 1, /*14*/ 5,
  173. /*15*/ 0, /*16*/ 5, /*17*/ 4,
  174. /*18*/ 1, /*19*/ 2, /*20*/ 5,
  175. /*21*/ 6, /*22*/ 5, /*23*/ 2,
  176. /*24*/ 7, /*25*/ 2, /*26*/ 3,
  177. /*27*/ 7, /*28*/ 6, /*29*/ 2,
  178. /*30*/ 7, /*31*/ 4, /*32*/ 5,
  179. /*33*/ 7, /*34*/ 5, /*35*/ 6,
  180. };
  181. indices.insert(indices.begin(), boxIndices.begin(), boxIndices.end());
  182. indices.push_back(2);
  183. indices.push_back(1);
  184. indices.push_back(0);
  185. }
  186. }
  187. };
  188. TEST_F(NavigationTest, GetNativeNavMesh)
  189. {
  190. Entity e;
  191. PopulateEntity(e);
  192. ActivateEntity(e);
  193. SetupNavigationMesh();
  194. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  195. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  196. {
  197. AddTestGeometry(vertices, indices, true);
  198. }));
  199. const Wait wait(AZ::EntityId(1));
  200. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  201. AZStd::shared_ptr<RecastNavigation::NavMeshQuery> navMeshQuery;
  202. RecastNavigationMeshRequestBus::EventResult(navMeshQuery, e.GetId(), &RecastNavigationMeshRequests::GetNavigationObject);
  203. RecastNavigation::NavMeshQuery::LockGuard lock(*navMeshQuery);
  204. /*
  205. * We updated the navigation mesh using a blocking call. We should have access to the native Recast object now.
  206. */
  207. EXPECT_NE(lock.GetNavMesh(), nullptr);
  208. }
  209. TEST_F(NavigationTest, TestAgainstEmptyPhysicalBody)
  210. {
  211. Entity e;
  212. PopulateEntity(e);
  213. ActivateEntity(e);
  214. {
  215. m_hit->m_resultFlags = AzPhysics::SceneQuery::EntityId;
  216. m_hit->m_entityId = AZ::EntityId{ 1 };
  217. m_hit->m_shape = m_mockPhysicsShape.get();
  218. ON_CALL(*m_mockSceneInterface, QueryScene(_, _)).WillByDefault(Invoke([this]
  219. (AzPhysics::SceneHandle, const AzPhysics::SceneQueryRequest* request)
  220. {
  221. const AzPhysics::OverlapRequest* overlapRequest = static_cast<const AzPhysics::OverlapRequest*>(request);
  222. overlapRequest->m_unboundedOverlapHitCallback({ *m_hit });
  223. return AzPhysics::SceneQueryHits();
  224. }));
  225. ON_CALL(*m_mockSceneInterface, GetSimulatedBodyFromHandle(_, _)).WillByDefault(Invoke([]
  226. (AzPhysics::SceneHandle, AzPhysics::SimulatedBodyHandle)
  227. {
  228. return nullptr; // empty physical body
  229. }));
  230. }
  231. /*
  232. * Corner case, when a collider doesn't have a physical body for reason. Just don't fail.
  233. */
  234. const Wait wait(AZ::EntityId(1));
  235. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  236. AZStd::shared_ptr<RecastNavigation::NavMeshQuery> navMeshQuery;
  237. RecastNavigationMeshRequestBus::EventResult(navMeshQuery, e.GetId(), &RecastNavigationMeshRequests::GetNavigationObject);
  238. RecastNavigation::NavMeshQuery::LockGuard lock(*navMeshQuery);
  239. EXPECT_NE(lock.GetNavQuery(), nullptr);
  240. }
  241. TEST_F(NavigationTest, BlockingTest)
  242. {
  243. Entity e;
  244. PopulateEntity(e);
  245. ActivateEntity(e);
  246. SetupNavigationMesh();
  247. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  248. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  249. {
  250. AddTestGeometry(vertices, indices, true);
  251. }));
  252. const Wait wait(AZ::EntityId(1));
  253. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  254. /*
  255. * Verify the notification EBus is called when a navigation mesh is updated.
  256. */
  257. EXPECT_EQ(wait.m_updatedCalls, 1);
  258. }
  259. TEST_F(NavigationTest, BlockingTestWithDebugDraw)
  260. {
  261. Entity e;
  262. {
  263. // custom entity construction
  264. e.SetId(AZ::EntityId{ 1 });
  265. e.CreateComponent<EventSchedulerSystemComponent>();
  266. m_mockShapeComponent = e.CreateComponent<MockShapeComponent>();
  267. /*
  268. * There is no way to test debug draw but tell the provider to attempt to debug draw anyway. Just don't crash.
  269. */
  270. e.CreateComponent<RecastNavigation::RecastNavigationPhysXProviderComponent>();
  271. e.CreateComponent<RecastNavigation::RecastNavigationMeshComponent>();
  272. }
  273. ActivateEntity(e);
  274. SetupNavigationMesh();
  275. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  276. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  277. {
  278. AddTestGeometry(vertices, indices, true);
  279. }));
  280. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  281. }
  282. TEST_F(NavigationTest, BlockingNonIndexedWithDebugDraw)
  283. {
  284. Entity e;
  285. {
  286. // custom entity construction
  287. e.SetId(AZ::EntityId{ 1 });
  288. e.CreateComponent<EventSchedulerSystemComponent>();
  289. m_mockShapeComponent = e.CreateComponent<MockShapeComponent>();
  290. /*
  291. * There is no way to test debug draw but tell the provider to attempt to debug draw anyway. Just don't crash.
  292. */
  293. e.CreateComponent<RecastNavigation::RecastNavigationPhysXProviderComponent>();
  294. e.CreateComponent<RecastNavigation::RecastNavigationMeshComponent>();
  295. }
  296. ActivateEntity(e);
  297. SetupNavigationMesh();
  298. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  299. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  300. {
  301. /*
  302. * Testing with non-indexed triangle data. No way to verify, though. This test must not crash, though.
  303. */
  304. AddTestGeometry(vertices, indices, false);
  305. }));
  306. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  307. }
  308. /*
  309. * Run update navigation mesh twice with indexed triangle data.
  310. */
  311. TEST_F(NavigationTest, BlockingTestRerun)
  312. {
  313. Entity e;
  314. PopulateEntity(e);
  315. ActivateEntity(e);
  316. SetupNavigationMesh();
  317. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  318. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  319. {
  320. AddTestGeometry(vertices, indices, true);
  321. }));
  322. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  323. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  324. }
  325. /*
  326. * Run update navigation mesh twice with no data.
  327. */
  328. TEST_F(NavigationTest, BlockingOnEmptyRerun)
  329. {
  330. Entity e;
  331. PopulateEntity(e);
  332. ActivateEntity(e);
  333. SetupNavigationMesh();
  334. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  335. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  336. }
  337. /*
  338. * Exercise debug ticking code.
  339. */
  340. TEST_F(NavigationTest, BlockingTestNonIndexedGeometry)
  341. {
  342. Entity e;
  343. PopulateEntity(e);
  344. ActivateEntity(e);
  345. SetupNavigationMesh();
  346. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  347. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  348. {
  349. AddTestGeometry(vertices, indices, false);
  350. }));
  351. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  352. AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.1f, AZ::ScriptTimePoint{});
  353. }
  354. /*
  355. * Exercise debug ticking code with indexed data.
  356. */
  357. TEST_F(NavigationTest, TickingDebugDraw)
  358. {
  359. Entity e;
  360. PopulateEntity(e);
  361. ActivateEntity(e);
  362. SetupNavigationMesh();
  363. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  364. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  365. {
  366. AddTestGeometry(vertices, indices, true);
  367. }));
  368. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  369. MockDebug debug;
  370. AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.1f, AZ::ScriptTimePoint{});
  371. }
  372. /*
  373. * Exercise API rarely used by Recast
  374. */
  375. TEST_F(NavigationTest, DirectTestOnDebugDrawQuad)
  376. {
  377. RecastNavigationDebugDraw debugDraw;
  378. MockDebug debug;
  379. debugDraw.begin(DU_DRAW_QUADS);
  380. debugDraw.vertex(0, 0, 0, 0, 0, 0);
  381. debugDraw.vertex(0, 1, 0, 0, 0, 0);
  382. debugDraw.vertex(1, 1, 0, 0, 0, 0);
  383. debugDraw.vertex(1, 0, 0, 0, 0, 0);
  384. debugDraw.end();
  385. }
  386. /*
  387. * Exercise API rarely used by Recast
  388. */
  389. TEST_F(NavigationTest, DirectTestOnDebugDrawLines)
  390. {
  391. RecastNavigationDebugDraw debugDraw(true);
  392. MockDebug debug;
  393. const float pos[] = { 0, 0, 0 };
  394. const float uv[] = { 0, 0, 0 };
  395. debugDraw.begin(DU_DRAW_LINES);
  396. debugDraw.vertex(pos, 0, uv);
  397. debugDraw.vertex(pos, 0, uv);
  398. debugDraw.end();
  399. }
  400. /*
  401. * Exercise API rarely used by Recast
  402. */
  403. TEST_F(NavigationTest, DirectTestOnDebugDrawWithoutDebugDisplayRequests)
  404. {
  405. RecastNavigationDebugDraw debugDraw(true);
  406. const float pos[] = { 0, 0, 0 };
  407. const float uv[] = { 0, 0, 0 };
  408. debugDraw.begin(DU_DRAW_POINTS);
  409. debugDraw.texture(true);
  410. debugDraw.vertex(pos, 0, uv);
  411. debugDraw.end();
  412. }
  413. /*
  414. * Basic find path test.
  415. */
  416. TEST_F(NavigationTest, FindPathTestDetaultDetourSettings)
  417. {
  418. Entity e;
  419. PopulateEntity(e);
  420. e.CreateComponent<DetourNavigationComponent>();
  421. ActivateEntity(e);
  422. SetupNavigationMesh();
  423. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  424. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  425. {
  426. AddTestGeometry(vertices, indices, true);
  427. }));
  428. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  429. AZStd::vector<AZ::Vector3> waypoints;
  430. DetourNavigationRequestBus::EventResult(waypoints, AZ::EntityId(1), &DetourNavigationRequests::FindPathBetweenPositions,
  431. AZ::Vector3(0.f, 0, 0), AZ::Vector3(2.f, 2, 0));
  432. EXPECT_GT(waypoints.size(), 0);
  433. }
  434. /*
  435. * Basic find path test.
  436. */
  437. TEST_F(NavigationTest, FindPathTest)
  438. {
  439. Entity e;
  440. PopulateEntity(e);
  441. e.CreateComponent<DetourNavigationComponent>(e.GetId(), 3.f);
  442. ActivateEntity(e);
  443. SetupNavigationMesh();
  444. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  445. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  446. {
  447. AddTestGeometry(vertices, indices, true);
  448. }));
  449. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  450. AZStd::vector<AZ::Vector3> waypoints;
  451. DetourNavigationRequestBus::EventResult(waypoints, AZ::EntityId(1), &DetourNavigationRequests::FindPathBetweenPositions,
  452. AZ::Vector3(0.f, 0, 0), AZ::Vector3(2.f, 2, 0));
  453. EXPECT_GT(waypoints.size(), 0);
  454. }
  455. /*
  456. * Test with one of the point being way outside of the range of the navigation mesh.
  457. */
  458. TEST_F(NavigationTest, FindPathToOutOfBoundsDestination)
  459. {
  460. Entity e;
  461. PopulateEntity(e);
  462. e.CreateComponent<DetourNavigationComponent>(e.GetId(), 3.f);
  463. ActivateEntity(e);
  464. SetupNavigationMesh();
  465. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  466. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  467. {
  468. AddTestGeometry(vertices, indices, true);
  469. }));
  470. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  471. AZStd::vector<AZ::Vector3> waypoints;
  472. DetourNavigationRequestBus::EventResult(waypoints, AZ::EntityId(1), &DetourNavigationRequests::FindPathBetweenPositions,
  473. AZ::Vector3(0.f, 0, 0), AZ::Vector3(2000.f, 2000, 0));
  474. EXPECT_EQ(waypoints.size(), 0);
  475. }
  476. /*
  477. * Corner case, test on empty data.
  478. */
  479. TEST_F(NavigationTest, FindPathOnEmptyNavMesh)
  480. {
  481. Entity e;
  482. PopulateEntity(e);
  483. e.CreateComponent<DetourNavigationComponent>(AZ::EntityId(1337/*pointing to a non-existing entity*/), 3.f);
  484. ActivateEntity(e);
  485. SetupNavigationMesh();
  486. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  487. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  488. {
  489. AddTestGeometry(vertices, indices, true);
  490. }));
  491. AZStd::vector<AZ::Vector3> waypoints;
  492. DetourNavigationRequestBus::EventResult(waypoints, AZ::EntityId(1), &DetourNavigationRequests::FindPathBetweenPositions,
  493. AZ::Vector3(0.f, 0, 0), AZ::Vector3(2.f, 2, 0));
  494. EXPECT_EQ(waypoints.size(), 0);
  495. }
  496. /*
  497. * Corner case. Invalid entities.
  498. */
  499. TEST_F(NavigationTest, FindPathBetweenInvalidEntities)
  500. {
  501. Entity e;
  502. PopulateEntity(e);
  503. e.CreateComponent<DetourNavigationComponent>(e.GetId(), 3.f);
  504. ActivateEntity(e);
  505. SetupNavigationMesh();
  506. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  507. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  508. {
  509. AddTestGeometry(vertices, indices, true);
  510. }));
  511. AZStd::vector<AZ::Vector3> waypoints;
  512. DetourNavigationRequestBus::EventResult(waypoints, AZ::EntityId(1), &DetourNavigationRequests::FindPathBetweenEntities,
  513. AZ::EntityId(), AZ::EntityId());
  514. EXPECT_EQ(waypoints.size(), 0);
  515. }
  516. /*
  517. * Corner case. Empty nav mesh.
  518. */
  519. TEST_F(NavigationTest, FindPathBetweenEntitiesOnEmptyNavMesh)
  520. {
  521. Entity e;
  522. PopulateEntity(e);
  523. e.CreateComponent<DetourNavigationComponent>(e.GetId(), 3.f);
  524. ActivateEntity(e);
  525. SetupNavigationMesh();
  526. MockTransforms mockTransforms({ AZ::EntityId(1), AZ::EntityId(2) });
  527. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  528. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  529. {
  530. AddTestGeometry(vertices, indices, true);
  531. }));
  532. AZStd::vector<AZ::Vector3> waypoints;
  533. DetourNavigationRequestBus::EventResult(waypoints, AZ::EntityId(1), &DetourNavigationRequests::FindPathBetweenEntities,
  534. AZ::EntityId(1), AZ::EntityId(2));
  535. EXPECT_EQ(waypoints.size(), 0);
  536. }
  537. /*
  538. * Just for code coverage!
  539. */
  540. TEST_F(NavigationTest, RecastNavigationMeshComponentControllerTests)
  541. {
  542. RecastNavigation::RecastNavigationMeshComponentController common;
  543. EXPECT_EQ(strcmp(common.TYPEINFO_Name(), "RecastNavigationMeshComponentController"), 0);
  544. }
  545. /*
  546. * Just for code coverage!
  547. */
  548. TEST_F(NavigationTest, RecastNavigationNotificationHandler)
  549. {
  550. RecastNavigation::RecastNavigationNotificationHandler handler;
  551. handler.OnNavigationMeshUpdated(AZ::EntityId(1));
  552. }
  553. /*
  554. * Just for code coverage!
  555. */
  556. TEST_F(NavigationTest, RecastNavigationPhysXProviderComponentController)
  557. {
  558. RecastNavigation::RecastNavigationPhysXProviderComponentController test;
  559. EXPECT_EQ(strcmp(test.TYPEINFO_Name(), "RecastNavigationPhysXProviderComponentController"), 0);
  560. }
  561. TEST_F(NavigationTest, DISABLED_AsyncOnNavigationMeshUpdatedIsCalled)
  562. {
  563. Entity e;
  564. PopulateEntity(e);
  565. ActivateEntity(e);
  566. SetupNavigationMesh();
  567. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  568. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  569. {
  570. AddTestGeometry(vertices, indices, true);
  571. }));
  572. const Wait wait(AZ::EntityId(1));
  573. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshAsync);
  574. wait.BlockUntilCalled();
  575. }
  576. TEST_F(NavigationTest, DISABLED_AsyncDeactivateRightAfterCallingUpdate)
  577. {
  578. Entity e;
  579. PopulateEntity(e);
  580. ActivateEntity(e);
  581. SetupNavigationMesh();
  582. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  583. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  584. {
  585. AddTestGeometry(vertices, indices, true);
  586. }));
  587. const Wait wait(AZ::EntityId(1));
  588. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshAsync);
  589. // Don't wait, deactivate the entity now.
  590. /*
  591. * If everything goes well, the entity will shutdown without a crash. With a bad design,
  592. * one of tile will be sent to a deactivate component. Note, RecastNavigationMeshComponent deactivates first while
  593. * RecastNavigationPhysXProviderComponent might still try to send it tile data.
  594. */
  595. }
  596. TEST_F(NavigationTest, DISABLED_AsyncEmpty)
  597. {
  598. Entity e;
  599. PopulateEntity(e);
  600. ActivateEntity(e);
  601. SetupNavigationMesh();
  602. const Wait wait(AZ::EntityId(1));
  603. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshAsync);
  604. wait.BlockUntilCalled();
  605. }
  606. // Disabling this test to unblock AR while an investigation is in progress.
  607. TEST_F(NavigationTest, DISABLED_AsyncRerun)
  608. {
  609. Entity e;
  610. PopulateEntity(e);
  611. ActivateEntity(e);
  612. SetupNavigationMesh();
  613. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  614. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  615. {
  616. AddTestGeometry(vertices, indices, true);
  617. }));
  618. for (int i = 1; i <= 2; ++i)
  619. {
  620. const Wait wait(AZ::EntityId(1));
  621. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshAsync);
  622. wait.BlockUntilCalled();
  623. }
  624. }
  625. TEST_F(NavigationTest, DISABLED_AsyncSecondWhileFirstIsInProgress)
  626. {
  627. Entity e;
  628. PopulateEntity(e);
  629. ActivateEntity(e);
  630. SetupNavigationMesh();
  631. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  632. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  633. {
  634. AddTestGeometry(vertices, indices, true);
  635. }));
  636. const Wait wait(AZ::EntityId(1));
  637. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshAsync);
  638. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshAsync);
  639. wait.BlockUntilCalled();
  640. EXPECT_EQ(wait.m_updatedCalls, 1);
  641. }
  642. TEST_F(NavigationTest, DISABLED_AsyncManyUpdatesWhileFirstIsInProgressStressTest)
  643. {
  644. Entity e;
  645. PopulateEntity(e);
  646. ActivateEntity(e);
  647. SetupNavigationMesh();
  648. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  649. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  650. {
  651. AddTestGeometry(vertices, indices, true);
  652. }));
  653. const Wait wait(AZ::EntityId(1));
  654. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshAsync);
  655. for (int i = 0; i < 9'001; ++i)
  656. {
  657. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshAsync);
  658. }
  659. wait.BlockUntilCalled();
  660. // Only one of those updates was done.
  661. EXPECT_EQ(wait.m_updatedCalls, 1);
  662. }
  663. TEST_F(NavigationTest, DISABLED_BlockingCallAfterAsync)
  664. {
  665. Entity e;
  666. PopulateEntity(e);
  667. ActivateEntity(e);
  668. SetupNavigationMesh();
  669. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  670. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  671. {
  672. AddTestGeometry(vertices, indices, true);
  673. }));
  674. const Wait wait(AZ::EntityId(1));
  675. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshAsync);
  676. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  677. wait.BlockUntilCalled();
  678. // Only one of those updates was done.
  679. EXPECT_EQ(wait.m_updatedCalls, 1);
  680. }
  681. TEST_F(NavigationTest, DISABLED_BlockingCallAfterAsyncReturnsFalse)
  682. {
  683. Entity e;
  684. PopulateEntity(e);
  685. ActivateEntity(e);
  686. SetupNavigationMesh();
  687. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  688. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  689. {
  690. AddTestGeometry(vertices, indices, true);
  691. }));
  692. const Wait wait(AZ::EntityId(1));
  693. bool result = false;
  694. RecastNavigationMeshRequestBus::EventResult(result, e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshAsync);
  695. EXPECT_EQ(result, true);
  696. RecastNavigationMeshRequestBus::EventResult(result, e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  697. EXPECT_EQ(result, false);
  698. wait.BlockUntilCalled();
  699. }
  700. TEST_F(NavigationTest, DISABLED_FindPathRightAfterUpdateAsync)
  701. {
  702. Entity e;
  703. PopulateEntity(e);
  704. e.CreateComponent<DetourNavigationComponent>(e.GetId(), 3.f);
  705. ActivateEntity(e);
  706. SetupNavigationMesh();
  707. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  708. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  709. {
  710. AddTestGeometry(vertices, indices, true);
  711. }));
  712. const Wait wait(AZ::EntityId(1));
  713. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshAsync);
  714. AZStd::vector<AZ::Vector3> waypoints;
  715. DetourNavigationRequestBus::EventResult(waypoints, AZ::EntityId(1), &DetourNavigationRequests::FindPathBetweenPositions,
  716. AZ::Vector3(0.f, 0.f, 0.f), AZ::Vector3(2.f, 2.f, 0.f));
  717. // We should not get the path yet since the async update operation is still in progress.
  718. EXPECT_EQ(waypoints.size(), 0);
  719. wait.BlockUntilCalled();
  720. }
  721. TEST_F(NavigationTest, CollectGeometryCornerCaseZeroTileSize)
  722. {
  723. Entity e;
  724. PopulateEntity(e);
  725. e.CreateComponent<DetourNavigationComponent>(e.GetId(), 3.f);
  726. ActivateEntity(e);
  727. SetupNavigationMesh();
  728. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  729. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  730. {
  731. AddTestGeometry(vertices, indices, true);
  732. }));
  733. AZStd::vector<AZStd::shared_ptr<RecastNavigation::TileGeometry>> tiles;
  734. RecastNavigation::RecastNavigationProviderRequestBus::EventResult(tiles, e.GetId(),
  735. &RecastNavigation::RecastNavigationProviderRequests::CollectGeometry,
  736. 0.f, 0.f);
  737. EXPECT_EQ(tiles.size(), 0);
  738. }
  739. TEST_F(NavigationTest, DetourSetNavMeshEntity)
  740. {
  741. Entity e;
  742. PopulateEntity(e);
  743. DetourNavigationComponent* detour = e.CreateComponent<DetourNavigationComponent>();
  744. ActivateEntity(e);
  745. SetupNavigationMesh();
  746. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  747. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  748. {
  749. AddTestGeometry(vertices, indices, true);
  750. }));
  751. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  752. detour->SetNavigationMeshEntity(AZ::EntityId(999)/*Doesn't exist*/);
  753. AZStd::vector<AZ::Vector3> waypoints = detour->FindPathBetweenPositions(AZ::Vector3(0.f, 0.f, 0.f), AZ::Vector3(2.f, 2.f, 0.f));
  754. EXPECT_EQ(waypoints.size(), 0);
  755. detour->SetNavigationMeshEntity(AZ::EntityId(1)/*The right entity*/);
  756. waypoints = detour->FindPathBetweenPositions(AZ::Vector3(0.f, 0.f, 0.f), AZ::Vector3(2.f, 2.f, 0.f));
  757. EXPECT_GE(waypoints.size(), 1);
  758. }
  759. TEST_F(NavigationTest, NavUpdateThenDeleteCollidersThenUpdateAgainThenFindPathShouldFail)
  760. {
  761. Entity e;
  762. PopulateEntity(e);
  763. e.CreateComponent<DetourNavigationComponent>(e.GetId(), 3.f);
  764. ActivateEntity(e);
  765. SetupNavigationMesh();
  766. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([this]
  767. (AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, const AZ::Aabb*)
  768. {
  769. AddTestGeometry(vertices, indices, true);
  770. }));
  771. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  772. AZStd::vector<AZ::Vector3> waypoints;
  773. DetourNavigationRequestBus::EventResult(waypoints, AZ::EntityId(1), &DetourNavigationRequests::FindPathBetweenPositions,
  774. AZ::Vector3(0.f, 0.f, 0.f), AZ::Vector3(2.f, 2.f, 0.f));
  775. EXPECT_GT(waypoints.size(), 1);
  776. ON_CALL(*m_mockPhysicsShape.get(), GetGeometry(_, _, _)).WillByDefault(Invoke([]
  777. (
  778. [[maybe_unused]] AZStd::vector<AZ::Vector3>& vertices,
  779. [[maybe_unused]] AZStd::vector<AZ::u32>& indices,
  780. [[maybe_unused]] const AZ::Aabb*)
  781. {
  782. // Act as if there colliders are gone.
  783. }));
  784. RecastNavigationMeshRequestBus::Event(e.GetId(), &RecastNavigationMeshRequests::UpdateNavigationMeshBlockUntilCompleted);
  785. waypoints.clear();
  786. DetourNavigationRequestBus::EventResult(waypoints, AZ::EntityId(1), &DetourNavigationRequests::FindPathBetweenPositions,
  787. AZ::Vector3(0.f, 0.f, 0.f), AZ::Vector3(2.f, 2.f, 0.f));
  788. EXPECT_EQ(waypoints.size(), 0);
  789. }
  790. }