summaryrefslogtreecommitdiff
path: root/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Nodes/LevelGridNode.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Nodes/LevelGridNode.cs')
-rw-r--r--Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Nodes/LevelGridNode.cs403
1 files changed, 403 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Nodes/LevelGridNode.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Nodes/LevelGridNode.cs
new file mode 100644
index 0000000..3a41a08
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Nodes/LevelGridNode.cs
@@ -0,0 +1,403 @@
+#if !ASTAR_NO_GRID_GRAPH
+#if !ASTAR_LEVELGRIDNODE_MORE_LAYERS
+#define ASTAR_LEVELGRIDNODE_FEW_LAYERS
+#endif
+using UnityEngine;
+using System.Collections.Generic;
+using Pathfinding.Serialization;
+
+namespace Pathfinding {
+ /// <summary>
+ /// Describes a single node for the LayerGridGraph.
+ /// Works almost the same as a grid node, except that it also stores to which layer the connections go to
+ /// </summary>
+ public class LevelGridNode : GridNodeBase {
+ public LevelGridNode() {
+ }
+
+ public LevelGridNode (AstarPath astar) {
+ astar.InitializeNode(this);
+ }
+
+ private static LayerGridGraph[] _gridGraphs = new LayerGridGraph[0];
+ public static LayerGridGraph GetGridGraph (uint graphIndex) { return _gridGraphs[(int)graphIndex]; }
+
+ public static void SetGridGraph (int graphIndex, LayerGridGraph graph) {
+ // LayeredGridGraphs also show up in the grid graph list
+ // This is required by e.g the XCoordinateInGrid properties
+ GridNode.SetGridGraph(graphIndex, graph);
+ if (_gridGraphs.Length <= graphIndex) {
+ var newGraphs = new LayerGridGraph[graphIndex+1];
+ for (int i = 0; i < _gridGraphs.Length; i++) newGraphs[i] = _gridGraphs[i];
+ _gridGraphs = newGraphs;
+ }
+
+ _gridGraphs[graphIndex] = graph;
+ }
+
+ public static void ClearGridGraph (int graphIndex, LayerGridGraph graph) {
+ if (graphIndex < _gridGraphs.Length && _gridGraphs[graphIndex] == graph) {
+ _gridGraphs[graphIndex] = null;
+ }
+ }
+
+#if ASTAR_LEVELGRIDNODE_FEW_LAYERS
+ public uint gridConnections;
+#else
+ public ulong gridConnections;
+#endif
+
+ protected static LayerGridGraph[] gridGraphs;
+
+ const int MaxNeighbours = 8;
+#if ASTAR_LEVELGRIDNODE_FEW_LAYERS
+ public const int ConnectionMask = 0xF;
+ public const int ConnectionStride = 4;
+ public const int AxisAlignedConnectionsMask = 0xFFFF;
+ public const uint AllConnectionsMask = 0xFFFFFFFF;
+#else
+ public const int ConnectionMask = 0xFF;
+ public const int ConnectionStride = 8;
+ public const ulong AxisAlignedConnectionsMask = 0xFFFFFFFF;
+ public const ulong AllConnectionsMask = 0xFFFFFFFFFFFFFFFF;
+#endif
+ public const int NoConnection = ConnectionMask;
+
+ internal const ulong DiagonalConnectionsMask = ((ulong)NoConnection << 4*ConnectionStride) | ((ulong)NoConnection << 5*ConnectionStride) | ((ulong)NoConnection << 6*ConnectionStride) | ((ulong)NoConnection << 7*ConnectionStride);
+
+ /// <summary>
+ /// Maximum number of layers the layered grid graph supports.
+ ///
+ /// This can be changed in the A* Inspector -> Optimizations tab by enabling or disabling the ASTAR_LEVELGRIDNODE_MORE_LAYERS option.
+ /// </summary>
+ public const int MaxLayerCount = ConnectionMask;
+
+ /// <summary>
+ /// Removes all grid connections from this node.
+ ///
+ /// Warning: Using this method can make the graph data inconsistent. It's recommended to use other ways to update the graph, instead.
+ /// </summary>
+ public override void ResetConnectionsInternal () {
+#if ASTAR_LEVELGRIDNODE_FEW_LAYERS
+ gridConnections = unchecked ((uint)-1);
+#else
+ gridConnections = unchecked ((ulong)-1);
+#endif
+ AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
+ }
+
+#if ASTAR_LEVELGRIDNODE_FEW_LAYERS
+ public override bool HasAnyGridConnections => gridConnections != unchecked ((uint)-1);
+#else
+ public override bool HasAnyGridConnections => gridConnections != unchecked ((ulong)-1);
+#endif
+
+ public override bool HasConnectionsToAllEightNeighbours {
+ get {
+ for (int i = 0; i < 8; i++) {
+ if (!HasConnectionInDirection(i)) return false;
+ }
+ return true;
+ }
+ }
+
+ public override bool HasConnectionsToAllAxisAlignedNeighbours {
+ get {
+ return (gridConnections & AxisAlignedConnectionsMask) == AxisAlignedConnectionsMask;
+ }
+ }
+
+ /// <summary>
+ /// Layer coordinate of the node in the grid.
+ /// If there are multiple nodes in the same (x,z) cell, then they will be stored in different layers.
+ /// Together with NodeInGridIndex, you can look up the node in the nodes array
+ /// <code>
+ /// int index = node.NodeInGridIndex + node.LayerCoordinateInGrid * graph.width * graph.depth;
+ /// Assert(node == graph.nodes[index]);
+ /// </code>
+ ///
+ /// See: XCoordInGrid
+ /// See: ZCoordInGrid
+ /// See: NodeInGridIndex
+ /// </summary>
+ public int LayerCoordinateInGrid { get { return nodeInGridIndex >> NodeInGridIndexLayerOffset; } set { nodeInGridIndex = (nodeInGridIndex & NodeInGridIndexMask) | (value << NodeInGridIndexLayerOffset); } }
+
+ public void SetPosition (Int3 position) {
+ this.position = position;
+ }
+
+ public override int GetGizmoHashCode () {
+ return base.GetGizmoHashCode() ^ (int)((805306457UL * gridConnections) ^ (402653189UL * (gridConnections >> 32)));
+ }
+
+ public override GridNodeBase GetNeighbourAlongDirection (int direction) {
+ int conn = GetConnectionValue(direction);
+
+ if (conn != NoConnection) {
+ LayerGridGraph graph = GetGridGraph(GraphIndex);
+ return graph.nodes[NodeInGridIndex+graph.neighbourOffsets[direction] + graph.lastScannedWidth*graph.lastScannedDepth*conn];
+ }
+ return null;
+ }
+
+ public override void ClearConnections (bool alsoReverse) {
+ if (alsoReverse) {
+ LayerGridGraph graph = GetGridGraph(GraphIndex);
+ int[] neighbourOffsets = graph.neighbourOffsets;
+ var nodes = graph.nodes;
+
+ for (int i = 0; i < MaxNeighbours; i++) {
+ int conn = GetConnectionValue(i);
+ if (conn != LevelGridNode.NoConnection) {
+ var other = nodes[NodeInGridIndex+neighbourOffsets[i] + graph.lastScannedWidth*graph.lastScannedDepth*conn] as LevelGridNode;
+ if (other != null) {
+ // Remove reverse connection
+ other.SetConnectionValue((i + 2) % 4, NoConnection);
+ }
+ }
+ }
+ }
+
+ ResetConnectionsInternal();
+
+#if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
+ base.ClearConnections(alsoReverse);
+#endif
+ }
+
+ public override void GetConnections<T>(GetConnectionsWithData<T> action, ref T data, int connectionFilter) {
+ if ((connectionFilter & (Connection.IncomingConnection | Connection.OutgoingConnection)) == 0) return;
+
+ LayerGridGraph graph = GetGridGraph(GraphIndex);
+
+ int[] neighbourOffsets = graph.neighbourOffsets;
+ var nodes = graph.nodes;
+ int index = NodeInGridIndex;
+
+ for (int i = 0; i < MaxNeighbours; i++) {
+ int conn = GetConnectionValue(i);
+ if (conn != LevelGridNode.NoConnection) {
+ var other = nodes[index+neighbourOffsets[i] + graph.lastScannedWidth*graph.lastScannedDepth*conn];
+ if (other != null) action(other, ref data);
+ }
+ }
+
+#if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
+ base.GetConnections(action, ref data, connectionFilter);
+#endif
+ }
+
+ /// <summary>
+ /// Is there a grid connection in that direction.
+ ///
+ /// Deprecated: Use <see cref="HasConnectionInDirection"/> instead
+ /// </summary>
+ [System.Obsolete("Use HasConnectionInDirection instead")]
+ public bool GetConnection (int i) {
+ return ((gridConnections >> i*ConnectionStride) & ConnectionMask) != NoConnection;
+ }
+
+ public override bool HasConnectionInDirection (int direction) {
+ return ((gridConnections >> direction*ConnectionStride) & ConnectionMask) != NoConnection;
+ }
+
+ /// <summary>
+ /// Set which layer a grid connection goes to.
+ ///
+ /// Warning: Using this method can make the graph data inconsistent. It's recommended to use other ways to update the graph, instead.
+ /// </summary>
+ /// <param name="dir">Direction for the connection.</param>
+ /// <param name="value">The layer of the connected node or #NoConnection if there should be no connection in that direction.</param>
+ public void SetConnectionValue (int dir, int value) {
+#if ASTAR_LEVELGRIDNODE_FEW_LAYERS
+ gridConnections = gridConnections & ~(((uint)NoConnection << dir*ConnectionStride)) | ((uint)value << dir*ConnectionStride);
+#else
+ gridConnections = gridConnections & ~(((ulong)NoConnection << dir*ConnectionStride)) | ((ulong)value << dir*ConnectionStride);
+#endif
+ AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
+ }
+
+#if ASTAR_LEVELGRIDNODE_FEW_LAYERS
+ public void SetAllConnectionInternal (ulong value) {
+ gridConnections = (uint)value;
+ }
+#else
+ public void SetAllConnectionInternal (ulong value) {
+ gridConnections = value;
+ }
+#endif
+
+
+ /// <summary>
+ /// Which layer a grid connection goes to.
+ /// Returns: The layer of the connected node or <see cref="NoConnection"/> if there is no connection in that direction.
+ /// </summary>
+ /// <param name="dir">Direction for the connection.</param>
+ public int GetConnectionValue (int dir) {
+ return (int)((gridConnections >> dir*ConnectionStride) & ConnectionMask);
+ }
+
+ public override void AddPartialConnection (GraphNode node, uint cost, bool isOutgoing, bool isIncoming) {
+ // In case the node was already added as an internal grid connection,
+ // we need to remove that connection before we insert it as a custom connection.
+ // Using a custom connection is necessary because it has a custom cost.
+ if (node is LevelGridNode gn && gn.GraphIndex == GraphIndex) {
+ RemoveGridConnection(gn);
+ }
+ base.AddPartialConnection(node, cost, isOutgoing, isIncoming);
+ }
+
+ public override void RemovePartialConnection (GraphNode node) {
+ base.RemovePartialConnection(node);
+ // If the node is a grid node on the same graph, it might be added as an internal connection and not a custom one.
+ if (node is LevelGridNode gn && gn.GraphIndex == GraphIndex) {
+ RemoveGridConnection(gn);
+ }
+ }
+
+ /// <summary>
+ /// Removes a connection from the internal grid connections, not the list of custom connections.
+ /// See: SetConnectionValue
+ /// </summary>
+ protected void RemoveGridConnection (LevelGridNode node) {
+ var nodeIndex = NodeInGridIndex;
+ var gg = GetGridGraph(GraphIndex);
+
+ for (int i = 0; i < 8; i++) {
+ if (nodeIndex + gg.neighbourOffsets[i] == node.NodeInGridIndex && GetNeighbourAlongDirection(i) == node) {
+ SetConnectionValue(i, NoConnection);
+ break;
+ }
+ }
+ }
+
+ public override bool GetPortal (GraphNode other, out Vector3 left, out Vector3 right) {
+ LayerGridGraph graph = GetGridGraph(GraphIndex);
+ int[] neighbourOffsets = graph.neighbourOffsets;
+ var nodes = graph.nodes;
+ int index = NodeInGridIndex;
+
+ for (int i = 0; i < MaxNeighbours; i++) {
+ int conn = GetConnectionValue(i);
+ if (conn != LevelGridNode.NoConnection) {
+ if (other == nodes[index+neighbourOffsets[i] + graph.lastScannedWidth*graph.lastScannedDepth*conn]) {
+ Vector3 middle = ((Vector3)(position + other.position))*0.5f;
+ Vector3 cross = Vector3.Cross(graph.collision.up, (Vector3)(other.position-position));
+ cross.Normalize();
+ cross *= graph.nodeSize*0.5f;
+ left = middle - cross;
+ right = middle + cross;
+ return true;
+ }
+ }
+ }
+
+ left = Vector3.zero;
+ right = Vector3.zero;
+ return false;
+ }
+
+ public override void Open (Path path, uint pathNodeIndex, uint gScore) {
+ LayerGridGraph graph = GetGridGraph(GraphIndex);
+
+ int[] neighbourOffsets = graph.neighbourOffsets;
+ uint[] neighbourCosts = graph.neighbourCosts;
+ var nodes = graph.nodes;
+ int index = NodeInGridIndex;
+
+ // Bitmask of the 8 connections out of this node.
+ // Each bit represents one connection.
+ // We only use this to be able to dynamically handle
+ // things like cutCorners and other diagonal connection filtering
+ // based on things like the tags or ITraversalProvider set for just this path.
+ // It starts off with all connections enabled but then in the following loop
+ // we will remove connections which are not traversable.
+ // When we get to the first diagonal connection we run a pass to
+ // filter out any diagonal connections which shouldn't be enabled.
+ // See the documentation for FilterDiagonalConnections for more info.
+ // The regular grid graph does a similar thing.
+ var conns = 0xFF;
+
+ for (int dir = 0; dir < MaxNeighbours; dir++) {
+ if (dir == 4 && (path.traversalProvider == null || path.traversalProvider.filterDiagonalGridConnections)) {
+ conns = GridNode.FilterDiagonalConnections(conns, graph.neighbours, graph.cutCorners);
+ }
+
+ int conn = GetConnectionValue(dir);
+ if (conn != LevelGridNode.NoConnection && ((conns >> dir) & 0x1) != 0) {
+ GraphNode other = nodes[index+neighbourOffsets[dir] + graph.lastScannedWidth*graph.lastScannedDepth*conn];
+
+ if (!path.CanTraverse(this, other)) {
+ conns &= ~(1 << dir);
+ continue;
+ }
+
+ path.OpenCandidateConnection(pathNodeIndex, other.NodeIndex, gScore, neighbourCosts[dir], 0, other.position);
+ } else {
+ conns &= ~(1 << dir);
+ }
+ }
+
+ base.Open(path, pathNodeIndex, gScore);
+ }
+
+ public override void SerializeNode (GraphSerializationContext ctx) {
+ base.SerializeNode(ctx);
+ ctx.SerializeInt3(position);
+ ctx.writer.Write(gridFlags);
+ // gridConnections are now always serialized as 64 bits for easier compatibility handling
+#if ASTAR_LEVELGRIDNODE_FEW_LAYERS
+ // Convert from 32 bits to 64-bits
+ ulong connectionsLong = 0;
+ for (int i = 0; i < 8; i++) connectionsLong |= (ulong)GetConnectionValue(i) << (i*8);
+#else
+ ulong connectionsLong = gridConnections;
+#endif
+ ctx.writer.Write(connectionsLong);
+ }
+
+ public override void DeserializeNode (GraphSerializationContext ctx) {
+ base.DeserializeNode(ctx);
+ position = ctx.DeserializeInt3();
+ gridFlags = ctx.reader.ReadUInt16();
+ if (ctx.meta.version < AstarSerializer.V4_3_12) {
+ // Note: assumes ASTAR_LEVELGRIDNODE_FEW_LAYERS was false when saving, which was the default
+ // This info not saved with the graph unfortunately and in 4.3.12 the default changed.
+ ulong conns;
+ if (ctx.meta.version < AstarSerializer.V3_9_0) {
+ // Set the upper 32 bits for compatibility
+ conns = ctx.reader.ReadUInt32() | (((ulong)NoConnection << 56) | ((ulong)NoConnection << 48) | ((ulong)NoConnection << 40) | ((ulong)NoConnection << 32));
+ } else {
+ conns = ctx.reader.ReadUInt64();
+ }
+ const int stride = 8;
+ const int mask = (1 << stride) - 1;
+ gridConnections = 0;
+ for (int i = 0; i < 8; i++) {
+ var y = (conns >> (i*stride)) & mask;
+ // 4.3.12 by default only supports 15 layers. So we may have to disable some connections when loading from earlier versions.
+ if ((y & ConnectionMask) != y) y = NoConnection;
+ SetConnectionValue(i, (int)y);
+ }
+ } else {
+ var gridConnectionsLong = ctx.reader.ReadUInt64();
+#if ASTAR_LEVELGRIDNODE_FEW_LAYERS
+ uint c = 0;
+ if (ctx.meta.version < AstarSerializer.V4_3_83) {
+ // The default during 4.3.12..4.3.83 was that ASTAR_LEVELGRIDNODE_FEW_LAYERS was enabled, but it was serialized just as 32-bits zero-extended to 64 bits
+ c = (uint)gridConnectionsLong;
+ } else {
+ // Convert from 64 bits to 32-bits
+ for (int i = 0; i < 8; i++) {
+ c |= ((uint)(gridConnectionsLong >> (i*8)) & LevelGridNode.ConnectionMask) << (LevelGridNode.ConnectionStride*i);
+ }
+ }
+ gridConnections = c;
+#else
+ gridConnections = gridConnectionsLong;
+#endif
+ }
+ }
+ }
+}
+#endif