302 lines
7.9 KiB
C#
302 lines
7.9 KiB
C#
using System.Diagnostics;
|
|
using dtNodeIndex = System.UInt16;
|
|
using dtPolyRef = System.UInt64;
|
|
|
|
public partial class Detour
|
|
{
|
|
// From Thomas Wang, https://gist.github.com/badboy/6267743
|
|
public static uint dtHashRef(dtPolyRef a)
|
|
{
|
|
a = (~a) + (a << 18); // a = (a << 18) - a - 1;
|
|
a = a ^ (a >> 31);
|
|
a = a * 21; // a = (a + (a << 2)) + (a << 4);
|
|
a = a ^ (a >> 11);
|
|
a = a + (a << 6);
|
|
a = a ^ (a >> 22);
|
|
return (uint)a;
|
|
}
|
|
}
|
|
|
|
public partial class Detour
|
|
{
|
|
public enum dtNodeFlags
|
|
{
|
|
DT_NODE_OPEN = 0x01,
|
|
DT_NODE_CLOSED = 0x02,
|
|
DT_NODE_PARENT_DETACHED = 0x04, // parent of the node is not adjacent. Found using raycast.
|
|
};
|
|
|
|
public const dtNodeIndex DT_NULL_IDX = dtNodeIndex.MaxValue; //(dtNodeIndex)~0;
|
|
|
|
public class dtNode
|
|
{
|
|
public float[] pos = new float[3]; //< Position of the node.
|
|
public float cost; //< Cost from previous node to current node.
|
|
public float total; //< Cost up to the node.
|
|
public uint pidx;// : 24; //< Index to parent node.
|
|
public uint state;// : 2; ///< extra state information. A polyRef can have multiple nodes with different extra info. see DT_MAX_STATES_PER_NODE
|
|
public byte flags;// : 3; //< Node flags 0/open/closed.
|
|
public dtPolyRef id; //< Polygon ref the node corresponds to.
|
|
///
|
|
public static int getSizeOf()
|
|
{
|
|
//C# can't guess the sizeof of the float array, let's pretend
|
|
return sizeof(float) * (3 + 1 + 1)
|
|
+ sizeof(uint)
|
|
+ sizeof(byte)
|
|
+ sizeof(dtPolyRef);
|
|
}
|
|
public void dtcsClearFlag(dtNodeFlags flag)
|
|
{
|
|
unchecked
|
|
{
|
|
flags &= (byte)(~flag);
|
|
}
|
|
}
|
|
public void dtcsSetFlag(dtNodeFlags flag)
|
|
{
|
|
flags |= (byte)flag;
|
|
}
|
|
public bool dtcsTestFlag(dtNodeFlags flag)
|
|
{
|
|
return (flags & (byte)flag) != 0;
|
|
}
|
|
};
|
|
|
|
|
|
public class dtNodePool
|
|
{
|
|
private dtNode[] m_nodes;
|
|
private dtNodeIndex[] m_first;
|
|
private dtNodeIndex[] m_next;
|
|
private int m_maxNodes;
|
|
private int m_hashSize;
|
|
private int m_nodeCount;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
public dtNodePool(int maxNodes, int hashSize)
|
|
{
|
|
m_maxNodes = maxNodes;
|
|
m_hashSize = hashSize;
|
|
|
|
Debug.Assert(dtNextPow2((uint)m_hashSize) == (uint)m_hashSize);
|
|
Debug.Assert(m_maxNodes > 0);
|
|
|
|
m_nodes = new dtNode[m_maxNodes];
|
|
dtcsArrayItemsCreate(m_nodes);
|
|
m_next = new dtNodeIndex[m_maxNodes];
|
|
m_first = new dtNodeIndex[hashSize];
|
|
|
|
Debug.Assert(m_nodes != null);
|
|
Debug.Assert(m_next != null);
|
|
Debug.Assert(m_first != null);
|
|
|
|
for (int i = 0; i < hashSize; ++i)
|
|
{
|
|
m_first[i] = DT_NULL_IDX;
|
|
}
|
|
for (int i = 0; i < m_maxNodes; ++i)
|
|
{
|
|
m_next[i] = DT_NULL_IDX;
|
|
}
|
|
}
|
|
|
|
public void clear()
|
|
{
|
|
for (int i = 0; i < m_hashSize; ++i)
|
|
{
|
|
m_first[i] = DT_NULL_IDX;
|
|
}
|
|
m_nodeCount = 0;
|
|
}
|
|
|
|
public uint getNodeIdx(dtNode node)
|
|
{
|
|
if (node == null)
|
|
return 0;
|
|
|
|
return (uint)(System.Array.IndexOf(m_nodes, node)) + 1;
|
|
}
|
|
|
|
public dtNode getNodeAtIdx(uint idx)
|
|
{
|
|
if (idx == 0)
|
|
return null;
|
|
return m_nodes[idx - 1];
|
|
}
|
|
|
|
public int getMemUsed()
|
|
{
|
|
return
|
|
sizeof(int) * 3 +
|
|
dtNode.getSizeOf() * m_maxNodes +
|
|
sizeof(dtNodeIndex) * m_maxNodes +
|
|
sizeof(dtNodeIndex) * m_hashSize;
|
|
}
|
|
|
|
public int getMaxNodes()
|
|
{
|
|
return m_maxNodes;
|
|
}
|
|
|
|
public int getHashSize()
|
|
{
|
|
return m_hashSize;
|
|
}
|
|
public dtNodeIndex getFirst(int bucket)
|
|
{
|
|
return m_first[bucket];
|
|
}
|
|
public dtNodeIndex getNext(int i)
|
|
{
|
|
return m_next[i];
|
|
}
|
|
|
|
public dtNode findNode(dtPolyRef id)
|
|
{
|
|
uint bucket = (uint)(dtHashRef(id) & (m_hashSize - 1));
|
|
dtNodeIndex i = m_first[bucket];
|
|
while (i != DT_NULL_IDX)
|
|
{
|
|
if (m_nodes[i].id == id)
|
|
return m_nodes[i];
|
|
i = m_next[i];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public dtNode getNode(dtPolyRef id, byte state = 0)
|
|
{
|
|
uint bucket = (uint)(dtHashRef(id) & (m_hashSize - 1));
|
|
dtNodeIndex i = m_first[bucket];
|
|
dtNode node = null;
|
|
while (i != DT_NULL_IDX)
|
|
{
|
|
if (m_nodes[i].id == id && m_nodes[i].state == state)
|
|
return m_nodes[i];
|
|
i = m_next[i];
|
|
}
|
|
|
|
if (m_nodeCount >= m_maxNodes)
|
|
return null;
|
|
|
|
i = (dtNodeIndex)m_nodeCount;
|
|
m_nodeCount++;
|
|
|
|
// Init node
|
|
node = m_nodes[i];
|
|
node.pidx = 0;
|
|
node.cost = 0;
|
|
node.total = 0;
|
|
node.id = id;
|
|
node.state = state;
|
|
node.flags = 0;
|
|
|
|
m_next[i] = m_first[bucket];
|
|
m_first[bucket] = i;
|
|
|
|
return node;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
public class dtNodeQueue
|
|
{
|
|
private dtNode[] m_heap;
|
|
private int m_capacity;
|
|
private int m_size;
|
|
|
|
public dtNodeQueue(int n)
|
|
{
|
|
m_capacity = n;
|
|
Debug.Assert(m_capacity > 0);
|
|
|
|
m_heap = new dtNode[m_capacity + 1];//(dtNode**)dtAlloc(sizeof(dtNode*)*(m_capacity+1), DT_ALLOC_PERM);
|
|
Debug.Assert(m_heap != null);
|
|
}
|
|
|
|
public void clear()
|
|
{
|
|
m_size = 0;
|
|
}
|
|
|
|
public dtNode top()
|
|
{
|
|
return m_heap[0];
|
|
}
|
|
|
|
public dtNode pop()
|
|
{
|
|
dtNode result = m_heap[0];
|
|
m_size--;
|
|
trickleDown(0, m_heap[m_size]);
|
|
return result;
|
|
}
|
|
|
|
public void push(dtNode node)
|
|
{
|
|
m_size++;
|
|
bubbleUp(m_size - 1, node);
|
|
}
|
|
|
|
public void modify(dtNode node)
|
|
{
|
|
for (int i = 0; i < m_size; ++i)
|
|
{
|
|
if (m_heap[i] == node)
|
|
{
|
|
bubbleUp(i, node);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool empty()
|
|
{
|
|
return m_size == 0;
|
|
}
|
|
|
|
public int getMemUsed()
|
|
{
|
|
return sizeof(int) * 2 +
|
|
dtNode.getSizeOf() * (m_capacity + 1);
|
|
}
|
|
|
|
public int getCapacity()
|
|
{
|
|
return m_capacity;
|
|
}
|
|
|
|
|
|
public void bubbleUp(int i, dtNode node)
|
|
{
|
|
int parent = (i - 1) / 2;
|
|
// note: (index > 0) means there is a parent
|
|
while ((i > 0) && (m_heap[parent].total > node.total))
|
|
{
|
|
m_heap[i] = m_heap[parent];
|
|
i = parent;
|
|
parent = (i - 1) / 2;
|
|
}
|
|
m_heap[i] = node;
|
|
}
|
|
|
|
public void trickleDown(int i, dtNode node)
|
|
{
|
|
int child = (i * 2) + 1;
|
|
while (child < m_size)
|
|
{
|
|
if (((child + 1) < m_size) &&
|
|
(m_heap[child].total > m_heap[child + 1].total))
|
|
{
|
|
child++;
|
|
}
|
|
m_heap[i] = m_heap[child];
|
|
i = child;
|
|
child = (i * 2) + 1;
|
|
}
|
|
bubbleUp(i, node);
|
|
}
|
|
}
|
|
} |