EditorRecastNavigationMeshComponent.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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 "EditorRecastNavigationMeshComponent.h"
  9. #include <AzCore/Console/IConsole.h>
  10. #include <AzCore/Serialization/EditContext.h>
  11. AZ_CVAR(
  12. int, ed_navmesh_updateFrequencyMs, 1000, nullptr, AZ::ConsoleFunctorFlags::Null,
  13. "How often to update the navigation mesh preview in the Editor (in milliseconds).");
  14. namespace RecastNavigation
  15. {
  16. void EditorRecastNavigationMeshComponent::Reflect(AZ::ReflectContext* context)
  17. {
  18. BaseClass::Reflect(context);
  19. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  20. {
  21. serializeContext->Class<EditorRecastNavigationMeshComponent, BaseClass>()
  22. ->Version(1);
  23. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  24. {
  25. editContext->Class<EditorRecastNavigationMeshComponent>("Recast Navigation Mesh",
  26. "[Calculates the walkable navigation mesh within a specified area.]")
  27. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  28. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  29. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  30. ;
  31. editContext->Class<RecastNavigationMeshComponentController>(
  32. "MeshComponentController", "")
  33. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  34. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  35. ->DataElement(AZ::Edit::UIHandlers::Default, &RecastNavigationMeshComponentController::m_configuration, "Configuration", "")
  36. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  37. ;
  38. using Config = RecastNavigationMeshConfig;
  39. editContext->Class<RecastNavigationMeshConfig>("Recast Navigation Mesh Config",
  40. "[Navigation mesh configuration]")
  41. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  42. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  43. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  44. // Agent configuration
  45. ->ClassElement(AZ::Edit::ClassElements::Group, "Agent Configuration")
  46. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  47. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_agentHeight, "Agent Height",
  48. "Minimum floor to 'ceiling' height that will still allow the floor area to be considered walkable.")
  49. ->Attribute(AZ::Edit::Attributes::SoftMin, 3.f)
  50. ->Attribute(AZ::Edit::Attributes::Suffix, " world units")
  51. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_agentMaxClimb, "Agent Max Climb",
  52. "Maximum ledge height that is considered to still be traversable.")
  53. ->Attribute(AZ::Edit::Attributes::Min, 0.f)
  54. ->Attribute(AZ::Edit::Attributes::Suffix, " world units")
  55. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_agentMaxSlope, "Agent Max Slope",
  56. "The maximum slope that is considered walkable.")
  57. ->Attribute(AZ::Edit::Attributes::Min, 0.f)
  58. ->Attribute(AZ::Edit::Attributes::Max, 90.f)
  59. ->Attribute(AZ::Edit::Attributes::Suffix, " degrees")
  60. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_agentRadius, "Agent Radius",
  61. "The distance to erode/shrink the walkable area of the heightfield away from obstructions.")
  62. ->Attribute(AZ::Edit::Attributes::Min, 0.f)
  63. ->Attribute(AZ::Edit::Attributes::Suffix, " world units")
  64. // Editor-only configuration
  65. ->ClassElement(AZ::Edit::ClassElements::Group, "Editor-only Configuration")
  66. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  67. ->DataElement(AZ::Edit::UIHandlers::Default, &RecastNavigationMeshConfig::m_enableEditorPreview,
  68. "Editor Preview", "If enabled, frequently calculates navigation mesh and draws in the Editor viewport."
  69. "Does not affect game mode.")
  70. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::ValuesOnly)
  71. // Debug configuration
  72. ->ClassElement(AZ::Edit::ClassElements::Group, "Debug Configuration")
  73. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  74. ->DataElement(AZ::Edit::UIHandlers::Default, &RecastNavigationMeshConfig::m_enableDebugDraw,
  75. "Debug Draw", "If enabled, draw the navigation mesh in game mode. Does not affect Editor preview.")
  76. // Advanced configuration
  77. ->ClassElement(AZ::Edit::ClassElements::Group, "Advanced Configuration")
  78. ->Attribute(AZ::Edit::Attributes::AutoExpand, false)
  79. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_tileSize, "Tile Size",
  80. "The width/height size of tile's on the xy-plane.")
  81. ->Attribute(AZ::Edit::Attributes::Min, 0.f)
  82. ->Attribute(AZ::Edit::Attributes::Suffix, " world units")
  83. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_borderSize, "Border Size",
  84. "The additional dimension around the tile to collect additional geometry in order to connect to adjacent tiles.")
  85. ->Attribute(AZ::Edit::Attributes::SoftMin, 10)
  86. ->Attribute(AZ::Edit::Attributes::Suffix, " voxels")
  87. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_cellHeight, "Voxel Height",
  88. "The y-axis cell size to use for fields.")
  89. ->Attribute(AZ::Edit::Attributes::Min, 0.f)
  90. ->Attribute(AZ::Edit::Attributes::Suffix, " world units")
  91. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_cellSize, "Voxel Size",
  92. "The xz-plane cell size to use for fields. This defines the voxel sizes for other configuration attributes.")
  93. ->Attribute(AZ::Edit::Attributes::Min, 0.f)
  94. ->Attribute(AZ::Edit::Attributes::Suffix, " world units")
  95. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_detailSampleDist, "Detail Sample Distance",
  96. "Sets the sampling distance to use when generating the detail mesh. (For height detail only.)")
  97. ->Attribute(AZ::Edit::Attributes::SoftMin, 0.9f)
  98. ->Attribute(AZ::Edit::Attributes::Suffix, " world units")
  99. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_detailSampleMaxError, "Detail Sample Max Error",
  100. "The maximum distance the detail mesh surface should deviate from heightfield data. (For height detail only.)")
  101. ->Attribute(AZ::Edit::Attributes::Min, 0.f)
  102. ->Attribute(AZ::Edit::Attributes::Suffix, " world units")
  103. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_edgeMaxError, "Edge Max Error",
  104. "The maximum distance a simplified contour's border edges should deviate the original raw contour.")
  105. ->Attribute(AZ::Edit::Attributes::Min, 0.f)
  106. ->Attribute(AZ::Edit::Attributes::Suffix, " world units")
  107. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_edgeMaxLen, "Edge Max Length",
  108. "The maximum allowed length for contour edges along the border of the mesh.")
  109. ->Attribute(AZ::Edit::Attributes::Min, 0.f)
  110. ->Attribute(AZ::Edit::Attributes::Suffix, " world units")
  111. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_filterLedgeSpans, "Filter Ledge Spans",
  112. "A ledge is a span with one or more neighbors whose maximum is further away than walkableClimb "
  113. " from the current span's maximum."
  114. " This method removes the impact of the overestimation of conservative voxelization"
  115. " so the resulting mesh will not have regions hanging in the air over ledges.")
  116. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_filterLowHangingObstacles, "Filter Low Hanging Obstacles",
  117. "Allows the formation of walkable regions that will flow over low lying objects such as curbs, and up structures such as stairways. ")
  118. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_filterWalkableLowHeightSpans, "Filter Walkable Low Height Spans",
  119. "For this filter, the clearance above the span is the distance from the span's maximum to the next higher span's minimum. (Same grid column.)")
  120. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_maxVerticesPerPoly, "Max Vertices Per Poly",
  121. "The maximum number of vertices allowed for polygons generated during the contour to polygon conversion process.")
  122. ->Attribute(AZ::Edit::Attributes::Min, 3)
  123. ->Attribute(AZ::Edit::Attributes::Suffix, " vertices")
  124. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_regionMergeSize, "Region Merge Size",
  125. "Any regions with a span count smaller than this value will, if possible, be merged with larger regions. [Limit: >=0]")
  126. ->Attribute(AZ::Edit::Attributes::Min, 0)
  127. ->Attribute(AZ::Edit::Attributes::Suffix, " voxels")
  128. ->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_regionMinSize, "Region Min Size",
  129. "The minimum number of cells allowed to form isolated island areas.")
  130. ->Attribute(AZ::Edit::Attributes::Min, 0)
  131. ->Attribute(AZ::Edit::Attributes::Suffix, " voxels")
  132. ;
  133. }
  134. }
  135. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  136. {
  137. behaviorContext->ConstantProperty("EditorRecastNavigationMeshComponentTypeId",
  138. BehaviorConstant(AZ::Uuid(EditorRecastNavigationMeshComponentTypeId)))
  139. ->Attribute(AZ::Script::Attributes::Module, "navigation")
  140. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation);
  141. }
  142. }
  143. EditorRecastNavigationMeshComponent::EditorRecastNavigationMeshComponent(const RecastNavigationMeshConfig& config)
  144. : BaseClass(config)
  145. {
  146. }
  147. void EditorRecastNavigationMeshComponent::Activate()
  148. {
  149. BaseClass::Activate();
  150. OnConfigurationChanged();
  151. }
  152. void EditorRecastNavigationMeshComponent::Deactivate()
  153. {
  154. BaseClass::Deactivate();
  155. }
  156. void EditorRecastNavigationMeshComponent::BuildGameEntity(AZ::Entity* gameEntity)
  157. {
  158. const bool saveState = m_controller.m_configuration.m_enableEditorPreview;
  159. m_controller.m_configuration.m_enableEditorPreview = false;
  160. // The game entity must query the regular game PhysX scene, while the Editor component must query the Editor PhysX scene.
  161. BaseClass::BuildGameEntity(gameEntity);
  162. m_controller.m_configuration.m_enableEditorPreview = saveState;
  163. }
  164. AZ::u32 EditorRecastNavigationMeshComponent::OnConfigurationChanged()
  165. {
  166. m_controller.CreateNavigationMesh(GetEntityId());
  167. if (m_controller.m_configuration.m_enableEditorPreview)
  168. {
  169. m_inEditorUpdateTick.Enqueue(AZ::TimeMs{ aznumeric_cast<int>(ed_navmesh_updateFrequencyMs) }, true);
  170. }
  171. else
  172. {
  173. m_inEditorUpdateTick.RemoveFromQueue();
  174. }
  175. BaseClass::OnConfigurationChanged();
  176. return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
  177. }
  178. void EditorRecastNavigationMeshComponent::OnEditorUpdateTick()
  179. {
  180. m_controller.UpdateNavigationMeshAsync();
  181. }
  182. void EditorRecastNavigationMeshComponent::SetEditorPreview(bool enable)
  183. {
  184. m_controller.m_configuration.m_enableEditorPreview = enable;
  185. }
  186. } // namespace RecastNavigation