using System; using UnityEngine; namespace Obi { public class VoxelPathFinder { private MeshVoxelizer voxelizer = null; private bool[,,] closed; private PriorityQueue open; public struct TargetVoxel : IEquatable, IComparable { public Vector3Int coordinates; public float distance; public float heuristic; //public TargetVoxel parent; public float cost { get { return distance + heuristic; } } public TargetVoxel(Vector3Int coordinates, float distance, float heuristic) { this.coordinates = coordinates; this.distance = distance; this.heuristic = heuristic; } public bool Equals(TargetVoxel other) { return this.coordinates.Equals(other.coordinates); } public int CompareTo(TargetVoxel other) { return this.cost.CompareTo(other.cost); } } public VoxelPathFinder(MeshVoxelizer voxelizer) { this.voxelizer = voxelizer; closed = new bool[voxelizer.resolution.x, voxelizer.resolution.y, voxelizer.resolution.z]; open = new PriorityQueue(); } private TargetVoxel AStar(in Vector3Int start, Func termination, Func heuristic) { Array.Clear(closed, 0, closed.Length); // A* algorithm: open.Clear(); open.Enqueue(new TargetVoxel(start, 0, 0)); while (open.Count() != 0) { var current = open.Dequeue(); if (termination(current)) return current; closed[current.coordinates.x, current.coordinates.y, current.coordinates.z] = true; for (int i = 0; i < MeshVoxelizer.fullNeighborhood.Length; ++i) { var successorCoords = current.coordinates + MeshVoxelizer.fullNeighborhood[i]; if (voxelizer.VoxelExists(successorCoords) && voxelizer[successorCoords.x, successorCoords.y, successorCoords.z] != MeshVoxelizer.Voxel.Outside && !closed[successorCoords.x, successorCoords.y, successorCoords.z]) { var successor = new TargetVoxel(successorCoords, current.distance + voxelizer.GetDistanceToNeighbor(i), heuristic(successorCoords)); //successor.parent = current; int index = -1; for (int j = 0; j < open.Count(); ++j) if (open.data[j].coordinates == successorCoords) { index = j; break; } if (index < 0) open.Enqueue(successor); else if (successor.distance < open.data[index].distance) open.data[index] = successor; } } } return new TargetVoxel(Vector3Int.zero, -1, -1); } public TargetVoxel FindClosestNonEmptyVoxel(in Vector3Int start) { if (voxelizer == null) return new TargetVoxel(Vector3Int.zero, -1, -1); if (!voxelizer.VoxelExists(start)) return new TargetVoxel(Vector3Int.zero, -1, -1); if (voxelizer[start.x, start.y, start.z] != MeshVoxelizer.Voxel.Outside) return new TargetVoxel(start, 0, 0); Array.Clear(closed, 0, closed.Length); return AStar(start, (TargetVoxel v) => { return voxelizer[v.coordinates.x, v.coordinates.y, v.coordinates.z] != MeshVoxelizer.Voxel.Outside; }, (Vector3Int c) => { return 0; }); } public TargetVoxel FindPath(in Vector3Int start, Vector3Int end) { if (voxelizer == null) return new TargetVoxel(Vector3Int.zero,-1, -1); if (!voxelizer.VoxelExists(start) || !voxelizer.VoxelExists(end)) return new TargetVoxel(Vector3Int.zero, -1, -1); if (voxelizer[start.x, start.y, start.z] == MeshVoxelizer.Voxel.Outside || voxelizer[end.x, end.y, end.z] == MeshVoxelizer.Voxel.Outside) return new TargetVoxel(Vector3Int.zero, -1, -1); return AStar(start, (TargetVoxel v) => { return v.coordinates == end; }, (Vector3Int c) => { return Vector3.Distance(c, end) * voxelizer.voxelSize; }); } } }