VoxelRasterization.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. namespace Pathfinding.Voxels {
  4. /// <summary>
  5. /// Voxelizer for recast graphs.
  6. ///
  7. /// In comments: units wu are World Units, vx are Voxels
  8. /// </summary>
  9. public partial class Voxelize {
  10. public List<RasterizationMesh> inputMeshes;
  11. /// <summary>Maximum ledge height that is considered to still be traversable. [Limit: >=0] [Units: vx]</summary>
  12. public readonly int voxelWalkableClimb;
  13. /// <summary>
  14. /// Minimum floor to 'ceiling' height that will still allow the floor area to
  15. /// be considered walkable. [Limit: >= 3] [Units: vx]
  16. /// </summary>
  17. public readonly uint voxelWalkableHeight;
  18. /// <summary>The xz-plane cell size to use for fields. [Limit: > 0] [Units: wu]</summary>
  19. public readonly float cellSize = 0.2F;
  20. /// <summary>The y-axis cell size to use for fields. [Limit: > 0] [Units: wu]</summary>
  21. public readonly float cellHeight = 0.1F;
  22. public int minRegionSize = 100;
  23. /// <summary>The size of the non-navigable border around the heightfield. [Limit: >=0] [Units: vx]</summary>
  24. public int borderSize = 0;
  25. /// <summary>The maximum allowed length for contour edges along the border of the mesh. [Limit: >= 0] [Units: vx]</summary>
  26. public float maxEdgeLength = 20;
  27. /// <summary>The maximum slope that is considered walkable. [Limits: 0 <= value < 90] [Units: Degrees]</summary>
  28. public float maxSlope = 30;
  29. public RecastGraph.RelevantGraphSurfaceMode relevantGraphSurfaceMode;
  30. /// <summary>The world AABB to rasterize</summary>
  31. public Bounds forcedBounds;
  32. public VoxelArea voxelArea;
  33. public VoxelContourSet countourSet;
  34. /// <summary>Transform from voxel space to world space</summary>
  35. Pathfinding.Util.GraphTransform transform;
  36. /// <summary>Transform from voxel space to graph space</summary>
  37. public Pathfinding.Util.GraphTransform transformVoxel2Graph { get; private set; }
  38. /// <summary>
  39. /// Width in voxels.
  40. /// Must match the <see cref="forcedBounds"/>
  41. /// </summary>
  42. public int width;
  43. /// <summary>
  44. /// Depth in voxels.
  45. /// Must match the <see cref="forcedBounds"/>
  46. /// </summary>
  47. public int depth;
  48. #region Debug
  49. Vector3 voxelOffset = Vector3.zero;
  50. public Vector3 CompactSpanToVector (int x, int z, int i) {
  51. return voxelOffset+new Vector3((x+0.5f)*cellSize, voxelArea.compactSpans[i].y*cellHeight, (z+0.5f)*cellSize);
  52. }
  53. public void VectorToIndex (Vector3 p, out int x, out int z) {
  54. p -= voxelOffset;
  55. x = Mathf.RoundToInt((p.x / cellSize) - 0.5f);
  56. z = Mathf.RoundToInt((p.z / cellSize) - 0.5f);
  57. }
  58. #endregion
  59. #region Constants /// <summary>@name Constants @{</summary>
  60. public const uint NotConnected = 0x3f;
  61. /// <summary>Unmotivated variable, but let's clamp the layers at 65535</summary>
  62. const int MaxLayers = 65535;
  63. /// <summary>TODO: : Check up on this variable</summary>
  64. const int MaxRegions = 500;
  65. const int UnwalkableArea = 0;
  66. /// <summary>
  67. /// If heightfield region ID has the following bit set, the region is on border area
  68. /// and excluded from many calculations.
  69. /// </summary>
  70. const ushort BorderReg = 0x8000;
  71. /// <summary>
  72. /// If contour region ID has the following bit set, the vertex will be later
  73. /// removed in order to match the segments and vertices at tile boundaries.
  74. /// </summary>
  75. const int RC_BORDER_VERTEX = 0x10000;
  76. const int RC_AREA_BORDER = 0x20000;
  77. const int VERTEX_BUCKET_COUNT = 1<<12;
  78. /// <summary>Tessellate wall edges</summary>
  79. public const int RC_CONTOUR_TESS_WALL_EDGES = 1 << 0;
  80. /// <summary>Tessellate edges between areas</summary>
  81. public const int RC_CONTOUR_TESS_AREA_EDGES = 1 << 1;
  82. /// <summary>Tessellate edges at the border of the tile</summary>
  83. public const int RC_CONTOUR_TESS_TILE_EDGES = 1 << 2;
  84. /// <summary>Mask used with contours to extract region id.</summary>
  85. const int ContourRegMask = 0xffff;
  86. #endregion /// <summary>@}</summary>
  87. readonly Vector3 cellScale;
  88. public Voxelize (float ch, float cs, float walkableClimb, float walkableHeight, float maxSlope, float maxEdgeLength) {
  89. cellSize = cs;
  90. cellHeight = ch;
  91. this.maxSlope = maxSlope;
  92. cellScale = new Vector3(cellSize, cellHeight, cellSize);
  93. voxelWalkableHeight = (uint)(walkableHeight/cellHeight);
  94. voxelWalkableClimb = Mathf.RoundToInt(walkableClimb/cellHeight);
  95. this.maxEdgeLength = maxEdgeLength;
  96. }
  97. public void Init () {
  98. // Initialize the voxel area
  99. if (voxelArea == null || voxelArea.width != width || voxelArea.depth != depth)
  100. voxelArea = new VoxelArea(width, depth);
  101. else voxelArea.Reset();
  102. }
  103. public void VoxelizeInput (Pathfinding.Util.GraphTransform graphTransform, Bounds graphSpaceBounds) {
  104. AstarProfiler.StartProfile("Build Navigation Mesh");
  105. AstarProfiler.StartProfile("Voxelizing - Step 1");
  106. // Transform from voxel space to graph space.
  107. // then scale from voxel space (one unit equals one voxel)
  108. // Finally add min
  109. Matrix4x4 voxelMatrix = Matrix4x4.TRS(graphSpaceBounds.min, Quaternion.identity, Vector3.one) * Matrix4x4.Scale(new Vector3(cellSize, cellHeight, cellSize));
  110. transformVoxel2Graph = new Pathfinding.Util.GraphTransform(voxelMatrix);
  111. // Transform from voxel space to world space
  112. // add half a voxel to fix rounding
  113. transform = graphTransform * voxelMatrix * Matrix4x4.TRS(new Vector3(0.5f, 0, 0.5f), Quaternion.identity, Vector3.one);
  114. int maximumVoxelYCoord = (int)(graphSpaceBounds.size.y / cellHeight);
  115. AstarProfiler.EndProfile("Voxelizing - Step 1");
  116. AstarProfiler.StartProfile("Voxelizing - Step 2 - Init");
  117. // Cosine of the slope limit in voxel space (some tweaks are needed because the voxel space might be stretched out along the y axis)
  118. float slopeLimit = Mathf.Cos(Mathf.Atan(Mathf.Tan(maxSlope*Mathf.Deg2Rad)*(cellSize/cellHeight)));
  119. // Temporary arrays used for rasterization
  120. var clipperOrig = new VoxelPolygonClipper(3);
  121. var clipperX1 = new VoxelPolygonClipper(7);
  122. var clipperX2 = new VoxelPolygonClipper(7);
  123. var clipperZ1 = new VoxelPolygonClipper(7);
  124. var clipperZ2 = new VoxelPolygonClipper(7);
  125. if (inputMeshes == null) throw new System.NullReferenceException("inputMeshes not set");
  126. // Find the largest lengths of vertex arrays and check for meshes which can be skipped
  127. int maxVerts = 0;
  128. for (int m = 0; m < inputMeshes.Count; m++) {
  129. maxVerts = System.Math.Max(inputMeshes[m].vertices.Length, maxVerts);
  130. }
  131. // Create buffer, here vertices will be stored multiplied with the local-to-voxel-space matrix
  132. var verts = new Vector3[maxVerts];
  133. AstarProfiler.EndProfile("Voxelizing - Step 2 - Init");
  134. AstarProfiler.StartProfile("Voxelizing - Step 2");
  135. // This loop is the hottest place in the whole rasterization process
  136. // it usually accounts for around 50% of the time
  137. for (int m = 0; m < inputMeshes.Count; m++) {
  138. RasterizationMesh mesh = inputMeshes[m];
  139. var meshMatrix = mesh.matrix;
  140. // Flip the orientation of all faces if the mesh is scaled in such a way
  141. // that the face orientations would change
  142. // This happens for example if a mesh has a negative scale along an odd number of axes
  143. // e.g it happens for the scale (-1, 1, 1) but not for (-1, -1, 1) or (1,1,1)
  144. var flipOrientation = VectorMath.ReversesFaceOrientations(meshMatrix);
  145. Vector3[] vs = mesh.vertices;
  146. int[] tris = mesh.triangles;
  147. int trisLength = mesh.numTriangles;
  148. // Transform vertices first to world space and then to voxel space
  149. for (int i = 0; i < vs.Length; i++) verts[i] = transform.InverseTransform(meshMatrix.MultiplyPoint3x4(vs[i]));
  150. int mesharea = mesh.area;
  151. for (int i = 0; i < trisLength; i += 3) {
  152. Vector3 p1 = verts[tris[i]];
  153. Vector3 p2 = verts[tris[i+1]];
  154. Vector3 p3 = verts[tris[i+2]];
  155. if (flipOrientation) {
  156. var tmp = p1;
  157. p1 = p3;
  158. p3 = tmp;
  159. }
  160. int minX = (int)(Utility.Min(p1.x, p2.x, p3.x));
  161. int minZ = (int)(Utility.Min(p1.z, p2.z, p3.z));
  162. int maxX = (int)System.Math.Ceiling(Utility.Max(p1.x, p2.x, p3.x));
  163. int maxZ = (int)System.Math.Ceiling(Utility.Max(p1.z, p2.z, p3.z));
  164. minX = Mathf.Clamp(minX, 0, voxelArea.width-1);
  165. maxX = Mathf.Clamp(maxX, 0, voxelArea.width-1);
  166. minZ = Mathf.Clamp(minZ, 0, voxelArea.depth-1);
  167. maxZ = Mathf.Clamp(maxZ, 0, voxelArea.depth-1);
  168. // Check if the mesh is completely out of bounds
  169. if (minX >= voxelArea.width || minZ >= voxelArea.depth || maxX <= 0 || maxZ <= 0) continue;
  170. Vector3 normal;
  171. int area;
  172. //AstarProfiler.StartProfile ("Rasterize...");
  173. normal = Vector3.Cross(p2-p1, p3-p1);
  174. float cosSlopeAngle = Vector3.Dot(normal.normalized, Vector3.up);
  175. if (cosSlopeAngle < slopeLimit) {
  176. area = UnwalkableArea;
  177. } else {
  178. area = 1 + mesharea;
  179. }
  180. clipperOrig[0] = p1;
  181. clipperOrig[1] = p2;
  182. clipperOrig[2] = p3;
  183. clipperOrig.n = 3;
  184. for (int x = minX; x <= maxX; x++) {
  185. clipperOrig.ClipPolygonAlongX(ref clipperX1, 1f, -x+0.5f);
  186. if (clipperX1.n < 3) {
  187. continue;
  188. }
  189. clipperX1.ClipPolygonAlongX(ref clipperX2, -1F, x+0.5F);
  190. if (clipperX2.n < 3) {
  191. continue;
  192. }
  193. float clampZ1 = clipperX2.z[0];
  194. float clampZ2 = clipperX2.z[0];
  195. for (int q = 1; q < clipperX2.n; q++) {
  196. float val = clipperX2.z[q];
  197. clampZ1 = System.Math.Min(clampZ1, val);
  198. clampZ2 = System.Math.Max(clampZ2, val);
  199. }
  200. int clampZ1I = Mathf.Clamp((int)System.Math.Round(clampZ1), 0, voxelArea.depth-1);
  201. int clampZ2I = Mathf.Clamp((int)System.Math.Round(clampZ2), 0, voxelArea.depth-1);
  202. for (int z = clampZ1I; z <= clampZ2I; z++) {
  203. clipperX2.ClipPolygonAlongZWithYZ(ref clipperZ1, 1F, -z+0.5F);
  204. if (clipperZ1.n < 3) {
  205. continue;
  206. }
  207. clipperZ1.ClipPolygonAlongZWithY(ref clipperZ2, -1F, z+0.5F);
  208. if (clipperZ2.n < 3) {
  209. continue;
  210. }
  211. float sMin = clipperZ2.y[0];
  212. float sMax = clipperZ2.y[0];
  213. for (int q = 1; q < clipperZ2.n; q++) {
  214. float val = clipperZ2.y[q];
  215. sMin = System.Math.Min(sMin, val);
  216. sMax = System.Math.Max(sMax, val);
  217. }
  218. int maxi = (int)System.Math.Ceiling(sMax);
  219. // Skip span if below or above the bounding box
  220. if (maxi >= 0 && sMin <= maximumVoxelYCoord) {
  221. // Make sure mini >= 0
  222. int mini = System.Math.Max(0, (int)sMin);
  223. // Make sure the span is at least 1 voxel high
  224. maxi = System.Math.Max(mini+1, maxi);
  225. voxelArea.AddLinkedSpan(z*voxelArea.width+x, (uint)mini, (uint)maxi, area, voxelWalkableClimb);
  226. }
  227. }
  228. }
  229. }
  230. //AstarProfiler.EndFastProfile(0);
  231. //AstarProfiler.EndProfile ("Rasterize...");
  232. }
  233. AstarProfiler.EndProfile("Voxelizing - Step 2");
  234. }
  235. public void DebugDrawSpans () {
  236. #if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  237. int wd = voxelArea.width*voxelArea.depth;
  238. var min = forcedBounds.min;
  239. LinkedVoxelSpan[] spans = voxelArea.linkedSpans;
  240. for (int z = 0, pz = 0; z < wd; z += voxelArea.width, pz++) {
  241. for (int x = 0; x < voxelArea.width; x++) {
  242. for (int s = z+x; s != -1 && spans[s].bottom != VoxelArea.InvalidSpanValue; s = spans[s].next) {
  243. uint bottom = spans[s].top;
  244. uint top = spans[s].next != -1 ? spans[spans[s].next].bottom : VoxelArea.MaxHeight;
  245. if (bottom > top) {
  246. Debug.Log(bottom + " " + top);
  247. Debug.DrawLine(new Vector3(x*cellSize, bottom*cellHeight, pz*cellSize)+min, new Vector3(x*cellSize, top*cellHeight, pz*cellSize)+min, Color.yellow, 1);
  248. }
  249. //Debug.DrawRay (p,voxelArea.VectorDirection[d]*cellSize*0.5F,Color.red);
  250. if (top - bottom < voxelWalkableHeight) {
  251. //spans[s].area = UnwalkableArea;
  252. }
  253. }
  254. }
  255. }
  256. #else
  257. Debug.LogError("This debug method only works with !ASTAR_RECAST_CLASS_BASED_LINKED_LIST");
  258. #endif
  259. }
  260. public void BuildCompactField () {
  261. AstarProfiler.StartProfile("Build Compact Voxel Field");
  262. //Build compact representation
  263. int spanCount = voxelArea.GetSpanCount();
  264. voxelArea.compactSpanCount = spanCount;
  265. if (voxelArea.compactSpans == null || voxelArea.compactSpans.Length < spanCount) {
  266. voxelArea.compactSpans = new CompactVoxelSpan[spanCount];
  267. voxelArea.areaTypes = new int[spanCount];
  268. }
  269. uint idx = 0;
  270. int w = voxelArea.width;
  271. int d = voxelArea.depth;
  272. int wd = w*d;
  273. if (this.voxelWalkableHeight >= 0xFFFF) {
  274. Debug.LogWarning("Too high walkable height to guarantee correctness. Increase voxel height or lower walkable height.");
  275. }
  276. #if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  277. LinkedVoxelSpan[] spans = voxelArea.linkedSpans;
  278. #endif
  279. //Parallel.For (0, voxelArea.depth, delegate (int pz) {
  280. for (int z = 0, pz = 0; z < wd; z += w, pz++) {
  281. for (int x = 0; x < w; x++) {
  282. #if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  283. int spanIndex = x+z;
  284. if (spans[spanIndex].bottom == VoxelArea.InvalidSpanValue) {
  285. voxelArea.compactCells[x+z] = new CompactVoxelCell(0, 0);
  286. continue;
  287. }
  288. uint index = idx;
  289. uint count = 0;
  290. //Vector3 p = new Vector3(x,0,pz)*cellSize+voxelOffset;
  291. while (spanIndex != -1) {
  292. if (spans[spanIndex].area != UnwalkableArea) {
  293. int bottom = (int)spans[spanIndex].top;
  294. int next = spans[spanIndex].next;
  295. int top = next != -1 ? (int)spans[next].bottom : VoxelArea.MaxHeightInt;
  296. voxelArea.compactSpans[idx] = new CompactVoxelSpan((ushort)(bottom > 0xFFFF ? 0xFFFF : bottom), (uint)(top-bottom > 0xFFFF ? 0xFFFF : top-bottom));
  297. voxelArea.areaTypes[idx] = spans[spanIndex].area;
  298. idx++;
  299. count++;
  300. }
  301. spanIndex = spans[spanIndex].next;
  302. }
  303. voxelArea.compactCells[x+z] = new CompactVoxelCell(index, count);
  304. #else
  305. VoxelSpan s = voxelArea.cells[x+z].firstSpan;
  306. if (s == null) {
  307. voxelArea.compactCells[x+z] = new CompactVoxelCell(0, 0);
  308. continue;
  309. }
  310. uint index = idx;
  311. uint count = 0;
  312. //Vector3 p = new Vector3(x,0,pz)*cellSize+voxelOffset;
  313. while (s != null) {
  314. if (s.area != UnwalkableArea) {
  315. int bottom = (int)s.top;
  316. int top = s.next != null ? (int)s.next.bottom : VoxelArea.MaxHeightInt;
  317. voxelArea.compactSpans[idx] = new CompactVoxelSpan((ushort)Mathf.Clamp(bottom, 0, 0xffff), (uint)Mathf.Clamp(top-bottom, 0, 0xffff));
  318. voxelArea.areaTypes[idx] = s.area;
  319. idx++;
  320. count++;
  321. }
  322. s = s.next;
  323. }
  324. voxelArea.compactCells[x+z] = new CompactVoxelCell(index, count);
  325. #endif
  326. }
  327. }
  328. AstarProfiler.EndProfile("Build Compact Voxel Field");
  329. }
  330. public void BuildVoxelConnections () {
  331. AstarProfiler.StartProfile("Build Voxel Connections");
  332. int wd = voxelArea.width*voxelArea.depth;
  333. CompactVoxelSpan[] spans = voxelArea.compactSpans;
  334. CompactVoxelCell[] cells = voxelArea.compactCells;
  335. // Build voxel connections
  336. for (int z = 0, pz = 0; z < wd; z += voxelArea.width, pz++) {
  337. for (int x = 0; x < voxelArea.width; x++) {
  338. CompactVoxelCell c = cells[x+z];
  339. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; i++) {
  340. CompactVoxelSpan s = spans[i];
  341. spans[i].con = 0xFFFFFFFF;
  342. for (int d = 0; d < 4; d++) {
  343. int nx = x+voxelArea.DirectionX[d];
  344. int nz = z+voxelArea.DirectionZ[d];
  345. if (nx < 0 || nz < 0 || nz >= wd || nx >= voxelArea.width) {
  346. continue;
  347. }
  348. CompactVoxelCell nc = cells[nx+nz];
  349. for (int k = (int)nc.index, nk = (int)(nc.index+nc.count); k < nk; k++) {
  350. CompactVoxelSpan ns = spans[k];
  351. int bottom = System.Math.Max(s.y, ns.y);
  352. int top = System.Math.Min((int)s.y+(int)s.h, (int)ns.y+(int)ns.h);
  353. if ((top-bottom) >= voxelWalkableHeight && System.Math.Abs((int)ns.y - (int)s.y) <= voxelWalkableClimb) {
  354. uint connIdx = (uint)k - nc.index;
  355. if (connIdx > MaxLayers) {
  356. Debug.LogError("Too many layers");
  357. continue;
  358. }
  359. spans[i].SetConnection(d, connIdx);
  360. break;
  361. }
  362. }
  363. }
  364. }
  365. }
  366. }
  367. AstarProfiler.EndProfile("Build Voxel Connections");
  368. }
  369. void DrawLine (int a, int b, int[] indices, int[] verts, Color color) {
  370. int p1 = (indices[a] & 0x0fffffff) * 4;
  371. int p2 = (indices[b] & 0x0fffffff) * 4;
  372. Debug.DrawLine(VoxelToWorld(verts[p1+0], verts[p1+1], verts[p1+2]), VoxelToWorld(verts[p2+0], verts[p2+1], verts[p2+2]), color);
  373. }
  374. #if ASTARDEBUG
  375. Vector3 ConvertPos (int x, int y, int z) {
  376. Vector3 p = Vector3.Scale(
  377. new Vector3(
  378. x+0.5F,
  379. y,
  380. (z/(float)voxelArea.width)+0.5F
  381. )
  382. , cellScale)
  383. +voxelOffset;
  384. return p;
  385. }
  386. #endif
  387. /// <summary>
  388. /// Convert from voxel coordinates to world coordinates.
  389. /// (0,0,0) in voxel coordinates is a bottom corner of the bounding box.
  390. /// (1,0,0) is one voxel in the +X direction of that.
  391. /// </summary>
  392. public Vector3 VoxelToWorld (int x, int y, int z) {
  393. Vector3 p = Vector3.Scale(
  394. new Vector3(
  395. x,
  396. y,
  397. z
  398. )
  399. , cellScale)
  400. +voxelOffset;
  401. return p;
  402. }
  403. /// <summary>
  404. /// Convert from voxel coordinates to world coordinates.
  405. /// (0,0,0) in voxel coordinates is a bottom corner of the bounding box.
  406. /// (1,0,0) is one voxel in the +X direction of that.
  407. /// </summary>
  408. public Int3 VoxelToWorldInt3 (Int3 voxelPosition) {
  409. var pos = voxelPosition * Int3.Precision;
  410. pos = new Int3(Mathf.RoundToInt(pos.x * cellScale.x), Mathf.RoundToInt(pos.y * cellScale.y), Mathf.RoundToInt(pos.z * cellScale.z));
  411. return pos +(Int3)voxelOffset;
  412. }
  413. Vector3 ConvertPosWithoutOffset (int x, int y, int z) {
  414. Vector3 p = Vector3.Scale(
  415. new Vector3(
  416. x,
  417. y,
  418. (z/(float)voxelArea.width)
  419. )
  420. , cellScale)
  421. +voxelOffset;
  422. return p;
  423. }
  424. Vector3 ConvertPosition (int x, int z, int i) {
  425. CompactVoxelSpan s = voxelArea.compactSpans[i];
  426. return new Vector3(x*cellSize, s.y*cellHeight, (z/(float)voxelArea.width)*cellSize)+voxelOffset;
  427. }
  428. public void ErodeWalkableArea (int radius) {
  429. AstarProfiler.StartProfile("Erode Walkable Area");
  430. ushort[] src = voxelArea.tmpUShortArr;
  431. if (src == null || src.Length < voxelArea.compactSpanCount) {
  432. src = voxelArea.tmpUShortArr = new ushort[voxelArea.compactSpanCount];
  433. }
  434. // Set all elements in src to 0xffff
  435. Pathfinding.Util.Memory.MemSet<ushort>(src, 0xffff, sizeof(ushort));
  436. CalculateDistanceField(src);
  437. for (int i = 0; i < src.Length; i++) {
  438. //Note multiplied with 2 because the distance field increments distance by 2 for each voxel (and 3 for diagonal)
  439. if (src[i] < radius*2) {
  440. voxelArea.areaTypes[i] = UnwalkableArea;
  441. }
  442. }
  443. AstarProfiler.EndProfile("Erode Walkable Area");
  444. }
  445. public void BuildDistanceField () {
  446. AstarProfiler.StartProfile("Build Distance Field");
  447. ushort[] src = voxelArea.tmpUShortArr;
  448. if (src == null || src.Length < voxelArea.compactSpanCount) {
  449. src = voxelArea.tmpUShortArr = new ushort[voxelArea.compactSpanCount];
  450. }
  451. // Set all elements in src to 0xffff
  452. Pathfinding.Util.Memory.MemSet<ushort>(src, 0xffff, sizeof(ushort));
  453. voxelArea.maxDistance = CalculateDistanceField(src);
  454. ushort[] dst = voxelArea.dist;
  455. if (dst == null || dst.Length < voxelArea.compactSpanCount) {
  456. dst = new ushort[voxelArea.compactSpanCount];
  457. }
  458. dst = BoxBlur(src, dst);
  459. voxelArea.dist = dst;
  460. AstarProfiler.EndProfile("Build Distance Field");
  461. }
  462. /// <summary>TODO: Complete the ErodeVoxels function translation</summary>
  463. [System.Obsolete("This function is not complete and should not be used")]
  464. public void ErodeVoxels (int radius) {
  465. if (radius > 255) {
  466. Debug.LogError("Max Erode Radius is 255");
  467. radius = 255;
  468. }
  469. int wd = voxelArea.width*voxelArea.depth;
  470. int[] dist = new int[voxelArea.compactSpanCount];
  471. for (int i = 0; i < dist.Length; i++) {
  472. dist[i] = 0xFF;
  473. }
  474. for (int z = 0; z < wd; z += voxelArea.width) {
  475. for (int x = 0; x < voxelArea.width; x++) {
  476. CompactVoxelCell c = voxelArea.compactCells[x+z];
  477. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; i++) {
  478. if (voxelArea.areaTypes[i] != UnwalkableArea) {
  479. CompactVoxelSpan s = voxelArea.compactSpans[i];
  480. int nc = 0;
  481. for (int dir = 0; dir < 4; dir++) {
  482. if (s.GetConnection(dir) != NotConnected)
  483. nc++;
  484. }
  485. //At least one missing neighbour
  486. if (nc != 4) {
  487. dist[i] = 0;
  488. }
  489. }
  490. }
  491. }
  492. }
  493. //int nd = 0;
  494. //Pass 1
  495. /*for (int z=0;z < wd;z += voxelArea.width) {
  496. * for (int x=0;x < voxelArea.width;x++) {
  497. *
  498. * CompactVoxelCell c = voxelArea.compactCells[x+z];
  499. *
  500. * for (int i= (int)c.index, ci = (int)(c.index+c.count); i < ci; i++) {
  501. * CompactVoxelSpan s = voxelArea.compactSpans[i];
  502. *
  503. * if (s.GetConnection (0) != NotConnected) {
  504. * // (-1,0)
  505. * int nx = x+voxelArea.DirectionX[0];
  506. * int nz = z+voxelArea.DirectionZ[0];
  507. *
  508. * int ni = (int)(voxelArea.compactCells[nx+nz].index+s.GetConnection (0));
  509. * CompactVoxelSpan ns = voxelArea.compactSpans[ni];
  510. *
  511. * if (dist[ni]+2 < dist[i]) {
  512. * dist[i] = (ushort)(dist[ni]+2);
  513. * }
  514. *
  515. * if (ns.GetConnection (3) != NotConnected) {
  516. * // (-1,0) + (0,-1) = (-1,-1)
  517. * int nnx = nx+voxelArea.DirectionX[3];
  518. * int nnz = nz+voxelArea.DirectionZ[3];
  519. *
  520. * int nni = (int)(voxelArea.compactCells[nnx+nnz].index+ns.GetConnection (3));
  521. *
  522. * if (src[nni]+3 < src[i]) {
  523. * src[i] = (ushort)(src[nni]+3);
  524. * }
  525. * }
  526. * }
  527. *
  528. * if (s.GetConnection (3) != NotConnected) {
  529. * // (0,-1)
  530. * int nx = x+voxelArea.DirectionX[3];
  531. * int nz = z+voxelArea.DirectionZ[3];
  532. *
  533. * int ni = (int)(voxelArea.compactCells[nx+nz].index+s.GetConnection (3));
  534. *
  535. * if (src[ni]+2 < src[i]) {
  536. * src[i] = (ushort)(src[ni]+2);
  537. * }
  538. *
  539. * CompactVoxelSpan ns = voxelArea.compactSpans[ni];
  540. *
  541. * if (ns.GetConnection (2) != NotConnected) {
  542. *
  543. * // (0,-1) + (1,0) = (1,-1)
  544. * int nnx = nx+voxelArea.DirectionX[2];
  545. * int nnz = nz+voxelArea.DirectionZ[2];
  546. *
  547. * voxelOffset nni = (int)(voxelArea.compactCells[nnx+nnz].index+ns.GetConnection (2));
  548. *
  549. * if (src[nni]+3 < src[i]) {
  550. * src[i] = (ushort)(src[nni]+3);
  551. * }
  552. * }
  553. * }
  554. * }
  555. * }
  556. * }*/
  557. }
  558. public void FilterLowHeightSpans (uint voxelWalkableHeight, float cs, float ch) {
  559. int wd = voxelArea.width*voxelArea.depth;
  560. //Filter all ledges
  561. #if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  562. LinkedVoxelSpan[] spans = voxelArea.linkedSpans;
  563. for (int z = 0, pz = 0; z < wd; z += voxelArea.width, pz++) {
  564. for (int x = 0; x < voxelArea.width; x++) {
  565. for (int s = z+x; s != -1 && spans[s].bottom != VoxelArea.InvalidSpanValue; s = spans[s].next) {
  566. uint bottom = spans[s].top;
  567. uint top = spans[s].next != -1 ? spans[spans[s].next].bottom : VoxelArea.MaxHeight;
  568. if (top - bottom < voxelWalkableHeight) {
  569. spans[s].area = UnwalkableArea;
  570. }
  571. }
  572. }
  573. }
  574. #else
  575. for (int z = 0, pz = 0; z < wd; z += voxelArea.width, pz++) {
  576. for (int x = 0; x < voxelArea.width; x++) {
  577. for (VoxelSpan s = voxelArea.cells[z+x].firstSpan; s != null; s = s.next) {
  578. uint bottom = s.top;
  579. uint top = s.next != null ? s.next.bottom : VoxelArea.MaxHeight;
  580. if (top - bottom < voxelWalkableHeight) {
  581. s.area = UnwalkableArea;
  582. }
  583. }
  584. }
  585. }
  586. #endif
  587. }
  588. //Code almost completely ripped from Recast
  589. public void FilterLedges (uint voxelWalkableHeight, int voxelWalkableClimb, float cs, float ch) {
  590. int wd = voxelArea.width*voxelArea.depth;
  591. #if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  592. LinkedVoxelSpan[] spans = voxelArea.linkedSpans;
  593. int[] DirectionX = voxelArea.DirectionX;
  594. int[] DirectionZ = voxelArea.DirectionZ;
  595. #endif
  596. int width = voxelArea.width;
  597. //Filter all ledges
  598. for (int z = 0, pz = 0; z < wd; z += width, pz++) {
  599. for (int x = 0; x < width; x++) {
  600. #if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  601. if (spans[x+z].bottom == VoxelArea.InvalidSpanValue) continue;
  602. for (int s = x+z; s != -1; s = spans[s].next) {
  603. //Skip non-walkable spans
  604. if (spans[s].area == UnwalkableArea) {
  605. continue;
  606. }
  607. int bottom = (int)spans[s].top;
  608. int top = spans[s].next != -1 ? (int)spans[spans[s].next].bottom : VoxelArea.MaxHeightInt;
  609. int minHeight = VoxelArea.MaxHeightInt;
  610. int aMinHeight = (int)spans[s].top;
  611. int aMaxHeight = aMinHeight;
  612. for (int d = 0; d < 4; d++) {
  613. int nx = x+DirectionX[d];
  614. int nz = z+DirectionZ[d];
  615. //Skip out-of-bounds points
  616. if (nx < 0 || nz < 0 || nz >= wd || nx >= width) {
  617. spans[s].area = UnwalkableArea;
  618. break;
  619. }
  620. int nsx = nx+nz;
  621. int nbottom = -voxelWalkableClimb;
  622. int ntop = spans[nsx].bottom != VoxelArea.InvalidSpanValue ? (int)spans[nsx].bottom : VoxelArea.MaxHeightInt;
  623. if (System.Math.Min(top, ntop) - System.Math.Max(bottom, nbottom) > voxelWalkableHeight) {
  624. minHeight = System.Math.Min(minHeight, nbottom - bottom);
  625. }
  626. //Loop through spans
  627. if (spans[nsx].bottom != VoxelArea.InvalidSpanValue) {
  628. for (int ns = nsx; ns != -1; ns = spans[ns].next) {
  629. nbottom = (int)spans[ns].top;
  630. ntop = spans[ns].next != -1 ? (int)spans[spans[ns].next].bottom : VoxelArea.MaxHeightInt;
  631. if (System.Math.Min(top, ntop) - System.Math.Max(bottom, nbottom) > voxelWalkableHeight) {
  632. minHeight = System.Math.Min(minHeight, nbottom - bottom);
  633. if (System.Math.Abs(nbottom - bottom) <= voxelWalkableClimb) {
  634. if (nbottom < aMinHeight) { aMinHeight = nbottom; }
  635. if (nbottom > aMaxHeight) { aMaxHeight = nbottom; }
  636. }
  637. }
  638. }
  639. }
  640. }
  641. if (minHeight < -voxelWalkableClimb || (aMaxHeight - aMinHeight) > voxelWalkableClimb) {
  642. spans[s].area = UnwalkableArea;
  643. }
  644. }
  645. #else
  646. for (VoxelSpan s = voxelArea.cells[z+x].firstSpan; s != null; s = s.next) {
  647. //Skip non-walkable spans
  648. if (s.area == UnwalkableArea) {
  649. continue;
  650. }
  651. int bottom = (int)s.top;
  652. int top = s.next != null ? (int)s.next.bottom : VoxelArea.MaxHeightInt;
  653. int minHeight = VoxelArea.MaxHeightInt;
  654. int aMinHeight = (int)s.top;
  655. int aMaxHeight = (int)s.top;
  656. for (int d = 0; d < 4; d++) {
  657. int nx = x+voxelArea.DirectionX[d];
  658. int nz = z+voxelArea.DirectionZ[d];
  659. //Skip out-of-bounds points
  660. if (nx < 0 || nz < 0 || nz >= wd || nx >= voxelArea.width) {
  661. s.area = UnwalkableArea;
  662. break;
  663. }
  664. VoxelSpan nsx = voxelArea.cells[nx+nz].firstSpan;
  665. int nbottom = -voxelWalkableClimb;
  666. int ntop = nsx != null ? (int)nsx.bottom : VoxelArea.MaxHeightInt;
  667. if (System.Math.Min(top, ntop) - System.Math.Max(bottom, nbottom) > voxelWalkableHeight) {
  668. minHeight = System.Math.Min(minHeight, nbottom - bottom);
  669. }
  670. //Loop through spans
  671. for (VoxelSpan ns = nsx; ns != null; ns = ns.next) {
  672. nbottom = (int)ns.top;
  673. ntop = ns.next != null ? (int)ns.next.bottom : VoxelArea.MaxHeightInt;
  674. if (System.Math.Min(top, ntop) - System.Math.Max(bottom, nbottom) > voxelWalkableHeight) {
  675. minHeight = System.Math.Min(minHeight, nbottom - bottom);
  676. if (System.Math.Abs(nbottom - bottom) <= voxelWalkableClimb) {
  677. if (nbottom < aMinHeight) { aMinHeight = nbottom; }
  678. if (nbottom > aMaxHeight) { aMaxHeight = nbottom; }
  679. }
  680. }
  681. }
  682. }
  683. if (minHeight < -voxelWalkableClimb || (aMaxHeight - aMinHeight) > voxelWalkableClimb) {
  684. s.area = UnwalkableArea;
  685. }
  686. }
  687. #endif
  688. }
  689. }
  690. }
  691. }
  692. }