using UnityEngine;
using System.Collections.Generic;
#if NETFX_CORE && !UNITY_EDITOR
//using MarkerMetro.Unity.WinLegacy.IO;
#endif
namespace Pathfinding.Voxels {
/// Stores a voxel field.
public class VoxelArea {
public const uint MaxHeight = 65536;
public const int MaxHeightInt = 65536;
///
/// Constant for default LinkedVoxelSpan top and bottom values.
/// It is important with the U since ~0 != ~0U
/// This can be used to check if a LinkedVoxelSpan is valid and not just the default span
///
public const uint InvalidSpanValue = ~0U;
/// Initial estimate on the average number of spans (layers) in the voxel representation. Should be greater or equal to 1
public const float AvgSpanLayerCountEstimate = 8;
/// The width of the field along the x-axis. [Limit: >= 0] [Units: vx]
public readonly int width = 0;
/// The depth of the field along the z-axis. [Limit: >= 0] [Units: vx]
public readonly int depth = 0;
#if ASTAR_RECAST_CLASS_BASED_LINKED_LIST
public VoxelCell[] cells;
#endif
public CompactVoxelSpan[] compactSpans;
public CompactVoxelCell[] compactCells;
public int compactSpanCount;
public ushort[] tmpUShortArr;
public int[] areaTypes;
public ushort[] dist;
public ushort maxDistance;
public int maxRegions = 0;
public int[] DirectionX;
public int[] DirectionZ;
public Vector3[] VectorDirection;
public void Reset () {
#if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
ResetLinkedVoxelSpans();
#else
for (int i = 0; i < cells.Length; i++) cells[i].firstSpan = null;
#endif
for (int i = 0; i < compactCells.Length; i++) {
compactCells[i].count = 0;
compactCells[i].index = 0;
}
}
#if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
private void ResetLinkedVoxelSpans () {
int len = linkedSpans.Length;
linkedSpanCount = width*depth;
LinkedVoxelSpan df = new LinkedVoxelSpan(InvalidSpanValue, InvalidSpanValue, -1, -1);
for (int i = 0; i < len;) {
// 16x unrolling, actually improves performance
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
linkedSpans[i] = df; i++;
}
removedStackCount = 0;
}
#endif
public VoxelArea (int width, int depth) {
this.width = width;
this.depth = depth;
int wd = width*depth;
compactCells = new CompactVoxelCell[wd];
#if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
// & ~0xF ensures it is a multiple of 16. Required for unrolling
linkedSpans = new LinkedVoxelSpan[((int)(wd*AvgSpanLayerCountEstimate) + 15)& ~0xF];
ResetLinkedVoxelSpans();
#else
cells = new VoxelCell[wd];
#endif
DirectionX = new int[4] { -1, 0, 1, 0 };
DirectionZ = new int[4] { 0, width, 0, -width };
VectorDirection = new Vector3[4] { Vector3.left, Vector3.forward, Vector3.right, Vector3.back };
}
public int GetSpanCountAll () {
int count = 0;
int wd = width*depth;
for (int x = 0; x < wd; x++) {
#if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
for (int s = x; s != -1 && linkedSpans[s].bottom != InvalidSpanValue; s = linkedSpans[s].next) {
count++;
}
#else
for (VoxelSpan s = cells[x].firstSpan; s != null; s = s.next) {
count++;
}
#endif
}
return count;
}
public int GetSpanCount () {
int count = 0;
int wd = width*depth;
for (int x = 0; x < wd; x++) {
#if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
for (int s = x; s != -1 && linkedSpans[s].bottom != InvalidSpanValue; s = linkedSpans[s].next) {
if (linkedSpans[s].area != 0) {
count++;
}
}
#else
for (VoxelSpan s = cells[x].firstSpan; s != null; s = s.next) {
if (s.area != 0) {
count++;
}
}
#endif
}
return count;
}
#if !ASTAR_RECAST_CLASS_BASED_LINKED_LIST
private int linkedSpanCount;
public LinkedVoxelSpan[] linkedSpans;
private int[] removedStack = new int[128];
private int removedStackCount = 0;
void PushToSpanRemovedStack (int index) {
// Make sure we don't overflow the list
if (removedStackCount == removedStack.Length) {
// Create a new list to hold recycled values
int[] st2 = new int[removedStackCount*4];
System.Buffer.BlockCopy(removedStack, 0, st2, 0, removedStackCount*sizeof(int));
removedStack = st2;
}
removedStack[removedStackCount] = index;
removedStackCount++;
}
#endif
public void AddLinkedSpan (int index, uint bottom, uint top, int area, int voxelWalkableClimb) {
#if ASTAR_RECAST_CLASS_BASED_LINKED_LIST
cells[index].AddSpan(bottom, top, area, voxelWalkableClimb);
#else
var linkedSpans = this.linkedSpans;
// linkedSpans[index] is the span with the lowest y-coordinate at the position x,z such that index=x+z*width
// i.e linkedSpans is a 2D array laid out in a 1D array (for performance and simplicity)
// Check if there is a root span, otherwise we can just add a new (valid) span and exit
if (linkedSpans[index].bottom == InvalidSpanValue) {
linkedSpans[index] = new LinkedVoxelSpan(bottom, top, area);
return;
}
int prev = -1;
// Original index, the first span we visited
int oindex = index;
while (index != -1) {
var current = linkedSpans[index];
if (current.bottom > top) {
// If the current span's bottom higher up than the span we want to insert's top, then they do not intersect
// and we should just insert a new span here
break;
} else if (current.top < bottom) {
// The current span and the span we want to insert do not intersect
// so just skip to the next span (it might intersect)
prev = index;
index = current.next;
} else {
// Intersection! Merge the spans
// Find the new bottom and top for the merged span
bottom = System.Math.Min(current.bottom, bottom);
top = System.Math.Max(current.top, top);
// voxelWalkableClimb is flagMergeDistance, when a walkable flag is favored before an unwalkable one
// So if a walkable span intersects an unwalkable span, the walkable span can be up to voxelWalkableClimb
// below the unwalkable span and the merged span will still be walkable
if (System.Math.Abs((int)top - (int)current.top) <= voxelWalkableClimb) {
// linkedSpans[index] is the lowest span, but we might use that span's area anyway if it is walkable
area = System.Math.Max(area, current.area);
}
// Find the next span in the linked list
int next = current.next;
if (prev != -1) {
// There is a previous span
// Remove this span from the linked list
linkedSpans[prev].next = next;
// Add this span index to a list for recycling
PushToSpanRemovedStack(index);
// Move to the next span in the list
index = next;
} else if (next != -1) {
// This was the root span and there is a span left in the linked list
// Remove this span from the linked list by assigning the next span as the root span
linkedSpans[oindex] = linkedSpans[next];
// Recycle the old span index
PushToSpanRemovedStack(next);
// Move to the next span in the list
// NOP since we just removed the current span, the next span
// we want to visit will have the same index as we are on now (i.e oindex)
} else {
// This was the root span and there are no other spans in the linked list
// Just replace the root span with the merged span and exit
linkedSpans[oindex] = new LinkedVoxelSpan(bottom, top, area);
return;
}
}
}
// We now have a merged span that needs to be inserted
// and connected with the existing spans
// The new merged span will be inserted right after 'prev' (if it exists, otherwise before index)
// Make sure that we have enough room in our array
if (linkedSpanCount >= linkedSpans.Length) {
LinkedVoxelSpan[] tmp = linkedSpans;
int count = linkedSpanCount;
int popped = removedStackCount;
this.linkedSpans = linkedSpans = new LinkedVoxelSpan[linkedSpans.Length*2];
ResetLinkedVoxelSpans();
linkedSpanCount = count;
removedStackCount = popped;
for (int i = 0; i < linkedSpanCount; i++) {
linkedSpans[i] = tmp[i];
}
}
// Take a node from the recycling stack if possible
// Otherwise create a new node (well, just a new index really)
int nextIndex;
if (removedStackCount > 0) {
removedStackCount--;
nextIndex = removedStack[removedStackCount];
} else {
nextIndex = linkedSpanCount;
linkedSpanCount++;
}
if (prev != -1) {
//span.next = prev.next;
//prev.next = span;
linkedSpans[nextIndex] = new LinkedVoxelSpan(bottom, top, area, linkedSpans[prev].next);
linkedSpans[prev].next = nextIndex;
} else {
//span.next = firstSpan;
//firstSpan = span;
linkedSpans[nextIndex] = linkedSpans[oindex];
linkedSpans[oindex] = new LinkedVoxelSpan(bottom, top, area, nextIndex);
}
#endif
}
}
public struct LinkedVoxelSpan {
public uint bottom;
public uint top;
public int next;
/*Area
* 0 is an unwalkable span (triangle face down)
* 1 is a walkable span (triangle face up)
*/
public int area;
public LinkedVoxelSpan (uint bottom, uint top, int area) {
this.bottom = bottom; this.top = top; this.area = area; this.next = -1;
}
public LinkedVoxelSpan (uint bottom, uint top, int area, int next) {
this.bottom = bottom; this.top = top; this.area = area; this.next = next;
}
}
///
/// Represents a mesh which will be rasterized.
/// The vertices will be multiplied with the matrix when rasterizing it to voxels.
/// The vertices and triangles array may be used in multiple instances, it is not changed when voxelizing.
///
/// See: SceneMesh
///
public class RasterizationMesh {
///
/// Source of the mesh.
/// May be null if the source was not a mesh filter
///
public MeshFilter original;
public int area;
public Vector3[] vertices;
public int[] triangles;
///
/// Number of vertices in the array.
/// The vertices array is often pooled and then it sometimes makes sense to use a larger array than is actually necessary.
///
public int numVertices;
///
/// Number of triangles in the array.
/// The triangles array is often pooled and then it sometimes makes sense to use a larger array than is actually necessary.
///
public int numTriangles;
/// World bounds of the mesh. Assumed to already be multiplied with the matrix
public Bounds bounds;
public Matrix4x4 matrix;
///
/// If true, the vertex and triangle arrays will be pooled after they have been used.
/// Should be used only if the vertex and triangle arrays were originally taken from a pool.
///
public bool pool;
public RasterizationMesh () {
}
public RasterizationMesh (Vector3[] vertices, int[] triangles, Bounds bounds) {
matrix = Matrix4x4.identity;
this.vertices = vertices;
this.numVertices = vertices.Length;
this.triangles = triangles;
this.numTriangles = triangles.Length;
this.bounds = bounds;
original = null;
area = 0;
}
public RasterizationMesh (Vector3[] vertices, int[] triangles, Bounds bounds, Matrix4x4 matrix) {
this.matrix = matrix;
this.vertices = vertices;
this.numVertices = vertices.Length;
this.triangles = triangles;
this.numTriangles = triangles.Length;
this.bounds = bounds;
original = null;
area = 0;
}
/// Recalculate the bounds based on and
public void RecalculateBounds () {
Bounds b = new Bounds(matrix.MultiplyPoint3x4(vertices[0]), Vector3.zero);
for (int i = 1; i < numVertices; i++) {
b.Encapsulate(matrix.MultiplyPoint3x4(vertices[i]));
}
// Assigned here to avoid changing bounds if vertices would happen to be null
bounds = b;
}
/// Pool the and arrays if the field is true
public void Pool () {
if (pool) {
Util.ArrayPool.Release(ref triangles);
Util.ArrayPool.Release(ref vertices);
}
}
}
/// VoxelContourSet used for recast graphs.
public class VoxelContourSet {
public List conts; // Pointer to all contours.
public Bounds bounds; // Bounding box of the heightfield.
}
/// VoxelContour used for recast graphs.
public struct VoxelContour {
public int nverts;
public int[] verts; // Vertex coordinates, each vertex contains 4 components.
public int[] rverts; // Raw vertex coordinates, each vertex contains 4 components.
public int reg; // Region ID of the contour.
public int area; // Area ID of the contour.
}
/// VoxelMesh used for recast graphs.
public struct VoxelMesh {
/// Vertices of the mesh
public Int3[] verts;
///
/// Triangles of the mesh.
/// Each element points to a vertex in the array
///
public int[] tris;
/// Area index for each triangle
public int[] areas;
}
/// VoxelCell used for recast graphs.
public struct VoxelCell {
public VoxelSpan firstSpan;
//public System.Object lockObject;
public void AddSpan (uint bottom, uint top, int area, int voxelWalkableClimb) {
VoxelSpan span = new VoxelSpan(bottom, top, area);
if (firstSpan == null) {
firstSpan = span;
return;
}
VoxelSpan prev = null;
VoxelSpan cSpan = firstSpan;
while (cSpan != null) {
if (cSpan.bottom > span.top) {
break;
} else if (cSpan.top < span.bottom) {
prev = cSpan;
cSpan = cSpan.next;
} else {
if (cSpan.bottom < bottom) {
span.bottom = cSpan.bottom;
}
if (cSpan.top > top) {
span.top = cSpan.top;
}
//1 is flagMergeDistance, when a walkable flag is favored before an unwalkable one
if (System.Math.Abs((int)span.top - (int)cSpan.top) <= voxelWalkableClimb) {
span.area = System.Math.Max(span.area, cSpan.area);
}
VoxelSpan next = cSpan.next;
if (prev != null) {
prev.next = next;
} else {
firstSpan = next;
}
cSpan = next;
}
}
if (prev != null) {
span.next = prev.next;
prev.next = span;
} else {
span.next = firstSpan;
firstSpan = span;
}
}
}
/// CompactVoxelCell used for recast graphs.
public struct CompactVoxelCell {
public uint index;
public uint count;
public CompactVoxelCell (uint i, uint 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;
}
}
/// VoxelSpan used for recast graphs.
public class VoxelSpan {
public uint bottom;
public uint top;
public VoxelSpan next;
/*Area
* 0 is an unwalkable span (triangle face down)
* 1 is a walkable span (triangle face up)
*/
public int area;
public VoxelSpan (uint b, uint t, int area) {
bottom = b;
top = t;
this.area = area;
}
}
}