123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- using System.Collections.Generic;
- using Pathfinding.Util;
- using UnityEngine;
- using UnityEngine.Profiling;
- namespace Pathfinding {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public class HierarchicalGraph {
- const int Tiling = 16;
- const int MaxChildrenPerNode = Tiling * Tiling;
- const int MinChildrenPerNode = MaxChildrenPerNode/2;
- List<GraphNode>[] children = new List<GraphNode>[0];
- List<int>[] connections = new List<int>[0];
- int[] areas = new int[0];
- byte[] dirty = new byte[0];
- public int version { get; private set; }
- public System.Action onConnectedComponentsChanged;
- System.Action<GraphNode> connectionCallback;
- Queue<GraphNode> temporaryQueue = new Queue<GraphNode>();
- List<GraphNode> currentChildren = null;
- List<int> currentConnections = null;
- int currentHierarchicalNodeIndex;
- Stack<int> temporaryStack = new Stack<int>();
- int numDirtyNodes = 0;
- GraphNode[] dirtyNodes = new GraphNode[128];
- Stack<int> freeNodeIndices = new Stack<int>();
- int gizmoVersion = 0;
- public HierarchicalGraph () {
-
-
- connectionCallback = (GraphNode neighbour) => {
- var hIndex = neighbour.HierarchicalNodeIndex;
- if (hIndex == 0) {
- if (currentChildren.Count < MaxChildrenPerNode && neighbour.Walkable ) {
- neighbour.HierarchicalNodeIndex = currentHierarchicalNodeIndex;
- temporaryQueue.Enqueue(neighbour);
- currentChildren.Add(neighbour);
- }
- } else if (hIndex != currentHierarchicalNodeIndex && !currentConnections.Contains(hIndex)) {
-
-
-
- currentConnections.Add(hIndex);
- }
- };
- Grow();
- }
- void Grow () {
- var newChildren = new List<GraphNode>[System.Math.Max(64, children.Length*2)];
- var newConnections = new List<int>[newChildren.Length];
- var newAreas = new int[newChildren.Length];
- var newDirty = new byte[newChildren.Length];
- children.CopyTo(newChildren, 0);
- connections.CopyTo(newConnections, 0);
- areas.CopyTo(newAreas, 0);
- dirty.CopyTo(newDirty, 0);
- for (int i = children.Length; i < newChildren.Length; i++) {
- newChildren[i] = ListPool<GraphNode>.Claim(MaxChildrenPerNode);
- newConnections[i] = new List<int>();
- if (i > 0) freeNodeIndices.Push(i);
- }
- children = newChildren;
- connections = newConnections;
- areas = newAreas;
- dirty = newDirty;
- }
- int GetHierarchicalNodeIndex () {
- if (freeNodeIndices.Count == 0) Grow();
- return freeNodeIndices.Pop();
- }
- internal void OnCreatedNode (GraphNode node) {
- if (node.NodeIndex >= dirtyNodes.Length) {
- var newDirty = new GraphNode[System.Math.Max(node.NodeIndex + 1, dirtyNodes.Length*2)];
- dirtyNodes.CopyTo(newDirty, 0);
- dirtyNodes = newDirty;
- }
- AddDirtyNode(node);
- }
-
-
-
-
-
-
- public void AddDirtyNode (GraphNode node) {
- if (!node.IsHierarchicalNodeDirty) {
- node.IsHierarchicalNodeDirty = true;
-
-
-
-
- if (numDirtyNodes < dirtyNodes.Length) {
- dirtyNodes[numDirtyNodes] = node;
- numDirtyNodes++;
- } else {
- int maxIndex = 0;
- for (int i = numDirtyNodes - 1; i >= 0; i--) {
- if (dirtyNodes[i].Destroyed) {
- numDirtyNodes--;
- dirty[dirtyNodes[i].HierarchicalNodeIndex] = 1;
- dirtyNodes[i] = dirtyNodes[numDirtyNodes];
- dirtyNodes[numDirtyNodes] = null;
- } else {
- maxIndex = System.Math.Max(maxIndex, dirtyNodes[i].NodeIndex);
- }
- }
- if (numDirtyNodes >= dirtyNodes.Length) throw new System.Exception("Failed to compactify dirty nodes array. This should not happen. " + maxIndex + " " + numDirtyNodes + " " + dirtyNodes.Length);
- AddDirtyNode(node);
- }
- }
- }
- public int NumConnectedComponents { get; private set; }
-
- public uint GetConnectedComponent (int hierarchicalNodeIndex) {
- return (uint)areas[hierarchicalNodeIndex];
- }
- void RemoveHierarchicalNode (int hierarchicalNode, bool removeAdjacentSmallNodes) {
- freeNodeIndices.Push(hierarchicalNode);
- var conns = connections[hierarchicalNode];
- for (int i = 0; i < conns.Count; i++) {
- var adjacentHierarchicalNode = conns[i];
-
- if (dirty[adjacentHierarchicalNode] != 0) continue;
- if (removeAdjacentSmallNodes && children[adjacentHierarchicalNode].Count < MinChildrenPerNode) {
- dirty[adjacentHierarchicalNode] = 2;
- RemoveHierarchicalNode(adjacentHierarchicalNode, false);
- } else {
-
- connections[adjacentHierarchicalNode].Remove(hierarchicalNode);
- }
- }
- conns.Clear();
- var nodeChildren = children[hierarchicalNode];
- for (int i = 0; i < nodeChildren.Count; i++) {
- AddDirtyNode(nodeChildren[i]);
- }
- nodeChildren.ClearFast();
- }
-
- public void RecalculateIfNecessary () {
- if (numDirtyNodes > 0) {
- Profiler.BeginSample("Recalculate Connected Components");
- for (int i = 0; i < numDirtyNodes; i++) {
- dirty[dirtyNodes[i].HierarchicalNodeIndex] = 1;
- }
-
-
- for (int i = 1; i < dirty.Length; i++) {
- if (dirty[i] == 1) RemoveHierarchicalNode(i, true);
- }
- for (int i = 1; i < dirty.Length; i++) dirty[i] = 0;
- for (int i = 0; i < numDirtyNodes; i++) {
- dirtyNodes[i].HierarchicalNodeIndex = 0;
- }
- for (int i = 0; i < numDirtyNodes; i++) {
- var node = dirtyNodes[i];
-
- dirtyNodes[i] = null;
- node.IsHierarchicalNodeDirty = false;
- if (node.HierarchicalNodeIndex == 0 && node.Walkable && !node.Destroyed) {
- FindHierarchicalNodeChildren(GetHierarchicalNodeIndex(), node);
- }
- }
- numDirtyNodes = 0;
-
- FloodFill();
- Profiler.EndSample();
- gizmoVersion++;
- }
- }
-
-
-
-
-
-
- public void RecalculateAll () {
- AstarPath.active.data.GetNodes(node => AddDirtyNode(node));
- RecalculateIfNecessary();
- }
-
- void FloodFill () {
- for (int i = 0; i < areas.Length; i++) areas[i] = 0;
- Stack<int> stack = temporaryStack;
- int currentArea = 0;
- for (int i = 1; i < areas.Length; i++) {
-
- if (areas[i] != 0) continue;
- currentArea++;
- areas[i] = currentArea;
- stack.Push(i);
- while (stack.Count > 0) {
- int node = stack.Pop();
- var conns = connections[node];
- for (int j = conns.Count - 1; j >= 0; j--) {
- var otherNode = conns[j];
-
- if (areas[otherNode] != currentArea) {
- areas[otherNode] = currentArea;
- stack.Push(otherNode);
- }
- }
- }
- }
- NumConnectedComponents = System.Math.Max(1, currentArea + 1);
- version++;
- }
-
- void FindHierarchicalNodeChildren (int hierarchicalNode, GraphNode startNode) {
-
- currentChildren = children[hierarchicalNode];
- currentConnections = connections[hierarchicalNode];
- currentHierarchicalNodeIndex = hierarchicalNode;
- var que = temporaryQueue;
- que.Enqueue(startNode);
- startNode.HierarchicalNodeIndex = hierarchicalNode;
- currentChildren.Add(startNode);
- while (que.Count > 0) {
- que.Dequeue().GetConnections(connectionCallback);
- }
- for (int i = 0; i < currentConnections.Count; i++) {
- connections[currentConnections[i]].Add(hierarchicalNode);
- }
- que.Clear();
- }
- public void OnDrawGizmos (Pathfinding.Util.RetainedGizmos gizmos) {
- var hasher = new Pathfinding.Util.RetainedGizmos.Hasher(AstarPath.active);
- hasher.AddHash(gizmoVersion);
- if (!gizmos.Draw(hasher)) {
- var builder = ObjectPool<RetainedGizmos.Builder>.Claim();
- var centers = ArrayPool<UnityEngine.Vector3>.Claim(areas.Length);
- for (int i = 0; i < areas.Length; i++) {
- Int3 center = Int3.zero;
- var childs = children[i];
- if (childs.Count > 0) {
- for (int j = 0; j < childs.Count; j++) center += childs[j].position;
- center /= childs.Count;
- centers[i] = (UnityEngine.Vector3)center;
- }
- }
- for (int i = 0; i < areas.Length; i++) {
- if (children[i].Count > 0) {
- for (int j = 0; j < connections[i].Count; j++) {
- if (connections[i][j] > i) {
- builder.DrawLine(centers[i], centers[connections[i][j]], UnityEngine.Color.black);
- }
- }
- }
- }
- builder.Submit(gizmos, hasher);
- }
- }
- }
- }
|