92 lines
3.3 KiB
C#

using UnityEngine;
using Unity.Collections;
using Unity.Mathematics;
using Pathfinding.Jobs;
using UnityEngine.Assertions;
namespace Pathfinding.Graphs.Grid.Jobs {
/// <summary>
/// Writes node data from unmanaged arrays into managed <see cref="GridNodeBase"/> objects.
///
/// This is done after burst jobs have been working on graph data, as they cannot access the managed objects directly.
///
/// Earlier, data will have been either calculated from scratch, or read from the managed objects using the <see cref="JobReadNodeData"/> job.
/// </summary>
public struct JobWriteNodeData : IJobParallelForBatched {
public System.Runtime.InteropServices.GCHandle nodesHandle;
public uint graphIndex;
/// <summary>(width, depth) of the array that the <see cref="nodesHandle"/> refers to</summary>
public int3 nodeArrayBounds;
public IntBounds dataBounds;
public IntBounds writeMask;
[ReadOnly]
public NativeArray<Vector3> nodePositions;
[ReadOnly]
public NativeArray<uint> nodePenalties;
[ReadOnly]
public NativeArray<int> nodeTags;
[ReadOnly]
public NativeArray<ulong> nodeConnections;
[ReadOnly]
public NativeArray<bool> nodeWalkableWithErosion;
[ReadOnly]
public NativeArray<bool> nodeWalkable;
public bool allowBoundsChecks => false;
public void Execute (int startIndex, int count) {
// This is a managed type, we need to trick Unity to allow this inside of a job
var nodes = (GridNodeBase[])nodesHandle.Target;
var relativeMask = writeMask.Offset(-dataBounds.min);
// Determinstically convert the indices to rows. It is much easier to process a number of whole rows.
var writeSize = writeMask.size;
var zstart = startIndex / (writeSize.x*writeSize.y);
var zend = (startIndex+count) / (writeSize.x*writeSize.y);
Assert.IsTrue(zstart >= 0 && zstart <= writeSize.z);
Assert.IsTrue(zend >= 0 && zend <= writeSize.z);
relativeMask.min.z = writeMask.min.z + zstart - dataBounds.min.z;
relativeMask.max.z = writeMask.min.z + zend - dataBounds.min.z;
var dataSize = dataBounds.size;
for (int y = relativeMask.min.y; y < relativeMask.max.y; y++) {
for (int z = relativeMask.min.z; z < relativeMask.max.z; z++) {
var rowOffset1 = (y*dataSize.z + z)*dataSize.x;
var rowOffset2 = (z + dataBounds.min.z)*nodeArrayBounds.x + dataBounds.min.x;
var rowOffset3 = (y + dataBounds.min.y)*nodeArrayBounds.z*nodeArrayBounds.x + rowOffset2;
for (int x = relativeMask.min.x; x < relativeMask.max.x; x++) {
int dataIndex = rowOffset1 + x;
int nodeIndex = rowOffset3 + x;
var node = nodes[nodeIndex];
if (node != null) {
node.GraphIndex = graphIndex;
node.NodeInGridIndex = rowOffset2 + x;
// TODO: Use UnsafeSpan
node.position = (Int3)nodePositions[dataIndex];
node.Penalty = nodePenalties[dataIndex];
node.Tag = (uint)nodeTags[dataIndex];
if (node is GridNode gridNode) {
gridNode.SetAllConnectionInternal((int)nodeConnections[dataIndex]);
} else if (node is LevelGridNode levelGridNode) {
levelGridNode.LayerCoordinateInGrid = y + dataBounds.min.y;
levelGridNode.SetAllConnectionInternal(nodeConnections[dataIndex]);
}
node.Walkable = nodeWalkableWithErosion[dataIndex];
node.WalkableErosion = nodeWalkable[dataIndex];
}
}
}
}
}
}
}