using Pathfinding.Jobs; using Unity.Collections; using Unity.Mathematics; using UnityEngine.Assertions; namespace Pathfinding.Graphs.Navmesh.Voxelization.Burst { /// Stores a compact voxel field. public struct CompactVoxelField : IArenaDisposable { public const int UnwalkableArea = 0; public const uint NotConnected = 0x3f; public readonly int voxelWalkableHeight; public readonly int width, depth; public NativeList spans; public NativeList cells; public NativeList areaTypes; /// Unmotivated variable, but let's clamp the layers at 65535 public const int MaxLayers = 65535; public CompactVoxelField (int width, int depth, int voxelWalkableHeight, Allocator allocator) { spans = new NativeList(0, allocator); cells = new NativeList(0, allocator); areaTypes = new NativeList(0, allocator); this.width = width; this.depth = depth; this.voxelWalkableHeight = voxelWalkableHeight; } void IArenaDisposable.DisposeWith (DisposeArena arena) { arena.Add(spans); arena.Add(cells); arena.Add(areaTypes); } public int GetNeighbourIndex (int index, int direction) { return index + VoxelUtilityBurst.DX[direction] + VoxelUtilityBurst.DZ[direction] * width; } public void BuildFromLinkedField (LinkedVoxelField field) { int idx = 0; Assert.AreEqual(this.width, field.width); Assert.AreEqual(this.depth, field.depth); int w = field.width; int d = field.depth; int wd = w*d; int spanCount = field.GetSpanCount(); spans.Resize(spanCount, NativeArrayOptions.UninitializedMemory); areaTypes.Resize(spanCount, NativeArrayOptions.UninitializedMemory); cells.Resize(wd, NativeArrayOptions.UninitializedMemory); #if ENABLE_UNITY_COLLECTIONS_CHECKS if (this.voxelWalkableHeight >= ushort.MaxValue) { throw new System.Exception("Too high walkable height to guarantee correctness. Increase voxel height or lower walkable height."); } #endif var linkedSpans = field.linkedSpans; for (int z = 0; z < wd; z += w) { for (int x = 0; x < w; x++) { int spanIndex = x+z; if (linkedSpans[spanIndex].bottom == LinkedVoxelField.InvalidSpanValue) { cells[x+z] = new CompactVoxelCell(0, 0); continue; } int index = idx; int count = 0; while (spanIndex != -1) { if (linkedSpans[spanIndex].area != UnwalkableArea) { int bottom = (int)linkedSpans[spanIndex].top; int next = linkedSpans[spanIndex].next; int top = next != -1 ? (int)linkedSpans[next].bottom : LinkedVoxelField.MaxHeightInt; // TODO: Why is top-bottom clamped to a ushort range? spans[idx] = new CompactVoxelSpan((ushort)math.min(bottom, ushort.MaxValue), (uint)math.min(top-bottom, ushort.MaxValue)); areaTypes[idx] = linkedSpans[spanIndex].area; idx++; count++; } spanIndex = linkedSpans[spanIndex].next; } cells[x+z] = new CompactVoxelCell(index, count); } } #if ENABLE_UNITY_COLLECTIONS_CHECKS if (idx != spanCount) throw new System.Exception("Found span count does not match expected value"); #endif } } /// CompactVoxelCell used for recast graphs. public struct CompactVoxelCell { public int index; public int count; public CompactVoxelCell (int i, int c) { index = i; count = c; } } /// CompactVoxelSpan used for recast graphs. public struct CompactVoxelSpan { public ushort y; public uint con; public uint h; public int reg; public CompactVoxelSpan (ushort bottom, uint height) { con = 24; y = bottom; h = height; reg = 0; } public void SetConnection (int dir, uint value) { int shift = dir*6; con = (uint)((con & ~(0x3f << shift)) | ((value & 0x3f) << shift)); } public int GetConnection (int dir) { return ((int)con >> dir*6) & 0x3f; } } }