VoxelClasses.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. #if NETFX_CORE && !UNITY_EDITOR
  4. //using MarkerMetro.Unity.WinLegacy.IO;
  5. #endif
  6. namespace Pathfinding.Voxels {
  7. /// <summary>Stores a voxel field. </summary>
  8. public class VoxelArea {
  9. public const uint MaxHeight = 65536;
  10. public const int MaxHeightInt = 65536;
  11. /// <summary>
  12. /// Constant for default LinkedVoxelSpan top and bottom values.
  13. /// It is important with the U since ~0 != ~0U
  14. /// This can be used to check if a LinkedVoxelSpan is valid and not just the default span
  15. /// </summary>
  16. public const uint InvalidSpanValue = ~0U;
  17. /// <summary>Initial estimate on the average number of spans (layers) in the voxel representation. Should be greater or equal to 1</summary>
  18. public const float AvgSpanLayerCountEstimate = 8;
  19. /// <summary>The width of the field along the x-axis. [Limit: >= 0] [Units: vx]</summary>
  20. public readonly int width = 0;
  21. /// <summary>The depth of the field along the z-axis. [Limit: >= 0] [Units: vx]</summary>
  22. public readonly int depth = 0;
  23. #if ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  24. public VoxelCell[] cells;
  25. #endif
  26. public CompactVoxelSpan[] compactSpans;
  27. public CompactVoxelCell[] compactCells;
  28. public int compactSpanCount;
  29. public ushort[] tmpUShortArr;
  30. public int[] areaTypes;
  31. public ushort[] dist;
  32. public ushort maxDistance;
  33. public int maxRegions = 0;
  34. public int[] DirectionX;
  35. public int[] DirectionZ;
  36. public Vector3[] VectorDirection;
  37. public void Reset () {
  38. #if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  39. ResetLinkedVoxelSpans();
  40. #else
  41. for (int i = 0; i < cells.Length; i++) cells[i].firstSpan = null;
  42. #endif
  43. for (int i = 0; i < compactCells.Length; i++) {
  44. compactCells[i].count = 0;
  45. compactCells[i].index = 0;
  46. }
  47. }
  48. #if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  49. private void ResetLinkedVoxelSpans () {
  50. int len = linkedSpans.Length;
  51. linkedSpanCount = width*depth;
  52. LinkedVoxelSpan df = new LinkedVoxelSpan(InvalidSpanValue, InvalidSpanValue, -1, -1);
  53. for (int i = 0; i < len;) {
  54. // 16x unrolling, actually improves performance
  55. linkedSpans[i] = df; i++;
  56. linkedSpans[i] = df; i++;
  57. linkedSpans[i] = df; i++;
  58. linkedSpans[i] = df; i++;
  59. linkedSpans[i] = df; i++;
  60. linkedSpans[i] = df; i++;
  61. linkedSpans[i] = df; i++;
  62. linkedSpans[i] = df; i++;
  63. linkedSpans[i] = df; i++;
  64. linkedSpans[i] = df; i++;
  65. linkedSpans[i] = df; i++;
  66. linkedSpans[i] = df; i++;
  67. linkedSpans[i] = df; i++;
  68. linkedSpans[i] = df; i++;
  69. linkedSpans[i] = df; i++;
  70. linkedSpans[i] = df; i++;
  71. }
  72. removedStackCount = 0;
  73. }
  74. #endif
  75. public VoxelArea (int width, int depth) {
  76. this.width = width;
  77. this.depth = depth;
  78. int wd = width*depth;
  79. compactCells = new CompactVoxelCell[wd];
  80. #if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  81. // & ~0xF ensures it is a multiple of 16. Required for unrolling
  82. linkedSpans = new LinkedVoxelSpan[((int)(wd*AvgSpanLayerCountEstimate) + 15)& ~0xF];
  83. ResetLinkedVoxelSpans();
  84. #else
  85. cells = new VoxelCell[wd];
  86. #endif
  87. DirectionX = new int[4] { -1, 0, 1, 0 };
  88. DirectionZ = new int[4] { 0, width, 0, -width };
  89. VectorDirection = new Vector3[4] { Vector3.left, Vector3.forward, Vector3.right, Vector3.back };
  90. }
  91. public int GetSpanCountAll () {
  92. int count = 0;
  93. int wd = width*depth;
  94. for (int x = 0; x < wd; x++) {
  95. #if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  96. for (int s = x; s != -1 && linkedSpans[s].bottom != InvalidSpanValue; s = linkedSpans[s].next) {
  97. count++;
  98. }
  99. #else
  100. for (VoxelSpan s = cells[x].firstSpan; s != null; s = s.next) {
  101. count++;
  102. }
  103. #endif
  104. }
  105. return count;
  106. }
  107. public int GetSpanCount () {
  108. int count = 0;
  109. int wd = width*depth;
  110. for (int x = 0; x < wd; x++) {
  111. #if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  112. for (int s = x; s != -1 && linkedSpans[s].bottom != InvalidSpanValue; s = linkedSpans[s].next) {
  113. if (linkedSpans[s].area != 0) {
  114. count++;
  115. }
  116. }
  117. #else
  118. for (VoxelSpan s = cells[x].firstSpan; s != null; s = s.next) {
  119. if (s.area != 0) {
  120. count++;
  121. }
  122. }
  123. #endif
  124. }
  125. return count;
  126. }
  127. #if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  128. private int linkedSpanCount;
  129. public LinkedVoxelSpan[] linkedSpans;
  130. private int[] removedStack = new int[128];
  131. private int removedStackCount = 0;
  132. void PushToSpanRemovedStack (int index) {
  133. // Make sure we don't overflow the list
  134. if (removedStackCount == removedStack.Length) {
  135. // Create a new list to hold recycled values
  136. int[] st2 = new int[removedStackCount*4];
  137. System.Buffer.BlockCopy(removedStack, 0, st2, 0, removedStackCount*sizeof(int));
  138. removedStack = st2;
  139. }
  140. removedStack[removedStackCount] = index;
  141. removedStackCount++;
  142. }
  143. #endif
  144. public void AddLinkedSpan (int index, uint bottom, uint top, int area, int voxelWalkableClimb) {
  145. #if ASTAR_RECAST_CLASS_BASED_LINKED_LIST
  146. cells[index].AddSpan(bottom, top, area, voxelWalkableClimb);
  147. #else
  148. var linkedSpans = this.linkedSpans;
  149. // linkedSpans[index] is the span with the lowest y-coordinate at the position x,z such that index=x+z*width
  150. // i.e linkedSpans is a 2D array laid out in a 1D array (for performance and simplicity)
  151. // Check if there is a root span, otherwise we can just add a new (valid) span and exit
  152. if (linkedSpans[index].bottom == InvalidSpanValue) {
  153. linkedSpans[index] = new LinkedVoxelSpan(bottom, top, area);
  154. return;
  155. }
  156. int prev = -1;
  157. // Original index, the first span we visited
  158. int oindex = index;
  159. while (index != -1) {
  160. var current = linkedSpans[index];
  161. if (current.bottom > top) {
  162. // If the current span's bottom higher up than the span we want to insert's top, then they do not intersect
  163. // and we should just insert a new span here
  164. break;
  165. } else if (current.top < bottom) {
  166. // The current span and the span we want to insert do not intersect
  167. // so just skip to the next span (it might intersect)
  168. prev = index;
  169. index = current.next;
  170. } else {
  171. // Intersection! Merge the spans
  172. // Find the new bottom and top for the merged span
  173. bottom = System.Math.Min(current.bottom, bottom);
  174. top = System.Math.Max(current.top, top);
  175. // voxelWalkableClimb is flagMergeDistance, when a walkable flag is favored before an unwalkable one
  176. // So if a walkable span intersects an unwalkable span, the walkable span can be up to voxelWalkableClimb
  177. // below the unwalkable span and the merged span will still be walkable
  178. if (System.Math.Abs((int)top - (int)current.top) <= voxelWalkableClimb) {
  179. // linkedSpans[index] is the lowest span, but we might use that span's area anyway if it is walkable
  180. area = System.Math.Max(area, current.area);
  181. }
  182. // Find the next span in the linked list
  183. int next = current.next;
  184. if (prev != -1) {
  185. // There is a previous span
  186. // Remove this span from the linked list
  187. linkedSpans[prev].next = next;
  188. // Add this span index to a list for recycling
  189. PushToSpanRemovedStack(index);
  190. // Move to the next span in the list
  191. index = next;
  192. } else if (next != -1) {
  193. // This was the root span and there is a span left in the linked list
  194. // Remove this span from the linked list by assigning the next span as the root span
  195. linkedSpans[oindex] = linkedSpans[next];
  196. // Recycle the old span index
  197. PushToSpanRemovedStack(next);
  198. // Move to the next span in the list
  199. // NOP since we just removed the current span, the next span
  200. // we want to visit will have the same index as we are on now (i.e oindex)
  201. } else {
  202. // This was the root span and there are no other spans in the linked list
  203. // Just replace the root span with the merged span and exit
  204. linkedSpans[oindex] = new LinkedVoxelSpan(bottom, top, area);
  205. return;
  206. }
  207. }
  208. }
  209. // We now have a merged span that needs to be inserted
  210. // and connected with the existing spans
  211. // The new merged span will be inserted right after 'prev' (if it exists, otherwise before index)
  212. // Make sure that we have enough room in our array
  213. if (linkedSpanCount >= linkedSpans.Length) {
  214. LinkedVoxelSpan[] tmp = linkedSpans;
  215. int count = linkedSpanCount;
  216. int popped = removedStackCount;
  217. this.linkedSpans = linkedSpans = new LinkedVoxelSpan[linkedSpans.Length*2];
  218. ResetLinkedVoxelSpans();
  219. linkedSpanCount = count;
  220. removedStackCount = popped;
  221. for (int i = 0; i < linkedSpanCount; i++) {
  222. linkedSpans[i] = tmp[i];
  223. }
  224. }
  225. // Take a node from the recycling stack if possible
  226. // Otherwise create a new node (well, just a new index really)
  227. int nextIndex;
  228. if (removedStackCount > 0) {
  229. removedStackCount--;
  230. nextIndex = removedStack[removedStackCount];
  231. } else {
  232. nextIndex = linkedSpanCount;
  233. linkedSpanCount++;
  234. }
  235. if (prev != -1) {
  236. //span.next = prev.next;
  237. //prev.next = span;
  238. linkedSpans[nextIndex] = new LinkedVoxelSpan(bottom, top, area, linkedSpans[prev].next);
  239. linkedSpans[prev].next = nextIndex;
  240. } else {
  241. //span.next = firstSpan;
  242. //firstSpan = span;
  243. linkedSpans[nextIndex] = linkedSpans[oindex];
  244. linkedSpans[oindex] = new LinkedVoxelSpan(bottom, top, area, nextIndex);
  245. }
  246. #endif
  247. }
  248. }
  249. public struct LinkedVoxelSpan {
  250. public uint bottom;
  251. public uint top;
  252. public int next;
  253. /*Area
  254. * 0 is an unwalkable span (triangle face down)
  255. * 1 is a walkable span (triangle face up)
  256. */
  257. public int area;
  258. public LinkedVoxelSpan (uint bottom, uint top, int area) {
  259. this.bottom = bottom; this.top = top; this.area = area; this.next = -1;
  260. }
  261. public LinkedVoxelSpan (uint bottom, uint top, int area, int next) {
  262. this.bottom = bottom; this.top = top; this.area = area; this.next = next;
  263. }
  264. }
  265. /// <summary>
  266. /// Represents a mesh which will be rasterized.
  267. /// The vertices will be multiplied with the matrix when rasterizing it to voxels.
  268. /// The vertices and triangles array may be used in multiple instances, it is not changed when voxelizing.
  269. ///
  270. /// See: SceneMesh
  271. /// </summary>
  272. public class RasterizationMesh {
  273. /// <summary>
  274. /// Source of the mesh.
  275. /// May be null if the source was not a mesh filter
  276. /// </summary>
  277. public MeshFilter original;
  278. public int area;
  279. public Vector3[] vertices;
  280. public int[] triangles;
  281. /// <summary>
  282. /// Number of vertices in the <see cref="vertices"/> array.
  283. /// The vertices array is often pooled and then it sometimes makes sense to use a larger array than is actually necessary.
  284. /// </summary>
  285. public int numVertices;
  286. /// <summary>
  287. /// Number of triangles in the <see cref="triangles"/> array.
  288. /// The triangles array is often pooled and then it sometimes makes sense to use a larger array than is actually necessary.
  289. /// </summary>
  290. public int numTriangles;
  291. /// <summary>World bounds of the mesh. Assumed to already be multiplied with the matrix</summary>
  292. public Bounds bounds;
  293. public Matrix4x4 matrix;
  294. /// <summary>
  295. /// If true, the vertex and triangle arrays will be pooled after they have been used.
  296. /// Should be used only if the vertex and triangle arrays were originally taken from a pool.
  297. /// </summary>
  298. public bool pool;
  299. public RasterizationMesh () {
  300. }
  301. public RasterizationMesh (Vector3[] vertices, int[] triangles, Bounds bounds) {
  302. matrix = Matrix4x4.identity;
  303. this.vertices = vertices;
  304. this.numVertices = vertices.Length;
  305. this.triangles = triangles;
  306. this.numTriangles = triangles.Length;
  307. this.bounds = bounds;
  308. original = null;
  309. area = 0;
  310. }
  311. public RasterizationMesh (Vector3[] vertices, int[] triangles, Bounds bounds, Matrix4x4 matrix) {
  312. this.matrix = matrix;
  313. this.vertices = vertices;
  314. this.numVertices = vertices.Length;
  315. this.triangles = triangles;
  316. this.numTriangles = triangles.Length;
  317. this.bounds = bounds;
  318. original = null;
  319. area = 0;
  320. }
  321. /// <summary>Recalculate the bounds based on <see cref="vertices"/> and <see cref="matrix"/></summary>
  322. public void RecalculateBounds () {
  323. Bounds b = new Bounds(matrix.MultiplyPoint3x4(vertices[0]), Vector3.zero);
  324. for (int i = 1; i < numVertices; i++) {
  325. b.Encapsulate(matrix.MultiplyPoint3x4(vertices[i]));
  326. }
  327. // Assigned here to avoid changing bounds if vertices would happen to be null
  328. bounds = b;
  329. }
  330. /// <summary>Pool the <see cref="vertices"/> and <see cref="triangles"/> arrays if the <see cref="pool"/> field is true</summary>
  331. public void Pool () {
  332. if (pool) {
  333. Util.ArrayPool<int>.Release(ref triangles);
  334. Util.ArrayPool<Vector3>.Release(ref vertices);
  335. }
  336. }
  337. }
  338. /// <summary>VoxelContourSet used for recast graphs.</summary>
  339. public class VoxelContourSet {
  340. public List<VoxelContour> conts; // Pointer to all contours.
  341. public Bounds bounds; // Bounding box of the heightfield.
  342. }
  343. /// <summary>VoxelContour used for recast graphs.</summary>
  344. public struct VoxelContour {
  345. public int nverts;
  346. public int[] verts; // Vertex coordinates, each vertex contains 4 components.
  347. public int[] rverts; // Raw vertex coordinates, each vertex contains 4 components.
  348. public int reg; // Region ID of the contour.
  349. public int area; // Area ID of the contour.
  350. }
  351. /// <summary>VoxelMesh used for recast graphs.</summary>
  352. public struct VoxelMesh {
  353. /// <summary>Vertices of the mesh</summary>
  354. public Int3[] verts;
  355. /// <summary>
  356. /// Triangles of the mesh.
  357. /// Each element points to a vertex in the <see cref="verts"/> array
  358. /// </summary>
  359. public int[] tris;
  360. /// <summary>Area index for each triangle</summary>
  361. public int[] areas;
  362. }
  363. /// <summary>VoxelCell used for recast graphs.</summary>
  364. public struct VoxelCell {
  365. public VoxelSpan firstSpan;
  366. //public System.Object lockObject;
  367. public void AddSpan (uint bottom, uint top, int area, int voxelWalkableClimb) {
  368. VoxelSpan span = new VoxelSpan(bottom, top, area);
  369. if (firstSpan == null) {
  370. firstSpan = span;
  371. return;
  372. }
  373. VoxelSpan prev = null;
  374. VoxelSpan cSpan = firstSpan;
  375. while (cSpan != null) {
  376. if (cSpan.bottom > span.top) {
  377. break;
  378. } else if (cSpan.top < span.bottom) {
  379. prev = cSpan;
  380. cSpan = cSpan.next;
  381. } else {
  382. if (cSpan.bottom < bottom) {
  383. span.bottom = cSpan.bottom;
  384. }
  385. if (cSpan.top > top) {
  386. span.top = cSpan.top;
  387. }
  388. //1 is flagMergeDistance, when a walkable flag is favored before an unwalkable one
  389. if (System.Math.Abs((int)span.top - (int)cSpan.top) <= voxelWalkableClimb) {
  390. span.area = System.Math.Max(span.area, cSpan.area);
  391. }
  392. VoxelSpan next = cSpan.next;
  393. if (prev != null) {
  394. prev.next = next;
  395. } else {
  396. firstSpan = next;
  397. }
  398. cSpan = next;
  399. }
  400. }
  401. if (prev != null) {
  402. span.next = prev.next;
  403. prev.next = span;
  404. } else {
  405. span.next = firstSpan;
  406. firstSpan = span;
  407. }
  408. }
  409. }
  410. /// <summary>CompactVoxelCell used for recast graphs.</summary>
  411. public struct CompactVoxelCell {
  412. public uint index;
  413. public uint count;
  414. public CompactVoxelCell (uint i, uint c) {
  415. index = i;
  416. count = c;
  417. }
  418. }
  419. /// <summary>CompactVoxelSpan used for recast graphs.</summary>
  420. public struct CompactVoxelSpan {
  421. public ushort y;
  422. public uint con;
  423. public uint h;
  424. public int reg;
  425. public CompactVoxelSpan (ushort bottom, uint height) {
  426. con = 24;
  427. y = bottom;
  428. h = height;
  429. reg = 0;
  430. }
  431. public void SetConnection (int dir, uint value) {
  432. int shift = dir*6;
  433. con = (uint)((con & ~(0x3f << shift)) | ((value & 0x3f) << shift));
  434. }
  435. public int GetConnection (int dir) {
  436. return ((int)con >> dir*6) & 0x3f;
  437. }
  438. }
  439. /// <summary>VoxelSpan used for recast graphs.</summary>
  440. public class VoxelSpan {
  441. public uint bottom;
  442. public uint top;
  443. public VoxelSpan next;
  444. /*Area
  445. * 0 is an unwalkable span (triangle face down)
  446. * 1 is a walkable span (triangle face up)
  447. */
  448. public int area;
  449. public VoxelSpan (uint b, uint t, int area) {
  450. bottom = b;
  451. top = t;
  452. this.area = area;
  453. }
  454. }
  455. }