160 lines
6.5 KiB
C#
160 lines
6.5 KiB
C#
using Unity.Collections;
|
|
using Unity.Jobs;
|
|
using Unity.Jobs.LowLevel.Unsafe;
|
|
using Unity.Mathematics;
|
|
using UnityEngine;
|
|
|
|
namespace Pathfinding.Graphs.Navmesh.Jobs {
|
|
/// <summary>
|
|
/// Connects adjacent tiles together.
|
|
///
|
|
/// This only creates connections between tiles. Connections internal to a tile should be handled by <see cref="JobCalculateTriangleConnections"/>.
|
|
///
|
|
/// Use the <see cref="ScheduleBatch"/> method to connect a bunch of tiles efficiently using maximum parallelism.
|
|
/// </summary>
|
|
public struct JobConnectTiles : IJob {
|
|
/// <summary>GCHandle referring to a NavmeshTile[] array of size tileRect.Width*tileRect.Height</summary>
|
|
public System.Runtime.InteropServices.GCHandle tiles;
|
|
public int coordinateSum;
|
|
public int direction;
|
|
public int zOffset;
|
|
public int zStride;
|
|
Vector2 tileWorldSize;
|
|
IntRect tileRect;
|
|
/// <summary>Maximum vertical distance between two tiles to create a connection between them</summary>
|
|
public float maxTileConnectionEdgeDistance;
|
|
|
|
static readonly Unity.Profiling.ProfilerMarker ConnectTilesMarker = new Unity.Profiling.ProfilerMarker("ConnectTiles");
|
|
|
|
/// <summary>
|
|
/// Schedule jobs to connect all the given tiles with each other while exploiting as much parallelism as possible.
|
|
/// tilesHandle should be a GCHandle referring to a NavmeshTile[] array of size tileRect.Width*tileRect.Height.
|
|
/// </summary>
|
|
public static JobHandle ScheduleBatch (System.Runtime.InteropServices.GCHandle tilesHandle, JobHandle dependency, IntRect tileRect, Vector2 tileWorldSize, float maxTileConnectionEdgeDistance) {
|
|
// First connect all tiles with an EVEN coordinate sum
|
|
// This would be the white squares on a chess board.
|
|
// Then connect all tiles with an ODD coordinate sum (which would be all black squares on a chess board).
|
|
// This will prevent the different threads that do all
|
|
// this in parallel from conflicting with each other.
|
|
// The directions are also done separately
|
|
// first they are connected along the X direction and then along the Z direction.
|
|
// Looping over 0 and then 1
|
|
|
|
int workers = Mathf.Max(1, JobsUtility.JobWorkerCount);
|
|
var handles = new NativeArray<JobHandle>(workers, Allocator.Temp);
|
|
for (int coordinateSum = 0; coordinateSum <= 1; coordinateSum++) {
|
|
for (int direction = 0; direction <= 1; direction++) {
|
|
for (int i = 0; i < workers; i++) {
|
|
handles[i] = new JobConnectTiles {
|
|
tiles = tilesHandle,
|
|
tileRect = tileRect,
|
|
tileWorldSize = tileWorldSize,
|
|
coordinateSum = coordinateSum,
|
|
direction = direction,
|
|
maxTileConnectionEdgeDistance = maxTileConnectionEdgeDistance,
|
|
zOffset = i,
|
|
zStride = workers,
|
|
}.Schedule(dependency);
|
|
}
|
|
dependency = JobHandle.CombineDependencies(handles);
|
|
}
|
|
}
|
|
|
|
return dependency;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Schedule jobs to connect all the given tiles inside innerRect with tiles that are outside it, while exploiting as much parallelism as possible.
|
|
/// tilesHandle should be a GCHandle referring to a NavmeshTile[] array of size tileRect.Width*tileRect.Height.
|
|
/// </summary>
|
|
public static JobHandle ScheduleRecalculateBorders (System.Runtime.InteropServices.GCHandle tilesHandle, JobHandle dependency, IntRect tileRect, IntRect innerRect, Vector2 tileWorldSize, float maxTileConnectionEdgeDistance) {
|
|
var w = innerRect.Width;
|
|
var h = innerRect.Height;
|
|
|
|
// Note: conservative estimate of number of handles. There may be fewer in reality.
|
|
var allDependencies = new NativeArray<JobHandle>(2*w + 2*math.max(0, h - 2), Allocator.Temp);
|
|
int count = 0;
|
|
for (int z = 0; z < h; z++) {
|
|
for (int x = 0; x < w; x++) {
|
|
// Check if the tile is on the border of the inner rect
|
|
if (!(x == 0 || z == 0 || x == w - 1 || z == h - 1)) continue;
|
|
|
|
var tileX = innerRect.xmin + x;
|
|
var tileZ = innerRect.ymin + z;
|
|
|
|
// For a corner tile, the jobs need to run sequentially
|
|
var dep = dependency;
|
|
for (int direction = 0; direction < 4; direction++) {
|
|
var nx = tileX + (direction == 0 ? 1 : direction == 1 ? -1 : 0);
|
|
var nz = tileZ + (direction == 2 ? 1 : direction == 3 ? -1 : 0);
|
|
if (innerRect.Contains(nx, nz) || !tileRect.Contains(nx, nz)) {
|
|
continue;
|
|
}
|
|
|
|
dep = new JobConnectTilesSingle {
|
|
tiles = tilesHandle,
|
|
tileIndex1 = tileX + tileZ * tileRect.Width,
|
|
tileIndex2 = nx + nz * tileRect.Width,
|
|
tileWorldSize = tileWorldSize,
|
|
maxTileConnectionEdgeDistance = maxTileConnectionEdgeDistance,
|
|
}.Schedule(dep);
|
|
}
|
|
|
|
allDependencies[count++] = dep;
|
|
}
|
|
}
|
|
return JobHandle.CombineDependencies(allDependencies);
|
|
}
|
|
|
|
public void Execute () {
|
|
var tiles = (NavmeshTile[])this.tiles.Target;
|
|
|
|
var tileRectDepth = tileRect.Height;
|
|
var tileRectWidth = tileRect.Width;
|
|
for (int z = zOffset; z < tileRectDepth; z += zStride) {
|
|
for (int x = 0; x < tileRectWidth; x++) {
|
|
if ((x + z) % 2 == coordinateSum) {
|
|
int tileIndex1 = x + z * tileRectWidth;
|
|
int tileIndex2;
|
|
if (direction == 0 && x < tileRectWidth - 1) {
|
|
tileIndex2 = x + 1 + z * tileRectWidth;
|
|
} else if (direction == 1 && z < tileRectDepth - 1) {
|
|
tileIndex2 = x + (z + 1) * tileRectWidth;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
ConnectTilesMarker.Begin();
|
|
NavmeshBase.ConnectTiles(tiles[tileIndex1], tiles[tileIndex2], tileWorldSize.x, tileWorldSize.y, maxTileConnectionEdgeDistance);
|
|
ConnectTilesMarker.End();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Connects two adjacent tiles together.
|
|
///
|
|
/// This only creates connections between tiles. Connections internal to a tile should be handled by <see cref="JobCalculateTriangleConnections"/>.
|
|
/// </summary>
|
|
struct JobConnectTilesSingle : IJob {
|
|
/// <summary>GCHandle referring to a NavmeshTile[] array of size tileRect.Width*tileRect.Height</summary>
|
|
public System.Runtime.InteropServices.GCHandle tiles;
|
|
/// <summary>Index of the first tile in the <see cref="tiles"/> array</summary>
|
|
public int tileIndex1;
|
|
/// <summary>Index of the second tile in the <see cref="tiles"/> array</summary>
|
|
public int tileIndex2;
|
|
/// <summary>Size of a tile in world units</summary>
|
|
public Vector2 tileWorldSize;
|
|
/// <summary>Maximum vertical distance between two tiles to create a connection between them</summary>
|
|
public float maxTileConnectionEdgeDistance;
|
|
|
|
public void Execute () {
|
|
var tiles = (NavmeshTile[])this.tiles.Target;
|
|
|
|
NavmeshBase.ConnectTiles(tiles[tileIndex1], tiles[tileIndex2], tileWorldSize.x, tileWorldSize.y, maxTileConnectionEdgeDistance);
|
|
}
|
|
}
|
|
}
|