summaryrefslogtreecommitdiff
path: root/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Grid/Jobs/JobErosion.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Grid/Jobs/JobErosion.cs')
-rw-r--r--Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Grid/Jobs/JobErosion.cs190
1 files changed, 190 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Grid/Jobs/JobErosion.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Grid/Jobs/JobErosion.cs
new file mode 100644
index 0000000..5b5ba35
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Grid/Jobs/JobErosion.cs
@@ -0,0 +1,190 @@
+using UnityEngine;
+using Unity.Burst;
+using Unity.Collections;
+using Unity.Jobs;
+using Unity.Mathematics;
+using Pathfinding.Jobs;
+using UnityEngine.Assertions;
+
+namespace Pathfinding.Graphs.Grid.Jobs {
+ /// <summary>
+ /// Calculates erosion.
+ /// Note that to ensure that connections are completely up to date after updating a node you
+ /// have to calculate the connections for both the changed node and its neighbours.
+ ///
+ /// In a layered grid graph, this will recalculate the connections for all nodes
+ /// in the (x,z) cell (it may have multiple layers of nodes).
+ ///
+ /// See: CalculateConnections(GridNodeBase)
+ /// </summary>
+ [BurstCompile]
+ public struct JobErosion<AdjacencyMapper> : IJob where AdjacencyMapper : GridAdjacencyMapper, new() {
+ public IntBounds bounds;
+ public IntBounds writeMask;
+ public NumNeighbours neighbours;
+ public int erosion;
+ public bool erosionUsesTags;
+ public int erosionStartTag;
+
+ [ReadOnly]
+ public NativeArray<ulong> nodeConnections;
+
+ [ReadOnly]
+ public NativeArray<bool> nodeWalkable;
+
+ [WriteOnly]
+ public NativeArray<bool> outNodeWalkable;
+
+ public NativeArray<int> nodeTags;
+ public int erosionTagsPrecedenceMask;
+
+ // Note: the first 3 connections are to nodes with a higher x or z coordinate
+ // The last 3 connections are to nodes with a lower x or z coordinate
+ // This is required for the grassfire transform to work properly
+ // This is a permutation of GridGraph.hexagonNeighbourIndices
+ static readonly int[] hexagonNeighbourIndices = { 1, 2, 5, 0, 3, 7 };
+
+ public void Execute () {
+ var slice = new Slice3D(bounds, bounds);
+ var size = slice.slice.size;
+ slice.AssertMatchesOuter(nodeConnections);
+ slice.AssertMatchesOuter(nodeWalkable);
+ slice.AssertMatchesOuter(outNodeWalkable);
+ slice.AssertMatchesOuter(nodeTags);
+ Assert.IsTrue(bounds.Contains(writeMask));
+
+ var(outerStrideX, outerStrideY, outerStrideZ) = slice.outerStrides;
+ var(innerStrideX, innerStrideY, innerStrideZ) = slice.innerStrides;
+ NativeArray<int> neighbourOffsets = new NativeArray<int>(8, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
+ for (int i = 0; i < 8; i++) neighbourOffsets[i] = GridGraph.neighbourZOffsets[i] * innerStrideZ + GridGraph.neighbourXOffsets[i] * innerStrideX;
+
+ var erosionDistances = new NativeArray<int>(slice.length, Allocator.Temp, NativeArrayOptions.ClearMemory);
+ var adjacencyMapper = new AdjacencyMapper();
+ var layers = adjacencyMapper.LayerCount(slice.slice);
+ var outerOffset = slice.outerStartIndex;
+ if (neighbours == NumNeighbours.Six) {
+ // Use the grassfire transform: https://en.wikipedia.org/wiki/Grassfire_transform extended to hexagonal graphs
+ for (int z = 1; z < size.z - 1; z++) {
+ for (int x = 1; x < size.x - 1; x++) {
+ for (int y = 0; y < layers; y++) {
+ // Note: This is significantly faster than using csum, because burst can optimize it better
+ int outerIndex = z * outerStrideZ + x * outerStrideX + y * outerStrideY + outerOffset;
+ var innerIndexXZ = z * innerStrideZ + x * innerStrideX;
+ int innerIndex = innerIndexXZ + y * innerStrideY;
+ int v = int.MaxValue;
+ for (int i = 3; i < 6; i++) {
+ int connection = hexagonNeighbourIndices[i];
+ if (!adjacencyMapper.HasConnection(outerIndex, connection, nodeConnections)) v = -1;
+ else v = math.min(v, erosionDistances[adjacencyMapper.GetNeighbourIndex(innerIndexXZ, innerIndex, connection, nodeConnections, neighbourOffsets, innerStrideY)]);
+ }
+
+ erosionDistances[innerIndex] = v + 1;
+ }
+ }
+ }
+
+ for (int z = size.z - 2; z > 0; z--) {
+ for (int x = size.x - 2; x > 0; x--) {
+ for (int y = 0; y < layers; y++) {
+ int outerIndex = z * outerStrideZ + x * outerStrideX + y * outerStrideY + outerOffset;
+ var innerIndexXZ = z * innerStrideZ + x * innerStrideX;
+ int innerIndex = innerIndexXZ + y * innerStrideY;
+ int v = int.MaxValue;
+ for (int i = 3; i < 6; i++) {
+ int connection = hexagonNeighbourIndices[i];
+ if (!adjacencyMapper.HasConnection(outerIndex, connection, nodeConnections)) v = -1;
+ else v = math.min(v, erosionDistances[adjacencyMapper.GetNeighbourIndex(innerIndexXZ, innerIndex, connection, nodeConnections, neighbourOffsets, innerStrideY)]);
+ }
+
+ erosionDistances[innerIndex] = math.min(erosionDistances[innerIndex], v + 1);
+ }
+ }
+ }
+ } else {
+ /* Index offset to get neighbour nodes. Added to a node's index to get a neighbour node index.
+ *
+ * \code
+ * Z
+ * |
+ * |
+ *
+ * 6 2 5
+ * \ | /
+ * -- 3 - X - 1 ----- X
+ * / | \
+ * 7 0 4
+ *
+ * |
+ * |
+ * \endcode
+ */
+ const int DirectionDown = 0;
+ const int DirectionRight = 1;
+ const int DirectionUp = 2;
+ const int DirectionLeft = 3;
+
+ // Use the grassfire transform: https://en.wikipedia.org/wiki/Grassfire_transform
+ for (int z = 1; z < size.z - 1; z++) {
+ for (int x = 1; x < size.x - 1; x++) {
+ for (int y = 0; y < layers; y++) {
+ int outerIndex = z * outerStrideZ + x * outerStrideX + y * outerStrideY + outerOffset;
+ var innerIndexXZ = z * innerStrideZ + x * innerStrideX;
+ int innerIndex = innerIndexXZ + y * innerStrideY;
+ var v1 = -1;
+ if (adjacencyMapper.HasConnection(outerIndex, DirectionDown, nodeConnections)) v1 = erosionDistances[adjacencyMapper.GetNeighbourIndex(innerIndexXZ, innerIndex, DirectionDown, nodeConnections, neighbourOffsets, innerStrideY)];
+ var v2 = -1;
+ if (adjacencyMapper.HasConnection(outerIndex, DirectionLeft, nodeConnections)) v2 = erosionDistances[adjacencyMapper.GetNeighbourIndex(innerIndexXZ, innerIndex, DirectionLeft, nodeConnections, neighbourOffsets, innerStrideY)];
+
+ erosionDistances[innerIndex] = math.min(v1, v2) + 1;
+ }
+ }
+ }
+
+ for (int z = size.z - 2; z > 0; z--) {
+ for (int x = size.x - 2; x > 0; x--) {
+ for (int y = 0; y < layers; y++) {
+ int outerIndex = z * outerStrideZ + x * outerStrideX + y * outerStrideY + outerOffset;
+ var innerIndexXZ = z * innerStrideZ + x * innerStrideX;
+ int innerIndex = innerIndexXZ + y * innerStrideY;
+ var v1 = -1;
+ if (adjacencyMapper.HasConnection(outerIndex, DirectionUp, nodeConnections)) v1 = erosionDistances[adjacencyMapper.GetNeighbourIndex(innerIndexXZ, innerIndex, DirectionUp, nodeConnections, neighbourOffsets, innerStrideY)];
+ var v2 = -1;
+ if (adjacencyMapper.HasConnection(outerIndex, DirectionRight, nodeConnections)) v2 = erosionDistances[adjacencyMapper.GetNeighbourIndex(innerIndexXZ, innerIndex, DirectionRight, nodeConnections, neighbourOffsets, innerStrideY)];
+
+ erosionDistances[innerIndex] = math.min(erosionDistances[outerIndex], math.min(v1, v2) + 1);
+ }
+ }
+ }
+ }
+
+ var relativeWriteMask = writeMask.Offset(-bounds.min);
+
+ // Erosion tags are allowed to overwrite the ones the user specifies, as well as the ones that are already reserved for erosion.
+ for (int i = erosionStartTag; i < erosionStartTag + erosion; i++) erosionTagsPrecedenceMask |= 1 << i;
+
+ for (int y = relativeWriteMask.min.y; y < relativeWriteMask.max.y; y++) {
+ for (int z = relativeWriteMask.min.z; z < relativeWriteMask.max.z; z++) {
+ for (int x = relativeWriteMask.min.x; x < relativeWriteMask.max.x; x++) {
+ int outerIndex = x * outerStrideX + y * outerStrideY + z * outerStrideZ + outerOffset;
+ int innerIndex = x * innerStrideX + y * innerStrideY + z * innerStrideZ;
+ if (erosionUsesTags) {
+ var prevTag = nodeTags[outerIndex];
+ outNodeWalkable[outerIndex] = nodeWalkable[outerIndex];
+
+ if (erosionDistances[innerIndex] < erosion) {
+ if (((erosionTagsPrecedenceMask >> prevTag) & 0x1) != 0) {
+ nodeTags[outerIndex] = nodeWalkable[outerIndex] ? math.min(GraphNode.MaxTagIndex, erosionDistances[innerIndex] + erosionStartTag) : 0;
+ }
+ } else if (prevTag >= erosionStartTag && prevTag < erosionStartTag + erosion) {
+ // If the node already had a tag that was reserved for erosion, but it shouldn't have that tag, then we remove it.
+ nodeTags[outerIndex] = 0;
+ }
+ } else {
+ outNodeWalkable[outerIndex] = nodeWalkable[outerIndex] & (erosionDistances[innerIndex] >= erosion);
+ }
+ }
+ }
+ }
+ }
+ }
+}