using Pathfinding.Collections;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Profiling;
namespace Pathfinding.Graphs.Navmesh.Jobs {
///
/// Builds tiles optimized for pathfinding, from a list of .
///
/// This job takes the following steps:
/// - Transform all vertices using the matrix.
/// - Remove duplicate vertices
/// - If is enabled: ensure all triangles are laid out in the clockwise direction.
///
public struct JobCreateTiles : IJob {
/// An array of of length tileRect.Width*tileRect.Height, or an uninitialized array
[ReadOnly]
[NativeDisableContainerSafetyRestriction]
public NativeArray preCutTileMeshes;
/// An array of of length tileRect.Width*tileRect.Height
[ReadOnly]
public NativeArray tileMeshes;
///
/// An array of of length tileRect.Width*tileRect.Height.
/// This array will be filled with the created tiles.
///
public System.Runtime.InteropServices.GCHandle tiles;
/// Graph index of the graph that these nodes will be added to
public uint graphIndex;
///
/// Number of tiles in the graph.
///
/// This may be much bigger than the that we are actually processing.
/// For example if a graph update is performed, the will just cover the tiles that are recalculated,
/// while will contain all tiles in the graph.
///
public Vector2Int graphTileCount;
///
/// Rectangle of tiles that we are processing.
///
/// (xmax, ymax) must be smaller than graphTileCount.
/// If for examples is (10, 10) and is {2, 3, 5, 6} then we are processing tiles (2, 3) to (5, 6) inclusive.
///
public IntRect tileRect;
/// Initial penalty for all nodes in the tile
public uint initialPenalty;
///
/// If true, all triangles will be guaranteed to be laid out in clockwise order.
/// If false, their original order will be preserved.
///
public bool recalculateNormals;
/// Size of a tile in world units along the graph's X and Z axes
public Vector2 tileWorldSize;
/// Matrix to convert from graph space to world space
public Matrix4x4 graphToWorldSpace;
public void Execute () {
var tiles = (NavmeshTile[])this.tiles.Target;
Assert.AreEqual(tileMeshes.Length, tiles.Length);
Assert.AreEqual(tileMeshes.Length, tileRect.Area);
Assert.IsTrue(tileRect.xmax < graphTileCount.x);
Assert.IsTrue(tileRect.ymax < graphTileCount.y);
var tileRectWidth = tileRect.Width;
var tileRectDepth = tileRect.Height;
bool isUsingCuts = preCutTileMeshes.IsCreated;
if (isUsingCuts) {
Assert.AreEqual(preCutTileMeshes.Length, tiles.Length);
Assert.AreEqual(preCutTileMeshes.Length, tileRect.Area);
}
for (int z = 0; z < tileRectDepth; z++) {
for (int x = 0; x < tileRectWidth; x++) {
var tileIndex = z*tileRectWidth + x;
// If we are just updating a part of the graph we still want to assign the nodes the proper global tile index
var graphTileIndex = (z + tileRect.ymin)*graphTileCount.x + (x + tileRect.xmin);
var mesh = tileMeshes[tileIndex];
// Convert tile space to graph space and world space
var verticesInGraphSpace = mesh.verticesInTileSpace.Clone(Allocator.Persistent);
var verticesInWorldSpace = verticesInGraphSpace.Clone(Allocator.Persistent);
var tileSpaceToGraphSpaceOffset = (Int3) new Vector3(tileWorldSize.x * (x + tileRect.xmin), 0, tileWorldSize.y * (z + tileRect.ymin));
for (int i = 0; i < verticesInGraphSpace.Length; i++) {
var v = verticesInGraphSpace[i] + tileSpaceToGraphSpaceOffset;
verticesInGraphSpace[i] = v;
verticesInWorldSpace[i] = (Int3)graphToWorldSpace.MultiplyPoint3x4((Vector3)v);
}
// Create a new navmesh tile and assign its settings
var triangles = mesh.triangles.Clone(Allocator.Persistent);
var tile = new NavmeshTile {
x = x + tileRect.xmin,
z = z + tileRect.ymin,
w = 1,
d = 1,
tris = triangles,
vertsInGraphSpace = verticesInGraphSpace,
verts = verticesInWorldSpace,
bbTree = new BBTree(triangles, verticesInGraphSpace),
nodes = new TriangleMeshNode[triangles.Length/3],
// Leave empty for now, it will be filled in later
graph = null,
isCut = false,
};
if (isUsingCuts) {
// Copy pre-cut data to be able to reference it when re-cutting the tile.
// If no cuts are used, we don't save the pre-cut data, to reduce memory usage,
// as it is identical to the post-cut data.
// These arrays can be re-created from the other tile data, if needed.
var preCutMesh = preCutTileMeshes[tileIndex];
tile.preCutVertsInTileSpace = preCutMesh.verticesInTileSpace.Clone(Allocator.Persistent);
tile.preCutTris = preCutMesh.triangles.Clone(Allocator.Persistent);
tile.preCutTags = preCutMesh.tags.Clone(Allocator.Persistent);
tile.isCut = true;
}
Profiler.BeginSample("CreateNodes");
NavmeshBase.CreateNodes(tile, tile.tris, graphTileIndex, graphIndex, mesh.tags, false, null, initialPenalty, false);
Profiler.EndSample();
tiles[tileIndex] = tile;
}
}
}
}
}