Files

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);
}
}
}