RecastProcessing.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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 <DetourNavMeshBuilder.h>
  9. #include <AzCore/Console/Console.h>
  10. #include <AzCore/Debug/Profiler.h>
  11. #include <Misc/RecastProcessing.h>
  12. AZ_DECLARE_BUDGET(Navigation);
  13. namespace RecastNavigation
  14. {
  15. void RecastProcessing::InitializeMeshConfig(TileGeometry* geom, const RecastNavigationMeshConfig& meshConfig)
  16. {
  17. // Init build configuration from GUI
  18. m_config = {};
  19. m_config.cs = meshConfig.m_cellSize;
  20. m_config.ch = meshConfig.m_cellHeight;
  21. m_config.walkableSlopeAngle = meshConfig.m_agentMaxSlope;
  22. m_config.walkableHeight = aznumeric_cast<int>(AZStd::ceil(meshConfig.m_agentHeight / m_config.ch));
  23. m_config.walkableClimb = aznumeric_cast<int>(AZStd::floor(meshConfig.m_agentMaxClimb / m_config.ch));
  24. m_config.walkableRadius = aznumeric_cast<int>(AZStd::ceil(meshConfig.m_agentRadius / m_config.cs));
  25. m_config.maxEdgeLen = aznumeric_cast<int>(meshConfig.m_edgeMaxLen / meshConfig.m_cellSize);
  26. m_config.maxSimplificationError = meshConfig.m_edgeMaxError;
  27. m_config.minRegionArea = rcSqr(meshConfig.m_regionMinSize); // Note: area = size*size
  28. m_config.mergeRegionArea = rcSqr(meshConfig.m_regionMergeSize); // Note: area = size*size
  29. m_config.maxVertsPerPoly = meshConfig.m_maxVerticesPerPoly;
  30. m_config.detailSampleDist = meshConfig.m_detailSampleDist < 0.9f ? 0 : meshConfig.m_cellSize * meshConfig.m_detailSampleDist;
  31. m_config.detailSampleMaxError = meshConfig.m_cellHeight * meshConfig.m_detailSampleMaxError;
  32. m_config.tileSize = aznumeric_cast<int>(meshConfig.m_tileSize / m_config.cs);
  33. m_config.borderSize = m_config.walkableRadius + meshConfig.m_borderSize; // Reserve enough padding.
  34. m_config.width = m_config.tileSize + m_config.borderSize * 2;
  35. m_config.height = m_config.tileSize + m_config.borderSize * 2;
  36. // Set the area where the navigation will be build.
  37. // Here the bounds of the input mesh are used, but the
  38. // area could be specified by an user defined box, etc.
  39. const RecastVector3 worldMin = RecastVector3::CreateFromVector3SwapYZ(geom->m_worldBounds.GetMin());
  40. const RecastVector3 worldMax = RecastVector3::CreateFromVector3SwapYZ(geom->m_worldBounds.GetMax());
  41. rcVcopy(m_config.bmin, worldMin.m_xyz);
  42. rcVcopy(m_config.bmax, worldMax.m_xyz);
  43. m_config.bmin[0] -= aznumeric_cast<float>(m_config.borderSize) * m_config.cs;
  44. m_config.bmin[2] -= aznumeric_cast<float>(m_config.borderSize) * m_config.cs;
  45. m_config.bmax[0] += aznumeric_cast<float>(m_config.borderSize) * m_config.cs;
  46. m_config.bmax[2] += aznumeric_cast<float>(m_config.borderSize) * m_config.cs;
  47. }
  48. bool RecastProcessing::RasterizeInputPolygonSoup()
  49. {
  50. // Allocate voxel height field where we rasterize our input data to.
  51. m_solid.reset(rcAllocHeightfield());
  52. if (!m_solid)
  53. {
  54. AZ_Error("Navigation", false, "buildNavigation: Out of memory 'solid'.");
  55. return false;
  56. }
  57. if (!rcCreateHeightfield(m_context, *m_solid, m_config.width, m_config.height,
  58. m_config.bmin, m_config.bmax, m_config.cs, m_config.ch))
  59. {
  60. AZ_Error("Navigation", false, "buildNavigation: Could not create solid height field.");
  61. return false;
  62. }
  63. // Allocate array that can hold triangle area types.
  64. // If you have multiple meshes you need to process, allocate
  65. // and array which can hold the max number of triangles you need to process.
  66. m_trianglesAreas.resize(m_triangleCount, 0);
  67. // Find triangles which are walkable based on their slope and rasterize them.
  68. // If your input data is multiple meshes, you can transform them here, calculate
  69. // the are type for each of the meshes and rasterize them.
  70. rcMarkWalkableTriangles(m_context, m_config.walkableSlopeAngle, m_vertices,
  71. m_vertexCount, m_triangleData, m_triangleCount, m_trianglesAreas.data());
  72. if (!rcRasterizeTriangles(m_context, m_vertices, m_vertexCount, m_triangleData,
  73. m_trianglesAreas.data(), m_triangleCount, *m_solid))
  74. {
  75. AZ_Error("Navigation", false, "buildNavigation: Could not rasterize triangles.");
  76. return false;
  77. }
  78. m_trianglesAreas.clear();
  79. return true;
  80. }
  81. void RecastProcessing::FilterWalkableSurfaces(const RecastNavigationMeshConfig& meshConfig)
  82. {
  83. // Once all geometry is rasterized, we do initial pass of filtering to
  84. // remove unwanted overhangs caused by the conservative rasterization
  85. // as well as filter spans where the character cannot possibly stand.
  86. if (meshConfig.m_filterLowHangingObstacles)
  87. {
  88. rcFilterLowHangingWalkableObstacles(m_context, m_config.walkableClimb, *m_solid);
  89. }
  90. if (meshConfig.m_filterLedgeSpans)
  91. {
  92. rcFilterLedgeSpans(m_context, m_config.walkableHeight, m_config.walkableClimb, *m_solid);
  93. }
  94. if (meshConfig.m_filterWalkableLowHeightSpans)
  95. {
  96. rcFilterWalkableLowHeightSpans(m_context, m_config.walkableHeight, *m_solid);
  97. }
  98. }
  99. bool RecastProcessing::PartitionWalkableSurfaceToSimpleRegions()
  100. {
  101. // Compact the height field so that it is faster to handle from now on.
  102. // This will result more cache coherent data as well as the neighbors
  103. // between walkable cells will be calculated.
  104. m_compactHeightfield.reset(rcAllocCompactHeightfield());
  105. if (!m_compactHeightfield)
  106. {
  107. AZ_Error("Navigation", false, "buildNavigation: Out of memory 'chf'.");
  108. return false;
  109. }
  110. if (!rcBuildCompactHeightfield(m_context, m_config.walkableHeight, m_config.walkableClimb, *m_solid, *m_compactHeightfield))
  111. {
  112. AZ_Error("Navigation", false, "buildNavigation: Could not build compact data.");
  113. return false;
  114. }
  115. m_solid.reset();
  116. // Erode the walkable area by agent radius.
  117. if (!rcErodeWalkableArea(m_context, m_config.walkableRadius, *m_compactHeightfield))
  118. {
  119. AZ_Error("Navigation", false, "buildNavigation: Could not erode.");
  120. return false;
  121. }
  122. // Partition the walkable surface into simple regions without holes.
  123. // Monotone partitioning does not need distance field.
  124. if (!rcBuildRegionsMonotone(m_context, *m_compactHeightfield,
  125. m_config.borderSize, m_config.minRegionArea, m_config.mergeRegionArea))
  126. {
  127. AZ_Error("Navigation", false, "buildNavigation: Could not build monotone regions.");
  128. return false;
  129. }
  130. return true;
  131. }
  132. bool RecastProcessing::TraceAndSimplifyRegionContours()
  133. {
  134. // Create contours.
  135. m_contourSet.reset(rcAllocContourSet());
  136. if (!m_contourSet)
  137. {
  138. AZ_Error("Navigation", false, "buildNavigation: Out of memory while allocating contours.");
  139. return false;
  140. }
  141. if (!rcBuildContours(m_context, *m_compactHeightfield, m_config.maxSimplificationError,
  142. m_config.maxEdgeLen, *m_contourSet))
  143. {
  144. AZ_Error("Navigation", false, "buildNavigation: Could not create contours.");
  145. return false;
  146. }
  147. return true;
  148. }
  149. bool RecastProcessing::BuildPolygonsMeshFromContours()
  150. {
  151. // Build polygon nav mesh from the contours.
  152. m_polyMesh.reset(rcAllocPolyMesh());
  153. if (!m_polyMesh)
  154. {
  155. AZ_Error("Navigation", false, "buildNavigation: Out of memory while creating poly mesh.");
  156. return false;
  157. }
  158. if (!rcBuildPolyMesh(m_context, *m_contourSet, m_config.maxVertsPerPoly, *m_polyMesh))
  159. {
  160. AZ_Error("Navigation", false, "buildNavigation: Could not triangulate contours.");
  161. return false;
  162. }
  163. return true;
  164. }
  165. bool RecastProcessing::CreateDetailMesh()
  166. {
  167. m_polyMeshDetail.reset(rcAllocPolyMeshDetail());
  168. if (!m_polyMeshDetail)
  169. {
  170. AZ_Error("Navigation", false, "buildNavigation: Out of memory while allocating detail mesh.");
  171. return false;
  172. }
  173. if (!rcBuildPolyMeshDetail(m_context, *m_polyMesh, *m_compactHeightfield, m_config.detailSampleDist, m_config.detailSampleMaxError, *m_polyMeshDetail))
  174. {
  175. AZ_Error("Navigation", false, "buildNavigation: Could not build detail mesh.");
  176. return false;
  177. }
  178. m_compactHeightfield = nullptr;
  179. m_contourSet = nullptr;
  180. return true;
  181. }
  182. NavigationTileData RecastProcessing::CreateDetourData(TileGeometry* geom, const RecastNavigationMeshConfig& meshConfig)
  183. {
  184. if (m_config.maxVertsPerPoly <= DT_VERTS_PER_POLYGON)
  185. {
  186. NavigationTileData navigationTileData;
  187. // Update poly flags from areas.
  188. for (int i = 0; i < m_polyMesh->npolys; ++i)
  189. {
  190. if (m_polyMesh->areas[i] == RC_WALKABLE_AREA)
  191. {
  192. m_polyMesh->flags[i] = RC_WALKABLE_AREA;
  193. }
  194. }
  195. dtNavMeshCreateParams params = {};
  196. params.verts = m_polyMesh->verts;
  197. params.vertCount = m_polyMesh->nverts;
  198. params.polys = m_polyMesh->polys;
  199. params.polyAreas = m_polyMesh->areas;
  200. params.polyFlags = m_polyMesh->flags;
  201. params.polyCount = m_polyMesh->npolys;
  202. params.nvp = m_polyMesh->nvp;
  203. params.detailMeshes = m_polyMeshDetail->meshes;
  204. params.detailVerts = m_polyMeshDetail->verts;
  205. params.detailVertsCount = m_polyMeshDetail->nverts;
  206. params.detailTris = m_polyMeshDetail->tris;
  207. params.detailTriCount = m_polyMeshDetail->ntris;
  208. params.offMeshConVerts = nullptr;
  209. params.offMeshConRad = nullptr;
  210. params.offMeshConDir = nullptr;
  211. params.offMeshConAreas = nullptr;
  212. params.offMeshConFlags = nullptr;
  213. params.offMeshConUserID = nullptr;
  214. params.offMeshConCount = 0;
  215. params.walkableHeight = meshConfig.m_agentHeight;
  216. params.walkableRadius = meshConfig.m_agentRadius;
  217. params.walkableClimb = meshConfig.m_agentMaxClimb;
  218. rcVcopy(params.bmin, m_polyMesh->bmin);
  219. rcVcopy(params.bmax, m_polyMesh->bmax);
  220. params.cs = m_config.cs;
  221. params.ch = m_config.ch;
  222. params.buildBvTree = false;
  223. params.tileX = geom->m_tileX;
  224. params.tileY = geom->m_tileY;
  225. params.tileLayer = 0; // This can be used to provide vertical layers when navigation map has multiple levels.
  226. if (!dtCreateNavMeshData(&params, &navigationTileData.m_data, &navigationTileData.m_size))
  227. {
  228. // Empty tile
  229. return {};
  230. }
  231. return navigationTileData;
  232. }
  233. return {};
  234. }
  235. } // namespace RecastNavigation