DetourNavigationComponent.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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/Component/TransformBus.h>
  9. #include <AzCore/Debug/Profiler.h>
  10. #include <AzCore/Serialization/SerializeContext.h>
  11. #include <Components/DetourNavigationComponent.h>
  12. #include <RecastNavigation/RecastHelpers.h>
  13. #include <RecastNavigation/RecastNavigationMeshBus.h>
  14. AZ_DECLARE_BUDGET(Navigation);
  15. namespace RecastNavigation
  16. {
  17. DetourNavigationComponent::DetourNavigationComponent(AZ::EntityId navQueryEntityId, float nearestDistance)
  18. : m_navQueryEntityId(navQueryEntityId), m_nearestDistance(nearestDistance)
  19. {
  20. }
  21. void DetourNavigationComponent::Reflect(AZ::ReflectContext* context)
  22. {
  23. if (auto serialize = azrtti_cast<AZ::SerializeContext*>(context))
  24. {
  25. serialize->Class<DetourNavigationComponent, AZ::Component>()
  26. ->Field("Navigation Query Entity", &DetourNavigationComponent::m_navQueryEntityId)
  27. ->Field("Nearest Distance", &DetourNavigationComponent::m_nearestDistance)
  28. ->Version(1);
  29. }
  30. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  31. {
  32. behaviorContext->EBus<DetourNavigationRequestBus>("DetourNavigationRequestBus")
  33. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  34. ->Attribute(AZ::Script::Attributes::Module, "navigation")
  35. ->Attribute(AZ::Script::Attributes::Category, "Recast Navigation")
  36. ->Event("FindPathBetweenEntities", &DetourNavigationRequests::FindPathBetweenEntities)
  37. ->Event("FindPathBetweenPositions", &DetourNavigationRequests::FindPathBetweenPositions)
  38. ->Event("SetNavigationMeshEntity", &DetourNavigationRequests::SetNavigationMeshEntity)
  39. ->Event("GetNavigationMeshEntity", &DetourNavigationRequests::GetNavigationMeshEntity)
  40. ;
  41. behaviorContext->Class<DetourNavigationComponent>()->RequestBus("DetourNavigationRequestBus");
  42. }
  43. }
  44. AZStd::vector<AZ::Vector3> DetourNavigationComponent::FindPathBetweenEntities(AZ::EntityId fromEntity, AZ::EntityId toEntity)
  45. {
  46. if (fromEntity.IsValid() && toEntity.IsValid())
  47. {
  48. AZ::Vector3 start = AZ::Vector3::CreateZero(), end = AZ::Vector3::CreateZero();
  49. AZ::TransformBus::EventResult(start, fromEntity, &AZ::TransformBus::Events::GetWorldTranslation);
  50. AZ::TransformBus::EventResult(end, toEntity, &AZ::TransformBus::Events::GetWorldTranslation);
  51. return FindPathBetweenPositions(start, end);
  52. }
  53. return {};
  54. }
  55. AZStd::vector<AZ::Vector3> DetourNavigationComponent::FindPathBetweenPositions(const AZ::Vector3& fromWorldPosition, const AZ::Vector3& toWorldPosition)
  56. {
  57. AZ_PROFILE_SCOPE(Navigation, "Navigation: FindPathBetweenPositions");
  58. AZStd::shared_ptr<NavMeshQuery> navMeshQuery;
  59. RecastNavigationMeshRequestBus::EventResult(navMeshQuery, m_navQueryEntityId, &RecastNavigationMeshRequests::GetNavigationObject);
  60. if (!navMeshQuery)
  61. {
  62. return {};
  63. }
  64. NavMeshQuery::LockGuard lock(*navMeshQuery);
  65. if (!lock.GetNavQuery())
  66. {
  67. return {};
  68. }
  69. RecastVector3 startRecast = RecastVector3::CreateFromVector3SwapYZ(fromWorldPosition);
  70. RecastVector3 endRecast = RecastVector3::CreateFromVector3SwapYZ(toWorldPosition);
  71. const float halfExtents[3] = { m_nearestDistance, m_nearestDistance, m_nearestDistance };
  72. dtPolyRef startPoly = 0, endPoly = 0;
  73. RecastVector3 nearestStartPoint, nearestEndPoint;
  74. const dtQueryFilter filter;
  75. // Find nearest points on the navigation mesh given the positions provided.
  76. // We are allowing some flexibility where looking for a point just a bit outside of the navigation mesh would still work.
  77. dtStatus result = lock.GetNavQuery()->findNearestPoly(startRecast.GetData(), halfExtents, &filter, &startPoly, nearestStartPoint.GetData());
  78. if (dtStatusFailed(result) || startPoly == 0)
  79. {
  80. return {};
  81. }
  82. result = lock.GetNavQuery()->findNearestPoly(endRecast.GetData(), halfExtents, &filter, &endPoly, nearestEndPoint.GetData());
  83. if (dtStatusFailed(result) || endPoly == 0)
  84. {
  85. return {};
  86. }
  87. // Some reasonable amount of waypoints along the path. Recast isn't made to calculate very long paths.
  88. constexpr int MaxPathLength = 100;
  89. AZStd::array<dtPolyRef, MaxPathLength> path;
  90. int pathLength = 0;
  91. // Find an approximate path first. In Recast, an approximate path is a collection of polygons, where a polygon covers an area.
  92. result = lock.GetNavQuery()->findPath(startPoly, endPoly, nearestStartPoint.GetData(), nearestEndPoint.GetData(),
  93. &filter, path.data(), &pathLength, MaxPathLength);
  94. if (dtStatusFailed(result))
  95. {
  96. return {};
  97. }
  98. AZStd::array<RecastVector3, MaxPathLength> detailedPath;
  99. AZStd::array<AZ::u8, MaxPathLength> detailedPathFlags;
  100. AZStd::array<dtPolyRef, MaxPathLength> detailedPolyPathRefs;
  101. int detailedPathCount = 0;
  102. // Then the detailed path. This gives us actual specific waypoints along the path over the polygons found earlier.
  103. result = lock.GetNavQuery()->findStraightPath(startRecast.GetData(), endRecast.GetData(), path.data(), pathLength,
  104. detailedPath[0].GetData(), detailedPathFlags.data(), detailedPolyPathRefs.data(),
  105. &detailedPathCount, MaxPathLength, DT_STRAIGHTPATH_ALL_CROSSINGS);
  106. if (dtStatusFailed(result))
  107. {
  108. return {};
  109. }
  110. AZStd::vector<AZ::Vector3> pathPoints;
  111. pathPoints.reserve(detailedPathCount);
  112. // Note: Recast uses +Y, O3DE used +Z as up vectors.
  113. for (int i = 0; i < detailedPathCount; ++i)
  114. {
  115. pathPoints.push_back(detailedPath[i].AsVector3WithZup());
  116. }
  117. return pathPoints;
  118. }
  119. void DetourNavigationComponent::SetNavigationMeshEntity(AZ::EntityId navMeshEntity)
  120. {
  121. m_navQueryEntityId = navMeshEntity;
  122. }
  123. AZ::EntityId DetourNavigationComponent::GetNavigationMeshEntity() const
  124. {
  125. return m_navQueryEntityId;
  126. }
  127. void DetourNavigationComponent::Activate()
  128. {
  129. if (!m_navQueryEntityId.IsValid())
  130. {
  131. // Default to looking for the navigation mesh component on the same entity if one is not specified.
  132. m_navQueryEntityId = GetEntityId();
  133. }
  134. DetourNavigationRequestBus::Handler::BusConnect(GetEntityId());
  135. }
  136. void DetourNavigationComponent::Deactivate()
  137. {
  138. DetourNavigationRequestBus::Handler::BusDisconnect();
  139. }
  140. } // namespace RecastNavigation