添加插件
This commit is contained in:
@@ -0,0 +1,635 @@
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
/**
|
||||
* Helper class that voxelizes a mesh.
|
||||
*/
|
||||
[Serializable]
|
||||
public class MeshVoxelizer
|
||||
{
|
||||
[Flags]
|
||||
public enum Voxel
|
||||
{
|
||||
Empty = 0,
|
||||
Inside = 1 << 0,
|
||||
Boundary = 1 << 1,
|
||||
Outside = 1 << 2,
|
||||
}
|
||||
|
||||
public readonly static Vector3Int[] fullNeighborhood =
|
||||
{
|
||||
// face neighbors:
|
||||
new Vector3Int(-1,0,0),
|
||||
new Vector3Int(1,0,0),
|
||||
new Vector3Int(0,-1,0),
|
||||
new Vector3Int(0,1,0),
|
||||
new Vector3Int(0,0,-1),
|
||||
new Vector3Int(0,0,1),
|
||||
|
||||
// edge neighbors:
|
||||
new Vector3Int(-1,-1,0),
|
||||
new Vector3Int(-1,0,-1),
|
||||
new Vector3Int(-1,0,1),
|
||||
new Vector3Int(-1,1,0),
|
||||
new Vector3Int(0,-1,-1),
|
||||
new Vector3Int(0,-1,1),
|
||||
new Vector3Int(0,1,-1),
|
||||
new Vector3Int(0,1,1),
|
||||
new Vector3Int(1,-1,0),
|
||||
new Vector3Int(1,0,-1),
|
||||
new Vector3Int(1,0,1),
|
||||
new Vector3Int(1,1,0),
|
||||
|
||||
// vertex neighbors:
|
||||
new Vector3Int(-1,-1,-1),
|
||||
new Vector3Int(-1,-1,1),
|
||||
new Vector3Int(-1,1,-1),
|
||||
new Vector3Int(-1,1,1),
|
||||
new Vector3Int(1,-1,-1),
|
||||
new Vector3Int(1,-1,1),
|
||||
new Vector3Int(1,1,-1),
|
||||
new Vector3Int(1,1,1)
|
||||
};
|
||||
|
||||
public readonly static Vector3Int[] edgefaceNeighborhood =
|
||||
{
|
||||
new Vector3Int(-1,-1,0),
|
||||
new Vector3Int(-1,0,-1),
|
||||
new Vector3Int(-1,0,0),
|
||||
new Vector3Int(-1,0,1),
|
||||
new Vector3Int(-1,1,0),
|
||||
new Vector3Int(0,-1,-1),
|
||||
new Vector3Int(0,-1,0),
|
||||
new Vector3Int(0,-1,1),
|
||||
new Vector3Int(0,0,-1),
|
||||
new Vector3Int(0,0,1),
|
||||
new Vector3Int(0,1,-1),
|
||||
new Vector3Int(0,1,0),
|
||||
new Vector3Int(0,1,1),
|
||||
new Vector3Int(1,-1,0),
|
||||
new Vector3Int(1,0,-1),
|
||||
new Vector3Int(1,0,0),
|
||||
new Vector3Int(1,0,1),
|
||||
new Vector3Int(1,1,0)
|
||||
};
|
||||
|
||||
public readonly static Vector3Int[] faceNeighborhood =
|
||||
{
|
||||
new Vector3Int(-1,0,0),
|
||||
new Vector3Int(1,0,0),
|
||||
new Vector3Int(0,-1,0),
|
||||
new Vector3Int(0,1,0),
|
||||
new Vector3Int(0,0,-1),
|
||||
new Vector3Int(0,0,1)
|
||||
};
|
||||
|
||||
public readonly static Vector3Int[] edgeNeighborhood =
|
||||
{
|
||||
new Vector3Int(-1,-1,0),
|
||||
new Vector3Int(-1,0,-1),
|
||||
new Vector3Int(-1,0,1),
|
||||
new Vector3Int(-1,1,0),
|
||||
new Vector3Int(0,-1,-1),
|
||||
new Vector3Int(0,-1,1),
|
||||
new Vector3Int(0,1,-1),
|
||||
new Vector3Int(0,1,1),
|
||||
new Vector3Int(1,-1,0),
|
||||
new Vector3Int(1,0,-1),
|
||||
new Vector3Int(1,0,1),
|
||||
new Vector3Int(1,1,0)
|
||||
};
|
||||
|
||||
public readonly static Vector3Int[] vertexNeighborhood =
|
||||
{
|
||||
new Vector3Int(-1,-1,-1),
|
||||
new Vector3Int(-1,-1,1),
|
||||
new Vector3Int(-1,1,-1),
|
||||
new Vector3Int(-1,1,1),
|
||||
new Vector3Int(1,-1,-1),
|
||||
new Vector3Int(1,-1,1),
|
||||
new Vector3Int(1,1,-1),
|
||||
new Vector3Int(1,1,1)
|
||||
};
|
||||
|
||||
[NonSerialized] public Mesh input;
|
||||
|
||||
[HideInInspector][SerializeField] private Voxel[] voxels;
|
||||
public float voxelSize;
|
||||
public Vector3Int resolution;
|
||||
|
||||
private List<int>[] triangleIndices; // temporary structure to hold triangles overlapping each voxel.
|
||||
private Vector3Int origin;
|
||||
|
||||
public Vector3Int Origin
|
||||
{
|
||||
get { return origin; }
|
||||
}
|
||||
|
||||
public int voxelCount
|
||||
{
|
||||
get { return resolution.x * resolution.y * resolution.z; }
|
||||
}
|
||||
|
||||
public MeshVoxelizer(Mesh input, float voxelSize)
|
||||
{
|
||||
this.input = input;
|
||||
this.voxelSize = voxelSize;
|
||||
}
|
||||
|
||||
public Voxel this[int x, int y, int z]
|
||||
{
|
||||
get { return voxels[GetVoxelIndex(x, y, z)]; }
|
||||
set { voxels[GetVoxelIndex(x, y, z)] = value; }
|
||||
}
|
||||
|
||||
public float GetDistanceToNeighbor(int i)
|
||||
{
|
||||
if (i > 17) return ObiUtils.sqrt3 * voxelSize;
|
||||
if (i > 5) return ObiUtils.sqrt2 * voxelSize;
|
||||
return voxelSize;
|
||||
}
|
||||
|
||||
public int GetVoxelIndex(int x, int y, int z)
|
||||
{
|
||||
return x + resolution.x * (y + resolution.y * z);
|
||||
}
|
||||
|
||||
public Vector3 GetVoxelCenter(in Vector3Int coords)
|
||||
{
|
||||
return new Vector3(Origin.x + coords.x + 0.5f,
|
||||
Origin.y + coords.y + 0.5f,
|
||||
Origin.z + coords.z + 0.5f) * voxelSize;
|
||||
}
|
||||
|
||||
private Bounds GetTriangleBounds(in Vector3 v1, in Vector3 v2, in Vector3 v3)
|
||||
{
|
||||
Bounds b = new Bounds(v1, Vector3.zero);
|
||||
b.Encapsulate(v2);
|
||||
b.Encapsulate(v3);
|
||||
return b;
|
||||
}
|
||||
|
||||
public List<int> GetTrianglesOverlappingVoxel(int voxelIndex)
|
||||
{
|
||||
if (voxelIndex >= 0 && voxelIndex < triangleIndices.Length)
|
||||
return triangleIndices[voxelIndex];
|
||||
return null;
|
||||
}
|
||||
|
||||
public Vector3Int GetPointVoxel(in Vector3 point)
|
||||
{
|
||||
return new Vector3Int(Mathf.FloorToInt(point.x / voxelSize),
|
||||
Mathf.FloorToInt(point.y / voxelSize),
|
||||
Mathf.FloorToInt(point.z / voxelSize));
|
||||
}
|
||||
|
||||
public bool VoxelExists(in Vector3Int coords)
|
||||
{
|
||||
return VoxelExists(coords.x, coords.y, coords.z);
|
||||
}
|
||||
|
||||
public bool VoxelExists(int x, int y, int z)
|
||||
{
|
||||
return x >= 0 && y >= 0 && z >= 0 &&
|
||||
x < resolution.x &&
|
||||
y < resolution.y &&
|
||||
z < resolution.z;
|
||||
}
|
||||
|
||||
private void AppendOverlappingVoxels(in Bounds bounds, in Vector3 v1, in Vector3 v2, in Vector3 v3, int triangleIndex)
|
||||
{
|
||||
|
||||
Vector3Int min = GetPointVoxel(bounds.min);
|
||||
Vector3Int max = GetPointVoxel(bounds.max);
|
||||
|
||||
for (int x = min.x; x <= max.x; ++x)
|
||||
for (int y = min.y; y <= max.y; ++y)
|
||||
for (int z = min.z; z <= max.z; ++z)
|
||||
{
|
||||
Bounds voxel = new Bounds(new Vector3(x + 0.5f, y + 0.5f, z + 0.5f) * voxelSize, Vector3.one * voxelSize);
|
||||
|
||||
if (IsIntersecting(voxel, v1, v2, v3))
|
||||
{
|
||||
int index = GetVoxelIndex(x - origin.x, y - origin.y, z - origin.z);
|
||||
voxels[index] = Voxel.Boundary;
|
||||
|
||||
if (triangleIndices != null)
|
||||
triangleIndices[index].Add(triangleIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public IEnumerator Voxelize(Matrix4x4 transform, Vector3Int axisMask, bool generateTriangleIndices = false)
|
||||
{
|
||||
voxelSize = Mathf.Max(0.0001f, voxelSize);
|
||||
|
||||
var xfBounds = input.bounds.Transform(transform);
|
||||
|
||||
// Calculate min and max voxels, adding a 1-voxel margin.
|
||||
origin = GetPointVoxel(Vector3.Scale(xfBounds.min, axisMask)) - axisMask;
|
||||
Vector3Int max = GetPointVoxel(Vector3.Scale(xfBounds.max, axisMask)) + axisMask;
|
||||
|
||||
resolution = new Vector3Int(max.x - origin.x + 1, max.y - origin.y + 1, max.z - origin.z + 1);
|
||||
|
||||
// Allocate voxels array, and initialize them to "inside" the mesh:
|
||||
voxels = new Voxel[resolution.x * resolution.y * resolution.z];
|
||||
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
for (int y = 0; y < resolution.y; ++y)
|
||||
for (int z = 0; z < resolution.z; ++z)
|
||||
this[x, y, z] = Voxel.Inside;
|
||||
|
||||
// Allocate triangle lists:
|
||||
if (generateTriangleIndices)
|
||||
{
|
||||
triangleIndices = new List<int>[voxels.Length];
|
||||
for (int i = 0; i < triangleIndices.Length; ++i)
|
||||
triangleIndices[i] = new List<int>(4);
|
||||
}
|
||||
else
|
||||
triangleIndices = null;
|
||||
|
||||
// Get input triangles and vertices:
|
||||
int[] triIndices = input.triangles;
|
||||
Vector3[] vertices = input.vertices;
|
||||
|
||||
// Generate surface voxels:
|
||||
for (int i = 0; i < triIndices.Length; i += 3)
|
||||
{
|
||||
Vector3 v1 = Vector3.Scale(transform.MultiplyPoint3x4(vertices[triIndices[i]]), axisMask);
|
||||
Vector3 v2 = Vector3.Scale(transform.MultiplyPoint3x4(vertices[triIndices[i + 1]]), axisMask);
|
||||
Vector3 v3 = Vector3.Scale(transform.MultiplyPoint3x4(vertices[triIndices[i + 2]]), axisMask);
|
||||
|
||||
Bounds triBounds = GetTriangleBounds(v1, v2, v3);
|
||||
|
||||
AppendOverlappingVoxels(triBounds, v1, v2, v3, i/3);
|
||||
|
||||
if (i % 150 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("Voxelizing mesh...", i / (float)triIndices.Length);
|
||||
}
|
||||
|
||||
// Flood fill outside the mesh. This deals with multiple disjoint regions, and non-watertight models.
|
||||
var fillCoroutine = FloodFill();
|
||||
while (fillCoroutine.MoveNext())
|
||||
yield return fillCoroutine.Current;
|
||||
}
|
||||
|
||||
// Ensures boundary is only one voxel thick.
|
||||
public void BoundaryThinning()
|
||||
{
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
for (int y = 0; y < resolution.y; ++y)
|
||||
for (int z = 0; z < resolution.z; ++z)
|
||||
if (this[x, y, z] == Voxel.Boundary)
|
||||
this[x, y, z] = Voxel.Inside;
|
||||
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
for (int y = 0; y < resolution.y; ++y)
|
||||
for (int z = 0; z < resolution.z; ++z)
|
||||
{
|
||||
int sum = 0;
|
||||
for (int j = 0; j < faceNeighborhood.Length; ++j)
|
||||
{
|
||||
var index = faceNeighborhood[j];
|
||||
if (VoxelExists(index.x + x, index.y + y, index.z + z) && this[index.x + x, index.y + y, index.z + z] != Voxel.Outside)
|
||||
{
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
|
||||
if (sum % faceNeighborhood.Length != 0 && this[x, y, z] == Voxel.Inside)
|
||||
this[x, y, z] = Voxel.Boundary;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures boundary voxels are 2D.
|
||||
public void MakeBoundary2D()
|
||||
{
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
for (int y = 0; y < resolution.y; ++y)
|
||||
for (int z = 0; z < resolution.z; ++z)
|
||||
if (this[x, y, z] == Voxel.Boundary)
|
||||
this[x, y, z] = Voxel.Inside;
|
||||
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
for (int y = 0; y < resolution.y; ++y)
|
||||
for (int z = 0; z < resolution.z; ++z)
|
||||
{
|
||||
int sum = 0;
|
||||
for (int j = 0; j < faceNeighborhood.Length; ++j)
|
||||
{
|
||||
var index = faceNeighborhood[j];
|
||||
if (VoxelExists(index.x + x, index.y + y, index.z + z) && this[index.x + x, index.y + y, index.z + z] != Voxel.Outside)
|
||||
{
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
|
||||
if (sum <= 3 && this[x, y, z] == Voxel.Inside)
|
||||
this[x, y, z] = Voxel.Boundary;
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateMesh(ref Mesh mesh, int smoothingIterations)
|
||||
{
|
||||
if (mesh == null)
|
||||
mesh = new Mesh();
|
||||
|
||||
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
||||
mesh.Clear();
|
||||
List<Vector3> vertices = new List<Vector3>();
|
||||
List<Vector3> vertices2 = new List<Vector3>();
|
||||
List<int> tris = new List<int>();
|
||||
vertices.Clear();
|
||||
vertices2.Clear();
|
||||
tris.Clear();
|
||||
|
||||
int[] vtxIndex = new int[voxelCount];
|
||||
for (int i = 0; i < vtxIndex.Length; ++i)
|
||||
vtxIndex[i] = -1;
|
||||
|
||||
// create vertices:
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
for (int y = 0; y < resolution.y; ++y)
|
||||
for (int z = 0; z < resolution.z; ++z)
|
||||
if (this[x, y, z] == Voxel.Boundary)
|
||||
{
|
||||
vtxIndex[GetVoxelIndex(x, y, z)] = vertices.Count;
|
||||
var vtx = new Vector3(Origin.x + x + 0.5f, Origin.y + y + 0.5f, Origin.z + z + 0.5f) * voxelSize;
|
||||
vertices.Add(vtx);
|
||||
vertices2.Add(vtx);
|
||||
}
|
||||
|
||||
List<Vector3> inputVertices = vertices;
|
||||
List<Vector3> outputVertices = vertices2;
|
||||
for (int i = 0; i < smoothingIterations; ++i)
|
||||
{
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
for (int y = 0; y < resolution.y; ++y)
|
||||
for (int z = 0; z < resolution.z; ++z)
|
||||
if (this[x, y, z] == Voxel.Boundary)
|
||||
{
|
||||
Vector3 avg = Vector3.zero;
|
||||
|
||||
int count = 0;
|
||||
for (int j = 0; j < faceNeighborhood.Length; ++j)
|
||||
{
|
||||
var index = faceNeighborhood[j];
|
||||
if (VoxelExists(index.x + x, index.y + y, index.z + z) && this[index.x + x, index.y + y, index.z + z] == Voxel.Boundary)
|
||||
{
|
||||
avg += inputVertices[vtxIndex[GetVoxelIndex(index.x + x, index.y + y, index.z + z)]];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
outputVertices[vtxIndex[GetVoxelIndex(x, y, z)]] = avg / count;
|
||||
}
|
||||
|
||||
var aux = inputVertices;
|
||||
inputVertices = outputVertices;
|
||||
outputVertices = aux;
|
||||
}
|
||||
|
||||
// triangulate
|
||||
for (int x = 0; x < resolution.x; ++x)
|
||||
for (int y = 0; y < resolution.y; ++y)
|
||||
for (int z = 0; z < resolution.z; ++z)
|
||||
if (this[x, y, z] == Voxel.Boundary)
|
||||
{
|
||||
int x0y0z0 = GetVoxelIndex(x, y, z);
|
||||
|
||||
int x1y0z0 = VoxelExists(x + 1, y, z) ? GetVoxelIndex(x+1, y, z) : -1;
|
||||
int x1y1z0 = VoxelExists(x + 1, y + 1, z) ? GetVoxelIndex(x+1, y+1, z) : -1;
|
||||
int x0y1z0 = VoxelExists(x, y + 1, z) ? GetVoxelIndex(x, y+1, z) : -1;
|
||||
|
||||
int x0y0z1 = VoxelExists(x, y, z + 1) ? GetVoxelIndex(x, y , z + 1) : -1;
|
||||
int x0y1z1 = VoxelExists(x, y + 1, z + 1) ? GetVoxelIndex(x, y + 1, z + 1) : -1;
|
||||
int x1y0z1 = VoxelExists(x + 1, y, z + 1) ? GetVoxelIndex(x+1, y, z + 1) : -1;
|
||||
|
||||
int x1y1z1 = VoxelExists(x + 1, y + 1, z + 1) ? GetVoxelIndex(x + 1, y + 1, z + 1) : -1;
|
||||
|
||||
// XY plane
|
||||
if (x1y0z0 >= 0 && x1y1z0 >= 0 && x0y1z0 >= 0 &&
|
||||
voxels[x1y0z0] == Voxel.Boundary &&
|
||||
voxels[x1y1z0] == Voxel.Boundary &&
|
||||
voxels[x0y1z0] == Voxel.Boundary)
|
||||
{
|
||||
if (x0y1z1 < 0 || voxels[x0y1z1] == Voxel.Outside ||
|
||||
x0y0z1 < 0 || voxels[x0y0z1] == Voxel.Outside ||
|
||||
x1y0z1 < 0 || voxels[x1y0z1] == Voxel.Outside ||
|
||||
x1y1z1 < 0 || voxels[x1y1z1] == Voxel.Outside)
|
||||
{
|
||||
tris.Add(vtxIndex[x0y0z0]);
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x1y1z0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x0y0z0]);
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
tris.Add(vtxIndex[x1y1z0]);
|
||||
}
|
||||
}
|
||||
|
||||
// XZ plane
|
||||
if (x1y0z0 >= 0 && x1y0z1 >= 0 && x0y0z1 >= 0 &&
|
||||
voxels[x1y0z0] == Voxel.Boundary &&
|
||||
voxels[x1y0z1] == Voxel.Boundary &&
|
||||
voxels[x0y0z1] == Voxel.Boundary)
|
||||
{
|
||||
if (x0y1z0 < 0 || voxels[x0y1z0] == Voxel.Outside ||
|
||||
x0y1z1 < 0 || voxels[x0y1z1] == Voxel.Outside ||
|
||||
x1y1z0 < 0 || voxels[x1y1z0] == Voxel.Outside ||
|
||||
x1y1z1 < 0 || voxels[x1y1z1] == Voxel.Outside)
|
||||
{
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x0y0z0]);
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
tris.Add(vtxIndex[x1y0z1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
tris.Add(vtxIndex[x0y0z0]);
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
tris.Add(vtxIndex[x1y0z0]);
|
||||
tris.Add(vtxIndex[x1y0z1]);
|
||||
}
|
||||
}
|
||||
|
||||
// XY plane
|
||||
if (x0y0z1 >= 0 && x0y1z1 >= 0 && x0y1z0 >= 0 &&
|
||||
voxels[x0y0z1] == Voxel.Boundary &&
|
||||
voxels[x0y1z1] == Voxel.Boundary &&
|
||||
voxels[x0y1z0] == Voxel.Boundary)
|
||||
{
|
||||
if (x1y0z0 < 0 || voxels[x1y0z0] == Voxel.Outside ||
|
||||
x1y0z1 < 0 || voxels[x1y0z1] == Voxel.Outside ||
|
||||
x1y1z0 < 0 || voxels[x1y1z0] == Voxel.Outside ||
|
||||
x1y1z1 < 0 || voxels[x1y1z1] == Voxel.Outside)
|
||||
{
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
tris.Add(vtxIndex[x0y0z0]);
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
tris.Add(vtxIndex[x0y1z1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
tris.Add(vtxIndex[x0y0z0]);
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
|
||||
tris.Add(vtxIndex[x0y1z0]);
|
||||
tris.Add(vtxIndex[x0y0z1]);
|
||||
tris.Add(vtxIndex[x0y1z1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mesh.SetVertices(outputVertices);
|
||||
mesh.SetIndices(tris, MeshTopology.Triangles, 0);
|
||||
mesh.RecalculateNormals();
|
||||
}
|
||||
|
||||
private IEnumerator FloodFill()
|
||||
{
|
||||
Queue<Vector3Int> queue = new Queue<Vector3Int>();
|
||||
queue.Enqueue(new Vector3Int(0, 0, 0));
|
||||
|
||||
this[0, 0, 0] = Voxel.Outside;
|
||||
|
||||
int i = 0;
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
Vector3Int c = queue.Dequeue();
|
||||
Vector3Int v;
|
||||
|
||||
if (c.x < resolution.x - 1 && this[c.x + 1, c.y, c.z] == Voxel.Inside)
|
||||
{
|
||||
v = new Vector3Int(c.x + 1, c.y, c.z);
|
||||
this[v.x, v.y, v.z] = Voxel.Outside;
|
||||
queue.Enqueue(v);
|
||||
}
|
||||
if (c.x > 0 && this[c.x - 1, c.y, c.z] == Voxel.Inside)
|
||||
{
|
||||
v = new Vector3Int(c.x - 1, c.y, c.z);
|
||||
this[v.x, v.y, v.z] = Voxel.Outside;
|
||||
queue.Enqueue(v);
|
||||
}
|
||||
if (c.y < resolution.y - 1 && this[c.x, c.y + 1, c.z] == Voxel.Inside)
|
||||
{
|
||||
v = new Vector3Int(c.x, c.y + 1, c.z);
|
||||
this[v.x, v.y, v.z] = Voxel.Outside;
|
||||
queue.Enqueue(v);
|
||||
}
|
||||
if (c.y > 0 && this[c.x, c.y - 1, c.z] == Voxel.Inside )
|
||||
{
|
||||
v = new Vector3Int(c.x, c.y - 1, c.z);
|
||||
this[v.x, v.y, v.z] = Voxel.Outside;
|
||||
queue.Enqueue(v);
|
||||
}
|
||||
|
||||
if (c.z < resolution.z - 1 && this[c.x, c.y, c.z + 1] == Voxel.Inside)
|
||||
{
|
||||
v = new Vector3Int(c.x, c.y, c.z + 1);
|
||||
this[v.x, v.y, v.z] = Voxel.Outside;
|
||||
queue.Enqueue(v);
|
||||
}
|
||||
if (c.z > 0 && this[c.x, c.y, c.z - 1] == Voxel.Inside)
|
||||
{
|
||||
v = new Vector3Int(c.x, c.y, c.z - 1);
|
||||
this[v.x, v.y, v.z] = Voxel.Outside;
|
||||
queue.Enqueue(v);
|
||||
}
|
||||
|
||||
if (++i % 150 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("Filling mesh...", i / (float)voxels.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsIntersecting(in Bounds box, Vector3 v1, Vector3 v2, Vector3 v3)
|
||||
{
|
||||
v1 -= box.center;
|
||||
v2 -= box.center;
|
||||
v3 -= box.center;
|
||||
|
||||
var ab = v2 - v1;
|
||||
var bc = v3 - v2;
|
||||
var ca = v1 - v3;
|
||||
|
||||
//cross with (1, 0, 0)
|
||||
var a00 = new Vector3(0, -ab.z, ab.y);
|
||||
var a01 = new Vector3(0, -bc.z, bc.y);
|
||||
var a02 = new Vector3(0, -ca.z, ca.y);
|
||||
|
||||
//cross with (0, 1, 0)
|
||||
var a10 = new Vector3(ab.z, 0, -ab.x);
|
||||
var a11 = new Vector3(bc.z, 0, -bc.x);
|
||||
var a12 = new Vector3(ca.z, 0, -ca.x);
|
||||
|
||||
//cross with (0, 0, 1)
|
||||
var a20 = new Vector3(-ab.y, ab.x, 0);
|
||||
var a21 = new Vector3(-bc.y, bc.x, 0);
|
||||
var a22 = new Vector3(-ca.y, ca.x, 0);
|
||||
|
||||
if (
|
||||
!TriangleAabbSATTest(v1, v2, v3, box.extents, a00) ||
|
||||
!TriangleAabbSATTest(v1, v2, v3, box.extents, a01) ||
|
||||
!TriangleAabbSATTest(v1, v2, v3, box.extents, a02) ||
|
||||
!TriangleAabbSATTest(v1, v2, v3, box.extents, a10) ||
|
||||
!TriangleAabbSATTest(v1, v2, v3, box.extents, a11) ||
|
||||
!TriangleAabbSATTest(v1, v2, v3, box.extents, a12) ||
|
||||
!TriangleAabbSATTest(v1, v2, v3, box.extents, a20) ||
|
||||
!TriangleAabbSATTest(v1, v2, v3, box.extents, a21) ||
|
||||
!TriangleAabbSATTest(v1, v2, v3, box.extents, a22) ||
|
||||
!TriangleAabbSATTest(v1, v2, v3, box.extents, Vector3.right) ||
|
||||
!TriangleAabbSATTest(v1, v2, v3, box.extents, Vector3.up) ||
|
||||
!TriangleAabbSATTest(v1, v2, v3, box.extents, Vector3.forward) ||
|
||||
!TriangleAabbSATTest(v1, v2, v3, box.extents, Vector3.Cross(ab, bc))
|
||||
)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool TriangleAabbSATTest(in Vector3 v0, in Vector3 v1, in Vector3 v2, in Vector3 aabbExtents, in Vector3 axis)
|
||||
{
|
||||
float p0 = Vector3.Dot(v0, axis);
|
||||
float p1 = Vector3.Dot(v1, axis);
|
||||
float p2 = Vector3.Dot(v2, axis);
|
||||
|
||||
float r = aabbExtents.x * Mathf.Abs(axis.x) +
|
||||
aabbExtents.y * Mathf.Abs(axis.y) +
|
||||
aabbExtents.z * Mathf.Abs(axis.z);
|
||||
|
||||
float maxP = Mathf.Max(p0, Mathf.Max(p1, p2));
|
||||
float minP = Mathf.Min(p0, Mathf.Min(p1, p2));
|
||||
|
||||
return !(Mathf.Max(-maxP, minP) > r);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d719a609a88a842d8b4a10db447a627c
|
||||
timeCreated: 1521796043
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
// From https://visualstudiomagazine.com/articles/2012/11/01/priority-queues-with-c.aspx
|
||||
public class PriorityQueue<T> where T : IComparable<T>
|
||||
{
|
||||
public List<T> data;
|
||||
|
||||
public PriorityQueue()
|
||||
{
|
||||
this.data = new List<T>();
|
||||
}
|
||||
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
data.Add(item);
|
||||
int ci = data.Count - 1; // child index; start at end
|
||||
while (ci > 0)
|
||||
{
|
||||
int pi = (ci - 1) / 2; // parent index
|
||||
if (data[ci].CompareTo(data[pi]) >= 0) break; // child item is larger than (or equal) parent so we're done
|
||||
T tmp = data[ci]; data[ci] = data[pi]; data[pi] = tmp;
|
||||
ci = pi;
|
||||
}
|
||||
}
|
||||
|
||||
public T Dequeue()
|
||||
{
|
||||
// assumes pq is not empty; up to calling code
|
||||
int li = data.Count - 1; // last index (before removal)
|
||||
T frontItem = data[0]; // fetch the front
|
||||
data[0] = data[li];
|
||||
data.RemoveAt(li);
|
||||
|
||||
--li; // last index (after removal)
|
||||
int pi = 0; // parent index. start at front of pq
|
||||
while (true)
|
||||
{
|
||||
int ci = pi * 2 + 1; // left child index of parent
|
||||
if (ci > li) break; // no children so done
|
||||
int rc = ci + 1; // right child
|
||||
if (rc <= li && data[rc].CompareTo(data[ci]) < 0) // if there is a rc (ci + 1), and it is smaller than left child, use the rc instead
|
||||
ci = rc;
|
||||
if (data[pi].CompareTo(data[ci]) <= 0) break; // parent is smaller than (or equal to) smallest child so done
|
||||
T tmp = data[pi]; data[pi] = data[ci]; data[ci] = tmp; // swap parent and child
|
||||
pi = ci;
|
||||
}
|
||||
return frontItem;
|
||||
}
|
||||
|
||||
public T Peek()
|
||||
{
|
||||
T frontItem = data[0];
|
||||
return frontItem;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < data.Count; ++i)
|
||||
yield return data[i];
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
data.Clear();
|
||||
}
|
||||
|
||||
public int Count()
|
||||
{
|
||||
return data.Count;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string s = "";
|
||||
for (int i = 0; i < data.Count; ++i)
|
||||
s += data[i].ToString() + " ";
|
||||
s += "count = " + data.Count;
|
||||
return s;
|
||||
}
|
||||
|
||||
public bool IsConsistent()
|
||||
{
|
||||
// is the heap property true for all data?
|
||||
if (data.Count == 0) return true;
|
||||
int li = data.Count - 1; // last index
|
||||
for (int pi = 0; pi < data.Count; ++pi) // each parent index
|
||||
{
|
||||
int lci = 2 * pi + 1; // left child index
|
||||
int rci = 2 * pi + 2; // right child index
|
||||
|
||||
if (lci <= li && data[pi].CompareTo(data[lci]) > 0) return false; // if lc exists and it's greater than parent then bad.
|
||||
if (rci <= li && data[pi].CompareTo(data[rci]) > 0) return false; // check the right child too.
|
||||
}
|
||||
return true; // passed all checks
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df456c360bde341bca8e2bfd472e9bcb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,231 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
/**
|
||||
* Generates a sparse distance field from a voxel representation of a mesh.
|
||||
*/
|
||||
public class VoxelDistanceField
|
||||
{
|
||||
public Vector3[,,] distanceField; // for each coordinate, stores coordinates of closest surface voxel.
|
||||
|
||||
private MeshVoxelizer voxelizer;
|
||||
|
||||
public VoxelDistanceField(MeshVoxelizer voxelizer)
|
||||
{
|
||||
this.voxelizer = voxelizer;
|
||||
}
|
||||
|
||||
public Vector4 SampleUnfiltered(int x, int y, int z)
|
||||
{
|
||||
x = Mathf.Clamp(x, 0, voxelizer.resolution.x - 1);
|
||||
y = Mathf.Clamp(y, 0, voxelizer.resolution.y - 1);
|
||||
z = Mathf.Clamp(z, 0, voxelizer.resolution.z - 1);
|
||||
|
||||
var grad = distanceField[x, y, z];
|
||||
float dist = grad.magnitude;
|
||||
grad.Normalize();
|
||||
|
||||
return new Vector4(grad.x, grad.y, grad.z, -dist);
|
||||
}
|
||||
|
||||
public Vector4 SampleFiltered(float x, float y, float z, Vector3Int axisMask)
|
||||
{
|
||||
var pos = new Vector3(x, y, z);
|
||||
|
||||
// clamp position inside the distance field:
|
||||
var min = voxelizer.GetVoxelCenter(new Vector3Int(0, 0, 0));
|
||||
var max = voxelizer.GetVoxelCenter(new Vector3Int(voxelizer.resolution.x - 1, voxelizer.resolution.y - 1, voxelizer.resolution.z - 1));
|
||||
pos.x = Mathf.Clamp(pos.x, min.x, max.x);
|
||||
pos.y = Mathf.Clamp(pos.y, min.y, max.y);
|
||||
pos.z = Mathf.Clamp(pos.z, min.z, max.z);
|
||||
|
||||
var voxel = voxelizer.GetPointVoxel(pos - (Vector3)axisMask * voxelizer.voxelSize * 0.5f) - voxelizer.Origin;
|
||||
var voxelCenter = voxelizer.GetVoxelCenter(voxel);
|
||||
var norm = Vector3.Scale((pos - voxelCenter) / voxelizer.voxelSize, axisMask);
|
||||
|
||||
var xz00 = SampleUnfiltered(voxel.x, voxel.y, voxel.z);
|
||||
var xz01 = SampleUnfiltered(voxel.x, voxel.y, voxel.z + 1);
|
||||
var xz10 = SampleUnfiltered(voxel.x + 1, voxel.y, voxel.z);
|
||||
var xz11 = SampleUnfiltered(voxel.x + 1, voxel.y, voxel.z + 1);
|
||||
|
||||
var yz00 = SampleUnfiltered(voxel.x, voxel.y + 1, voxel.z);
|
||||
var yz01 = SampleUnfiltered(voxel.x, voxel.y + 1, voxel.z + 1);
|
||||
var yz10 = SampleUnfiltered(voxel.x + 1, voxel.y + 1, voxel.z);
|
||||
var yz11 = SampleUnfiltered(voxel.x + 1, voxel.y + 1, voxel.z + 1);
|
||||
|
||||
var X1 = Vector4.Lerp(xz00, xz10, norm.x);
|
||||
var X2 = Vector4.Lerp(xz01, xz11, norm.x);
|
||||
var X3 = Vector4.Lerp(yz00, yz10, norm.x);
|
||||
var X4 = Vector4.Lerp(yz01, yz11, norm.x);
|
||||
|
||||
var Y1 = Vector4.Lerp(X1, X2, norm.z);
|
||||
var Y2 = Vector4.Lerp(X3, X4, norm.z);
|
||||
|
||||
return Vector4.Lerp(Y1, Y2, norm.y);
|
||||
}
|
||||
|
||||
public void Smooth()
|
||||
{
|
||||
// create output buffer for ping-pong.
|
||||
Vector3[,,] smoothed = new Vector3[voxelizer.resolution.x,
|
||||
voxelizer.resolution.y,
|
||||
voxelizer.resolution.z];
|
||||
|
||||
for (int x = 0; x < distanceField.GetLength(0); ++x)
|
||||
for (int y = 0; y < distanceField.GetLength(1); ++y)
|
||||
for (int z = 0; z < distanceField.GetLength(2); ++z)
|
||||
{
|
||||
if (voxelizer[x, y, z] != MeshVoxelizer.Voxel.Outside)
|
||||
{
|
||||
var p = new Vector3Int(x, y, z);
|
||||
Vector3 df = distanceField[x, y, z];
|
||||
int count = 1;
|
||||
foreach (var o in MeshVoxelizer.faceNeighborhood)
|
||||
{
|
||||
// offset voxel to get neighbor:
|
||||
var n = p + o;
|
||||
if (voxelizer.VoxelExists(n.x, n.y, n.z) && voxelizer[n.x, n.y, n.z] != MeshVoxelizer.Voxel.Outside)
|
||||
{
|
||||
df += distanceField[n.x, n.y, n.z];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
df /= count;
|
||||
smoothed[x, y, z] = df;
|
||||
}
|
||||
}
|
||||
|
||||
distanceField = smoothed;
|
||||
}
|
||||
|
||||
private void CalculateGradientsAndDistances(Vector3Int[,,] buffer1)
|
||||
{
|
||||
distanceField = new Vector3[voxelizer.resolution.x,
|
||||
voxelizer.resolution.y,
|
||||
voxelizer.resolution.z];
|
||||
|
||||
for (int x = 0; x < buffer1.GetLength(0); ++x)
|
||||
for (int y = 0; y < buffer1.GetLength(1); ++y)
|
||||
for (int z = 0; z < buffer1.GetLength(2); ++z)
|
||||
{
|
||||
if (voxelizer[x, y, z] != MeshVoxelizer.Voxel.Outside)
|
||||
{
|
||||
distanceField[x, y, z] = voxelizer.GetVoxelCenter(buffer1[x, y, z]) -
|
||||
voxelizer.GetVoxelCenter(new Vector3Int(x, y, z));
|
||||
}
|
||||
else
|
||||
distanceField[x, y, z] = Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator JumpFlood()
|
||||
{
|
||||
// create two buffers for ping-ponging:
|
||||
Vector3Int[,,] buffer1 = new Vector3Int[voxelizer.resolution.x,
|
||||
voxelizer.resolution.y,
|
||||
voxelizer.resolution.z];
|
||||
|
||||
Vector3Int[,,] buffer2 = new Vector3Int[voxelizer.resolution.x,
|
||||
voxelizer.resolution.y,
|
||||
voxelizer.resolution.z];
|
||||
|
||||
// initialize distance field:
|
||||
for (int x = 0; x < buffer1.GetLength(0); ++x)
|
||||
for (int y = 0; y < buffer1.GetLength(1); ++y)
|
||||
for (int z = 0; z < buffer1.GetLength(2); ++z)
|
||||
{
|
||||
if (voxelizer[x, y, z] == MeshVoxelizer.Voxel.Outside)
|
||||
buffer1[x, y, z] = new Vector3Int(x, y, z);
|
||||
else
|
||||
buffer1[x, y, z] = new Vector3Int(-1, -1, -1);
|
||||
}
|
||||
|
||||
// calculate the maximum size of the buffer:
|
||||
int size = Mathf.Max(buffer1.GetLength(0),
|
||||
buffer1.GetLength(1),
|
||||
buffer1.GetLength(2));
|
||||
int step = (int)(size / 2.0f);
|
||||
|
||||
yield return new CoroutineJob.ProgressInfo("Generating voxel distance field...", 0);
|
||||
|
||||
float numPasses = (int)Mathf.Log(size, 2);
|
||||
int i = 0;
|
||||
|
||||
// jump flood passes:
|
||||
while (step >= 1)
|
||||
{
|
||||
JumpFloodPass(step, buffer1, buffer2);
|
||||
|
||||
// halve step:
|
||||
step /= 2;
|
||||
|
||||
// swap buffers:
|
||||
Vector3Int[,,] temp = buffer1;
|
||||
buffer1 = buffer2;
|
||||
buffer2 = temp;
|
||||
|
||||
yield return new CoroutineJob.ProgressInfo("Generating voxel distance field...", ++i / numPasses);
|
||||
}
|
||||
|
||||
CalculateGradientsAndDistances(buffer1);
|
||||
}
|
||||
|
||||
private void JumpFloodPass(int stride, Vector3Int[,,] input, Vector3Int[,,] output)
|
||||
{
|
||||
// for each voxel:
|
||||
for (int x = 0; x < input.GetLength(0); ++x)
|
||||
for (int y = 0; y < input.GetLength(1); ++y)
|
||||
for (int z = 0; z < input.GetLength(2); ++z)
|
||||
{
|
||||
// our position:
|
||||
Vector3Int p = new Vector3Int(x, y, z);
|
||||
|
||||
// our seed:
|
||||
Vector3Int s = input[x, y, z];
|
||||
|
||||
// copy the closest seed to the output, in case we do not update it this pass:
|
||||
output[x, y, z] = s;
|
||||
|
||||
// this voxel is a seed, skip it.
|
||||
if (s.x == x && s.y == y && s.z == z)
|
||||
continue;
|
||||
|
||||
// distance to our closest seed:
|
||||
float dist = float.MaxValue;
|
||||
if (s.x >= 0)
|
||||
dist = (s - p).sqrMagnitude;
|
||||
|
||||
// for each neighbor voxel:
|
||||
foreach (var o in MeshVoxelizer.fullNeighborhood)
|
||||
{
|
||||
// offset voxel to get neighbor:
|
||||
var n = p + o * stride;
|
||||
|
||||
if (voxelizer.VoxelExists(n.x, n.y, n.z))
|
||||
{
|
||||
// neighbors' closest seed.
|
||||
Vector3Int nc = input[n.x, n.y, n.z];
|
||||
|
||||
if (nc.x >= 0)
|
||||
{
|
||||
// distance to neighbor's closest seed:
|
||||
float newDist = (nc - p).sqrMagnitude;
|
||||
|
||||
// if the distance to the neighbor's closest seed is smaller than the distance to ours:
|
||||
if (newDist < dist)
|
||||
{
|
||||
output[x, y, z] = nc;
|
||||
dist = newDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c65cc27d2e30248c5afce6392f782c11
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class VoxelPathFinder
|
||||
{
|
||||
private MeshVoxelizer voxelizer = null;
|
||||
private bool[,,] closed;
|
||||
private PriorityQueue<TargetVoxel> open;
|
||||
|
||||
public struct TargetVoxel : IEquatable<TargetVoxel>, IComparable<TargetVoxel>
|
||||
{
|
||||
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<TargetVoxel>();
|
||||
}
|
||||
|
||||
private TargetVoxel AStar(in Vector3Int start, Func<TargetVoxel,bool> termination, Func<Vector3Int, float> 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e4d304562b1140b8a161652aaf4f122
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user