Files
ZeroVR/ZeroPacientVR/Assets/CodeRespawn/DungeonArchitect/ThirdParty/SharpNav/PolyMesh.cs
T
2022-04-18 19:17:20 +03:00

1520 lines
39 KiB
C#

// Copyright (c) 2013-2015 Robert Rouhani <robert.rouhani@gmail.com> and other contributors (see CONTRIBUTORS file).
// Licensed under the MIT License - https://raw.github.com/Robmaister/SharpNav/master/LICENSE
using System;
using System.Collections.Generic;
using SharpNav.Geometry;
#if MONOGAME
using Vector3 = Microsoft.Xna.Framework.Vector3;
#elif OPENTK
using Vector3 = OpenTK.Vector3;
#elif SHARPDX
using Vector3 = SharpDX.Vector3;
#endif
namespace SharpNav
{
/// <summary>
/// The class of Poly mesh.
/// </summary>
public class PolyMesh
{
public const int NullId = -1;
private const int DiagonalFlag = unchecked((int)0x80000000);
private const int NeighborEdgeFlag = unchecked((int)0x80000000);
private PolyVertex[] vertices;
private Polygon[] polygons;
private int numVertsPerPoly;
//copied data from CompactHeightfield
private BBox3 bounds;
private float cellSize;
private float cellHeight;
private int borderSize;
//HACK borderSize is 0 here. Fix with borderSize.
/// <summary>
/// Initializes a new instance of the <see cref="PolyMesh"/> class.
/// </summary>
/// <param name="contSet">The <see cref="ContourSet"/> to generate polygons from.</param>
/// <param name="settings">The settings to build with.</param>
public PolyMesh(ContourSet contSet, NavMeshGenerationSettings settings)
: this(contSet, settings.CellSize, settings.CellHeight, 0, settings.VertsPerPoly)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PolyMesh"/> class by creating polygons from contours.
/// </summary>
/// <param name="contSet">The <see cref="ContourSet"/> to generate polygons from.</param>
/// <param name="cellSize">The size of one voxel/cell.</param>
/// <param name="cellHeight">The height of one voxel/cell.</param>
/// <param name="borderSize">The size of the border around the mesh.</param>
/// <param name="numVertsPerPoly">The maximum number of vertices per polygon.</param>
public PolyMesh(ContourSet contSet, float cellSize, float cellHeight, int borderSize, int numVertsPerPoly)
{
//copy contour data
this.bounds = contSet.Bounds;
this.cellSize = cellSize;
this.cellHeight = cellHeight;
this.borderSize = borderSize;
//get maximum limits
//TODO move to ContourSet?
int maxVertices = 0;
int maxTris = 0;
int maxVertsPerCont = 0;
foreach (var cont in contSet)
{
int vertCount = cont.Vertices.Length;
//skip null contours
if (vertCount < 3)
continue;
maxVertices += vertCount;
maxTris += vertCount - 2;
maxVertsPerCont = Math.Max(maxVertsPerCont, vertCount);
}
//initialize the mesh members
var verts = new List<PolyVertex>(maxVertices);
var polys = new List<Polygon>(maxTris);
Queue<int> vertRemoveQueue = new Queue<int>(maxVertices);
this.numVertsPerPoly = numVertsPerPoly;
var vertDict = new Dictionary<PolyVertex, int>(new PolyVertex.RoughYEqualityComparer(2));
int[] indices = new int[maxVertsPerCont]; //keep track of vertex hash codes
Triangle[] tris = new Triangle[maxVertsPerCont];
List<Polygon> contPolys = new List<Polygon>(maxVertsPerCont + 1);
//extract contour data
foreach (Contour cont in contSet)
{
//skip null contours
if (cont.IsNull)
continue;
PolyVertex[] vertices = new PolyVertex[cont.Vertices.Length];
//triangulate contours
for (int i = 0; i < cont.Vertices.Length; i++)
{
var cv = cont.Vertices[i];
vertices[i] = new PolyVertex(cv.X, cv.Y, cv.Z);
indices[i] = i;
}
//Form triangles inside the area bounded by the contours
int ntris = Triangulate(vertices, indices, tris);
if (ntris <= 0) //TODO notify user when this happens. Logging?
ntris = -ntris;
//add and merge vertices
for (int i = 0; i < cont.Vertices.Length; i++)
{
var cv = cont.Vertices[i];
var pv = vertices[i];
//save the hash code for each vertex
indices[i] = AddVertex(vertDict, pv, verts);
if (RegionId.HasFlags(cv.RegionId, RegionFlags.VertexBorder))
{
//the vertex should be removed
vertRemoveQueue.Enqueue(indices[i]);
}
}
contPolys.Clear();
//iterate through all the triangles
for (int i = 0; i < ntris; i++)
{
Triangle ti = tris[i];
//make sure there are three distinct vertices. anything less can't be a polygon.
if (ti.Index0 == ti.Index1
|| ti.Index0 == ti.Index2
|| ti.Index1 == ti.Index2)
continue;
//each polygon has numVertsPerPoly
//index 0, 1, 2 store triangle vertices
//other polygon indexes (3 to numVertsPerPoly - 1) should be used for storing extra vertices when two polygons merge together
Polygon p = new Polygon(numVertsPerPoly, Area.Null, RegionId.Null, 0);
p.Vertices[0] = RemoveDiagonalFlag(indices[ti.Index0]);
p.Vertices[1] = RemoveDiagonalFlag(indices[ti.Index1]);
p.Vertices[2] = RemoveDiagonalFlag(indices[ti.Index2]);
contPolys.Add(p);
}
//no polygons generated, so skip
if (contPolys.Count == 0)
continue;
//merge polygons
if (numVertsPerPoly > 3)
{
while (true)
{
//find best polygons
int bestMergeVal = 0;
int bestPolyA = 0, bestPolyB = 0, bestEdgeA = 0, bestEdgeB = 0;
for (int i = 0; i < contPolys.Count - 1; i++)
{
int pj = i;
for (int j = i + 1; j < contPolys.Count; j++)
{
int pk = j;
int ea = 0, eb = 0;
int v = GetPolyMergeValue(contPolys, pj, pk, verts, out ea, out eb);
if (v > bestMergeVal)
{
bestMergeVal = v;
bestPolyA = i;
bestPolyB = j;
bestEdgeA = ea;
bestEdgeB = eb;
}
}
}
if (bestMergeVal > 0)
{
int pa = bestPolyA;
int pb = bestPolyB;
MergePolys(contPolys, pa, pb, bestEdgeA, bestEdgeB);
contPolys[pb] = contPolys[contPolys.Count - 1];
contPolys.RemoveAt(contPolys.Count - 1);
}
else
{
//no more merging
break;
}
}
}
//store polygons
for (int i = 0; i < contPolys.Count; i++)
{
Polygon p = contPolys[i];
Polygon p2 = new Polygon(numVertsPerPoly, cont.Area, cont.RegionId, 0);
Buffer.BlockCopy(p.Vertices, 0, p2.Vertices, 0, numVertsPerPoly * sizeof(int));
polys.Add(p2);
}
}
//remove edge vertices
while (vertRemoveQueue.Count > 0)
{
int i = vertRemoveQueue.Dequeue();
if (CanRemoveVertex(polys, i))
RemoveVertex(verts, polys, i);
}
//calculate adjacency (edges)
BuildMeshAdjacency(verts, polys, numVertsPerPoly);
//find portal edges
if (this.borderSize > 0)
{
//iterate through all the polygons
for (int i = 0; i < polys.Count; i++)
{
Polygon p = polys[i];
//iterate through all the vertices
for (int j = 0; j < numVertsPerPoly; j++)
{
if (p.Vertices[j] == NullId)
break;
//skip connected edges
if (p.NeighborEdges[j] != NullId)
continue;
int nj = j + 1;
if (nj >= numVertsPerPoly || p.Vertices[nj] == NullId)
nj = 0;
//grab two consecutive vertices
int va = p.Vertices[j];
int vb = p.Vertices[nj];
//set some flags
if (verts[va].X == 0 && verts[vb].X == 0)
p.NeighborEdges[j] = NeighborEdgeFlag | 0;
else if (verts[va].Z == contSet.Height && verts[vb].Z == contSet.Height)
p.NeighborEdges[j] = NeighborEdgeFlag | 1;
else if (verts[va].X == contSet.Width && verts[vb].X == contSet.Width)
p.NeighborEdges[j] = NeighborEdgeFlag | 2;
else if (verts[va].Z == 0 && verts[vb].Z == 0)
p.NeighborEdges[j] = NeighborEdgeFlag | 3;
}
}
}
this.vertices = verts.ToArray();
this.polygons = polys.ToArray();
}
/// <summary>
/// Gets the number of vertices
/// </summary>
public int VertCount
{
get
{
return vertices.Length;
}
}
/// <summary>
/// Gets the number of polygons
/// </summary>
public int PolyCount
{
get
{
return polygons.Length;
}
}
/// <summary>
/// Gets the number of vertices per polygon
/// </summary>
public int NumVertsPerPoly
{
get
{
return numVertsPerPoly;
}
}
/// <summary>
/// Gets the vertex data
/// </summary>
public PolyVertex[] Verts
{
get
{
return vertices;
}
}
/// <summary>
/// Gets the polygon data
/// </summary>
public Polygon[] Polys
{
get
{
return polygons;
}
}
/// <summary>
/// Gets the bounds.
/// </summary>
/// <value>The bounds.</value>
public BBox3 Bounds
{
get
{
return bounds;
}
}
/// <summary>
/// Gets the cell size
/// </summary>
public float CellSize
{
get
{
return cellSize;
}
}
/// <summary>
/// Gets the cell height
/// </summary>
public float CellHeight
{
get
{
return cellHeight;
}
}
/// <summary>
/// Gets the border size
/// </summary>
public int BorderSize
{
get
{
return borderSize;
}
}
/// <summary>
/// Determines if it is a boundary edge with the specified flag.
/// </summary>
/// <returns><c>true</c> if is boundary edge the specified flag; otherwise, <c>false</c>.</returns>
/// <param name="flag">The flag.</param>
public static bool IsBoundaryEdge(int flag)
{
return (flag & NeighborEdgeFlag) != 0;
}
/// <summary>
/// Determines if it is an interior edge with the specified flag.
/// </summary>
/// <returns><c>true</c> if is interior edge the specified flag; otherwise, <c>false</c>.</returns>
/// <param name="flag">The flag.</param>
public static bool IsInteriorEdge(int flag)
{
return (flag & NeighborEdgeFlag) == 0;
}
/// <summary>
/// Determines if it is a diagonal flag on the specified index.
/// </summary>
/// <param name="index">The index</param>
/// <returns><c>true</c> if it is a diagonal flag on the specified index; otherwise, <c>false</c>.</returns>
public static bool HasDiagonalFlag(int index)
{
return (index & DiagonalFlag) != 0;
}
/// <summary>
/// True if and only if (v[i], v[j]) is a proper internal diagonal of polygon.
/// </summary>
/// <param name="i">Vertex index i</param>
/// <param name="j">Vertex index j</param>
/// <param name="verts">Contour vertices</param>
/// <param name="indices">PolyMesh indices</param>
/// <returns>True, if internal diagonal. False, if otherwise.</returns>
public static bool Diagonal(int i, int j, PolyVertex[] verts, int[] indices)
{
return InCone(i, j, verts, indices) && Diagonalie(i, j, verts, indices);
}
/// <summary>
/// True if and only if diagonal (i, j) is strictly internal to polygon
/// in neighborhood of i endpoint.
/// </summary>
/// <param name="i">Vertex index i</param>
/// <param name="j">Vertex index j</param>
/// <param name="verts">Contour vertices</param>
/// <param name="indices">PolyMesh indices</param>
/// <returns>True, if internal. False, if otherwise.</returns>
public static bool InCone(int i, int j, PolyVertex[] verts, int[] indices)
{
int pi = RemoveDiagonalFlag(indices[i]);
int pj = RemoveDiagonalFlag(indices[j]);
int pi1 = RemoveDiagonalFlag(indices[Next(i, verts.Length)]);
int pin1 = RemoveDiagonalFlag(indices[Prev(i, verts.Length)]);
//if P[i] is convex vertex (i + 1 left or on (i - 1, i))
if (PolyVertex.IsLeftOn(ref verts[pin1], ref verts[pi], ref verts[pi1]))
return PolyVertex.IsLeft(ref verts[pi], ref verts[pj], ref verts[pin1]) && PolyVertex.IsLeft(ref verts[pj], ref verts[pi], ref verts[pi1]);
//assume (i - 1, i, i + 1) not collinear
return !(PolyVertex.IsLeftOn(ref verts[pi], ref verts[pj], ref verts[pi1]) && PolyVertex.IsLeftOn(ref verts[pj], ref verts[pi], ref verts[pin1]));
}
/// <summary>
/// True if and only if (v[i], v[j]) is internal or external diagonal
/// ignoring edges incident to v[i] or v[j].
/// </summary>
/// <param name="i">Vertex index i</param>
/// <param name="j">Vertex index j</param>
/// <param name="verts">Contour vertices</param>
/// <param name="indices">PolyMesh indices</param>
/// <returns>True, if internal or external diagonal. False, if otherwise.</returns>
public static bool Diagonalie(int i, int j, PolyVertex[] verts, int[] indices)
{
int d0 = RemoveDiagonalFlag(indices[i]);
int d1 = RemoveDiagonalFlag(indices[j]);
//for each edge (k, k + 1)
for (int k = 0; k < verts.Length; k++)
{
int k1 = Next(k, verts.Length);
//skip edges incident to i or j
if (!((k == i) || (k1 == i) || (k == j) || (k1 == j)))
{
int p0 = RemoveDiagonalFlag(indices[k]);
int p1 = RemoveDiagonalFlag(indices[k1]);
if (PolyVertex.Equal2D(ref verts[d0], ref verts[p0]) ||
PolyVertex.Equal2D(ref verts[d1], ref verts[p0]) ||
PolyVertex.Equal2D(ref verts[d0], ref verts[p1]) ||
PolyVertex.Equal2D(ref verts[d1], ref verts[p1]))
continue;
if (PolyVertex.Intersect(ref verts[d0], ref verts[d1], ref verts[p0], ref verts[p1]))
return false;
}
}
return true;
}
/// <summary>
/// Gets the previous vertex index
/// </summary>
/// <param name="i">The current index</param>
/// <param name="n">The max number of vertices</param>
/// <returns>The previous index</returns>
private static int Prev(int i, int n)
{
return i - 1 >= 0 ? i - 1 : n - 1;
}
/// <summary>
/// Gets the next vertex index
/// </summary>
/// <param name="i">The current index</param>
/// <param name="n">The max number of vertices</param>
/// <returns>The next index</returns>
private static int Next(int i, int n)
{
return i + 1 < n ? i + 1 : 0;
}
/// <summary>
/// Determines whether the vertices follow a certain order
/// </summary>
/// <param name="a">Vertex A</param>
/// <param name="b">Vertex B</param>
/// <param name="c">Vertex C</param>
/// <returns>True if conditions met, false if not</returns>
private static bool ULeft(PolyVertex a, PolyVertex b, PolyVertex c)
{
return (b.X - a.X) * (c.Z - a.Z) -
(c.X - a.X) * (b.Z - a.Z) < 0;
}
/// <summary>
/// Sets the diagonal flag for a vertex
/// </summary>
/// <param name="index">The vertex index</param>
private static void SetDiagonalFlag(ref int index)
{
index |= DiagonalFlag;
}
/// <summary>
/// Remove the diagonal flag for a vertex
/// </summary>
/// <param name="index">The vertex index</param>
/// <returns>The new index</returns>
private static int RemoveDiagonalFlag(int index)
{
return index & ~DiagonalFlag;
}
/// <summary>
/// Remove the diagonal flag for a vertex
/// </summary>
/// <param name="index">The vertex index</param>
private static void RemoveDiagonalFlag(ref int index)
{
index &= ~DiagonalFlag;
}
/// <summary>
/// Walk the edges of a contour to determine whether a triangle can be formed.
/// Form as many triangles as possible.
/// </summary>
/// <param name="verts">Vertices array</param>
/// <param name="indices">Indices array</param>
/// <param name="tris">Triangles array</param>
/// <returns>The number of triangles.</returns>
private static int Triangulate(PolyVertex[] verts, int[] indices, Triangle[] tris)
{
int ntris = 0;
int n = verts.Length;
//last bit of index determines whether vertex can be removed
for (int i = 0; i < n; i++)
{
int i1 = Next(i, n);
int i2 = Next(i1, n);
if (Diagonal(i, i2, verts, indices))
{
SetDiagonalFlag(ref indices[i1]);
}
}
//need 3 verts minimum for a polygon
while (n > 3)
{
//find the minimum distance betwee two vertices.
//also, save their index
int minLen = -1;
int minIndex = -1;
for (int i = 0; i < n; i++)
{
int i1 = Next(i, n);
if (HasDiagonalFlag(indices[i1]))
{
int p0 = RemoveDiagonalFlag(indices[i]);
int p2 = RemoveDiagonalFlag(indices[Next(i1, n)]);
int dx = verts[p2].X - verts[p0].X;
int dy = verts[p2].Z - verts[p0].Z;
int len = dx * dx + dy * dy;
if (minLen < 0 || len < minLen)
{
minLen = len;
minIndex = i;
}
}
}
if (minIndex == -1)
{
minLen = -1;
minIndex = -1;
for (int i = 0; i < n; i++)
{
int i1 = Next(i, n);
int i2 = Next(i1, n);
if (IsDiagonalLoose(i, i2, verts, indices))
{
var p0 = verts[RemoveDiagonalFlag(indices[i])];
var p2 = verts[RemoveDiagonalFlag(indices[Next(i2, n)])];
var dx = p2.X - p0.X;
var dy = p2.Z - p0.Z;
var len = dx * dx + dy * dy;
if (minLen < 0 || len < minLen)
{
minLen = len;
minIndex = i;
}
}
}
if (minIndex == -1)
{
return -ntris;
}
}
int mi = minIndex;
int mi1 = Next(mi, n);
int mi2 = Next(mi1, n);
tris[ntris] = new Triangle();
tris[ntris].Index0 = RemoveDiagonalFlag(indices[mi]);
tris[ntris].Index1 = RemoveDiagonalFlag(indices[mi1]);
tris[ntris].Index2 = RemoveDiagonalFlag(indices[mi2]);
ntris++;
//remove P[i1]
n--;
for (int k = mi1; k < n; k++)
indices[k] = indices[k + 1];
if (mi1 >= n) mi1 = 0;
mi = Prev(mi1, n);
//update diagonal flags
if (Diagonal(Prev(mi, n), mi1, verts, indices))
{
SetDiagonalFlag(ref indices[mi]);
}
else
{
RemoveDiagonalFlag(ref indices[mi]);
}
if (Diagonal(mi, Next(mi1, n), verts, indices))
{
SetDiagonalFlag(ref indices[mi1]);
}
else
{
RemoveDiagonalFlag(ref indices[mi1]);
}
}
//append remaining triangle
tris[ntris] = new Triangle();
tris[ntris].Index0 = RemoveDiagonalFlag(indices[0]);
tris[ntris].Index1 = RemoveDiagonalFlag(indices[1]);
tris[ntris].Index2 = RemoveDiagonalFlag(indices[2]);
ntris++;
return ntris;
}
static bool IsInConeLoose(int i, int j, PolyVertex[] verts, int[] indices)
{
var n = verts.Length;
// int p2 = RemoveDiagonalFlag(indices[Next(i1, n)]);
//const int* pi = &verts[(indices[i] & 0x0fffffff) * 4];
//const int* pj = &verts[(indices[j] & 0x0fffffff) * 4];
//const int* pi1 = &verts[(indices[next(i, n)] & 0x0fffffff) * 4];
//const int* pin1 = &verts[(indices[prev(i, n)] & 0x0fffffff) * 4];
var pi = verts[RemoveDiagonalFlag(indices[i])];
var pj = verts[RemoveDiagonalFlag(indices[j])];
var pi1 = verts[RemoveDiagonalFlag(indices[Next(i, n)])];
var pin1 = verts[RemoveDiagonalFlag(indices[Prev(i, n)])];
// If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
if (PolyVertex.IsLeftOn(ref pin1, ref pi, ref pi1))
return PolyVertex.IsLeftOn(ref pi, ref pj, ref pin1) && PolyVertex.IsLeftOn(ref pj, ref pi, ref pi1);
// Assume (i-1,i,i+1) not collinear.
// else P[i] is reflex.
return !(PolyVertex.IsLeftOn(ref pi, ref pj, ref pi1) && PolyVertex.IsLeftOn(ref pj, ref pi, ref pin1));
}
static bool IsDiagonalLoose(int i, int j, PolyVertex[] verts, int[] indices)
{
//var n = verts.Length;
return IsInConeLoose(i, j, verts, indices) && IsDiagonalieLoose(i, j, verts, indices);
}
static bool IsDiagonalieLoose(int i, int j, PolyVertex[] verts, int[] indices)
{
//const int* d0 = &verts[(indices[i] & 0x0fffffff) * 4];
//const int* d1 = &verts[(indices[j] & 0x0fffffff) * 4];
var n = verts.Length;
var d0 = verts[RemoveDiagonalFlag(indices[i])];
var d1 = verts[RemoveDiagonalFlag(indices[j])];
// For each edge (k,k+1) of P
for (int k = 0; k < n; k++)
{
int k1 = Next(k, n);
// Skip edges incident to i or j
if (!((k == i) || (k1 == i) || (k == j) || (k1 == j)))
{
//const int* p0 = &verts[(indices[k] & 0x0fffffff) * 4];
//const int* p1 = &verts[(indices[k1] & 0x0fffffff) * 4];
var p0 = verts[RemoveDiagonalFlag(indices[k])];
var p1 = verts[RemoveDiagonalFlag(indices[k1])];
if (PolyVertex.Equal2D(ref d0, ref p0) ||
PolyVertex.Equal2D(ref d1, ref p0) ||
PolyVertex.Equal2D(ref d0, ref p1) ||
PolyVertex.Equal2D(ref d1, ref p1))
continue;
if (IsIntersectProp(ref d0, ref d1, ref p0, ref p1))
return false;
}
}
return true;
}
static bool xorb(bool x, bool y)
{
return !x ^ !y;
}
// Returns true iff ab properly intersects cd: they share
// a point interior to both segments. The properness of the
// intersection is ensured by using strict leftness.
static bool IsIntersectProp(ref PolyVertex a, ref PolyVertex b, ref PolyVertex c, ref PolyVertex d)
{
// Eliminate improper cases.
if (PolyVertex.IsCollinear(ref a, ref b, ref c) || PolyVertex.IsCollinear(ref a, ref b, ref d) ||
PolyVertex.IsCollinear(ref c, ref d, ref a) || PolyVertex.IsCollinear(ref c, ref d, ref b))
return false;
return xorb(PolyVertex.IsLeft(ref a, ref b, ref c), PolyVertex.IsLeft(ref a, ref b, ref d)) &&
xorb(PolyVertex.IsLeft(ref c, ref d, ref a), PolyVertex.IsLeft(ref c, ref d, ref b));
}
/// <summary>
/// Generate a new vertices with (x, y, z) coordiates and return the hash code index
/// </summary>
/// <param name="vertDict">Vertex dictionary that maps coordinates to index</param>
/// <param name="v">A vertex.</param>
/// <param name="verts">The list of vertices</param>
/// <returns>The vertex index</returns>
private static int AddVertex(Dictionary<PolyVertex, int> vertDict, PolyVertex v, List<PolyVertex> verts)
{
int index;
if (vertDict.TryGetValue(v, out index))
{
return index;
}
index = verts.Count;
verts.Add(v);
vertDict.Add(v, index);
return index;
}
/// <summary>
/// Try to merge two polygons. If possible, return the distance squared between two vertices.
/// </summary>
/// <param name="polys">Polygon list</param>
/// <param name="polyA">Polygon A</param>
/// <param name="polyB">Polygon B</param>
/// <param name="verts">Vertex list</param>
/// <param name="edgeA">Shared edge's endpoint A</param>
/// <param name="edgeB">Shared edge's endpoint B</param>
/// <returns>The distance between two vertices</returns>
private static int GetPolyMergeValue(List<Polygon> polys, int polyA, int polyB, List<PolyVertex> verts, out int edgeA, out int edgeB)
{
int numVertsA = polys[polyA].VertexCount;
int numVertsB = polys[polyB].VertexCount;
//check if polygons share an edge
edgeA = -1;
edgeB = -1;
//don't merge if result is too big
if (numVertsA + numVertsB - 2 > polys[polyA].Vertices.Length)
return -1;
//iterate through all the vertices of polygonA
for (int i = 0; i < numVertsA; i++)
{
//take two nearby vertices
int va0 = polys[polyA].Vertices[i];
int va1 = polys[polyA].Vertices[(i + 1) % numVertsA];
//make sure va0 < va1
if (va0 > va1)
{
int temp = va0;
va0 = va1;
va1 = temp;
}
//iterate through all the vertices of polygon B
for (int j = 0; j < numVertsB; j++)
{
//take two nearby vertices
int vb0 = polys[polyB].Vertices[j];
int vb1 = polys[polyB].Vertices[(j + 1) % numVertsB];
//make sure vb0 < vb1
if (vb0 > vb1)
{
int temp = vb0;
vb0 = vb1;
vb1 = temp;
}
//edge shared, since vertices are equal
if (va0 == vb0 && va1 == vb1)
{
edgeA = i;
edgeB = j;
break;
}
}
}
//no common edge
if (edgeA == -1 || edgeB == -1)
return -1;
//check if merged polygon would be convex
int vertA, vertB, vertC;
vertA = polys[polyA].Vertices[(edgeA + numVertsA - 1) % numVertsA];
vertB = polys[polyA].Vertices[edgeA];
vertC = polys[polyB].Vertices[(edgeB + 2) % numVertsB];
if (!ULeft(verts[vertA], verts[vertB], verts[vertC]))
return -1;
vertA = polys[polyB].Vertices[(edgeB + numVertsB - 1) % numVertsB];
vertB = polys[polyB].Vertices[edgeB];
vertC = polys[polyA].Vertices[(edgeA + 2) % numVertsA];
if (!ULeft(verts[vertA], verts[vertB], verts[vertC]))
return -1;
vertA = polys[polyA].Vertices[edgeA];
vertB = polys[polyA].Vertices[(edgeA + 1) % numVertsA];
int dx = (int)(verts[vertA].X - verts[vertB].X);
int dy = (int)(verts[vertA].Z - verts[vertB].Z);
return dx * dx + dy * dy;
}
/// <summary>
/// If vertex can't be removed, there is no need to spend time deleting it.
/// </summary>
/// <param name="polys">The polygon list</param>
/// <param name="remove">The vertex index</param>
/// <returns>True, if vertex can be removed. False, if otherwise.</returns>
private static bool CanRemoveVertex(List<Polygon> polys, int remove)
{
//count number of polygons to remove
int numRemovedVerts = 0;
int numTouchedVerts = 0;
int numRemainingEdges = 0;
for (int i = 0; i < polys.Count; i++)
{
Polygon p = polys[i];
int nv = p.VertexCount;
int numRemoved = 0;
int numVerts = 0;
for (int j = 0; j < nv; j++)
{
if (p.Vertices[j] == remove)
{
numTouchedVerts++;
numRemoved++;
}
numVerts++;
}
if (numRemoved > 0)
{
numRemovedVerts += numRemoved;
numRemainingEdges += numVerts - (numRemoved + 1);
}
}
//don't remove a vertex from a triangle since you need at least three vertices to make a polygon
if (numRemainingEdges <= 2)
return false;
//find edges which share removed vertex
int maxEdges = numTouchedVerts * 2;
int nedges = 0;
int[] edges = new int[maxEdges * 3];
for (int i = 0; i < polys.Count; i++)
{
Polygon p = polys[i];
int nv = p.VertexCount;
//collect edges which touch removed vertex
for (int j = 0, k = nv - 1; j < nv; k = j++)
{
if (p.Vertices[j] == remove || p.Vertices[k] == remove)
{
//arrange edge so that a has the removed value
int a = p.Vertices[j], b = p.Vertices[k];
if (b == remove)
{
int temp = a;
a = b;
b = temp;
}
//check if edge exists
bool exists = false;
for (int m = 0; m < nedges; m++)
{
int e = m * 3;
if (edges[e + 1] == b)
{
//increment vertex share count
edges[e + 2]++;
exists = true;
}
}
//add new edge
if (!exists)
{
int e = nedges * 3;
edges[e + 0] = a;
edges[e + 1] = b;
edges[e + 2] = 1;
nedges++;
}
}
}
}
//make sure there can't be more than two open edges
//since there could be two non-adjacent polygons which share the same vertex, which shouldn't be removed
int numOpenEdges = 0;
for (int i = 0; i < nedges; i++)
{
if (edges[i * 3 + 2] < 2)
numOpenEdges++;
}
if (numOpenEdges > 2)
return false;
return true;
}
/// <summary>
/// Connect two adjacent vertices with edges.
/// </summary>
/// <param name="vertices">The vertex list</param>
/// <param name="polys">The polygon list</param>
/// <param name="numVertsPerPoly">Number of vertices per polygon</param>
private static void BuildMeshAdjacency(List<PolyVertex> vertices, List<Polygon> polys, int numVertsPerPoly)
{
int maxEdgeCount = polys.Count * numVertsPerPoly;
int[] firstEdge = new int[vertices.Count + maxEdgeCount];
int nextEdge = vertices.Count;
List<AdjacencyEdge> edges = new List<AdjacencyEdge>(maxEdgeCount);
for (int i = 0; i < vertices.Count; i++)
firstEdge[i] = NullId;
//Iterate through all the polygons
for (int i = 0; i < polys.Count; i++)
{
Polygon p = polys[i];
//Iterate through all the vertices
for (int j = 0; j < numVertsPerPoly; j++)
{
if (p.Vertices[j] == NullId)
break;
//get closest two verts
int v0 = p.Vertices[j];
int v1 = (j + 1 >= numVertsPerPoly || p.Vertices[j + 1] == NullId) ? p.Vertices[0] : p.Vertices[j + 1];
if (v0 < v1)
{
AdjacencyEdge edge;
//store vertices
edge.Vert0 = v0;
edge.Vert1 = v1;
//poly array stores index of polygon
//polyEdge stores the vertex
edge.Poly0 = i;
edge.PolyEdge0 = j;
edge.Poly1 = i;
edge.PolyEdge1 = 0;
//insert edge
firstEdge[nextEdge + edges.Count] = firstEdge[v0];
firstEdge[v0] = edges.Count;
edges.Add(edge);
}
}
}
//Iterate through all the polygons again
for (int i = 0; i < polys.Count; i++)
{
Polygon p = polys[i];
for (int j = 0; j < numVertsPerPoly; j++)
{
if (p.Vertices[j] == NullId)
break;
//get adjacent vertices
int v0 = p.Vertices[j];
int v1 = (j + 1 >= numVertsPerPoly || p.Vertices[j + 1] == NullId) ? p.Vertices[0] : p.Vertices[j + 1];
if (v0 > v1)
{
//Iterate through all the edges
for (int e = firstEdge[v1]; e != NullId; e = firstEdge[nextEdge + e])
{
AdjacencyEdge edge = edges[e];
if (edge.Vert1 == v0 && edge.Poly0 == edge.Poly1)
{
edge.Poly1 = i;
edge.PolyEdge1 = j;
edges[e] = edge;
break;
}
}
}
}
}
//store adjacency
for (int i = 0; i < edges.Count; i++)
{
AdjacencyEdge e = edges[i];
//the endpoints belong to different polygons
if (e.Poly0 != e.Poly1)
{
//store other polygon number as part of extra info
polys[e.Poly0].NeighborEdges[e.PolyEdge0] = e.Poly1;
polys[e.Poly1].NeighborEdges[e.PolyEdge1] = e.Poly0;
}
}
}
/// <summary>
/// The two polygon arrrays are merged into a single array
/// </summary>
/// <param name="polys">The polygon list</param>
/// <param name="polyA">Polygon A</param>
/// <param name="polyB">Polygon B</param>
/// <param name="edgeA">Starting edge for polygon A</param>
/// <param name="edgeB">Starting edge for polygon B</param>
private void MergePolys(List<Polygon> polys, int polyA, int polyB, int edgeA, int edgeB)
{
//TODO replace with Polygon.Merge()
int numA = polys[polyA].VertexCount;
int numB = polys[polyB].VertexCount;
int[] temp = new int[numA + numB];
//merge
for (int i = 0; i < numVertsPerPoly; i++)
temp[i] = NullId;
int n = 0;
//add polygon A
for (int i = 0; i < numA - 1; i++)
temp[n++] = polys[polyA].Vertices[(edgeA + 1 + i) % numA];
//add polygon B
for (int i = 0; i < numB - 1; i++)
temp[n++] = polys[polyB].Vertices[(edgeB + 1 + i) % numB];
//save merged data to new polygon
for (int i = 0; i < numVertsPerPoly; i++)
polys[polyA].Vertices[i] = temp[i];
}
/// <summary>
/// Removing vertices will leave holes that have to be triangulated again.
/// </summary>
/// <param name="verts">A list of vertices</param>
/// <param name="polys">A list of polygons</param>
/// <param name="vertex">The vertex to remove</param>
private void RemoveVertex(List<PolyVertex> verts, List<Polygon> polys, int vertex)
{
int numVertsPerPoly = this.numVertsPerPoly;
//count number of polygons to remove
int numRemovedVerts = 0;
for (int i = 0; i < polys.Count; i++)
{
Polygon p = polys[i];
for (int j = 0; j < p.VertexCount; j++)
{
if (p.Vertices[j] == vertex)
numRemovedVerts++;
}
}
List<Edge> edges = new List<Edge>(numRemovedVerts * numVertsPerPoly);
List<int> hole = new List<int>(numRemovedVerts * numVertsPerPoly);
List<RegionId> regions = new List<RegionId>(numRemovedVerts * numVertsPerPoly);
List<Area> areas = new List<Area>(numRemovedVerts * numVertsPerPoly);
//Iterate through all the polygons
for (int i = 0; i < polys.Count; i++)
{
Polygon p = polys[i];
if (p.ContainsVertex(vertex))
{
int nv = p.VertexCount;
//collect edges which don't touch removed vertex
for (int j = 0, k = nv - 1; j < nv; k = j++)
if (p.Vertices[j] != vertex && p.Vertices[k] != vertex)
edges.Add(new Edge(p.Vertices[k], p.Vertices[j], p.RegionId, p.Area));
polys[i] = polys[polys.Count - 1];
polys.RemoveAt(polys.Count - 1);
i--;
}
}
//remove vertex
verts.RemoveAt(vertex);
//adjust indices
for (int i = 0; i < polys.Count; i++)
{
Polygon p = polys[i];
for (int j = 0; j < p.VertexCount; j++)
{
if (p.Vertices[j] > vertex)
p.Vertices[j]--;
}
}
for (int i = 0; i < edges.Count; i++)
{
Edge edge = edges[i];
if (edge.Vert0 > vertex)
edge.Vert0--;
if (edge.Vert1 > vertex)
edge.Vert1--;
edges[i] = edge;
}
if (edges.Count == 0)
return;
//Find edges surrounding the holes
hole.Add(edges[0].Vert0);
regions.Add(edges[0].Region);
areas.Add(edges[0].Area);
while (edges.Count > 0)
{
bool match = false;
for (int i = 0; i < edges.Count; i++)
{
Edge edge = edges[i];
bool add = false;
if (hole[0] == edge.Vert1)
{
//segment matches beginning of hole boundary
hole.Insert(0, edge.Vert0);
regions.Insert(0, edge.Region);
areas.Insert(0, edge.Area);
add = true;
}
else if (hole[hole.Count - 1] == edge.Vert0)
{
//segment matches end of hole boundary
hole.Add(edge.Vert1);
regions.Add(edge.Region);
areas.Add(edge.Area);
add = true;
}
if (add)
{
//edge segment was added so remove it
edges[i] = edges[edges.Count - 1];
edges.RemoveAt(edges.Count - 1);
match = true;
i--;
}
}
if (!match)
break;
}
var tris = new Triangle[hole.Count];
var tverts = new PolyVertex[hole.Count];
var thole = new int[hole.Count];
//generate temp vertex array for triangulation
for (int i = 0; i < hole.Count; i++)
{
int polyIndex = hole[i];
tverts[i] = verts[polyIndex];
thole[i] = i;
}
//triangulate the hole
int ntris = Triangulate(tverts, thole, tris);
if (ntris < 0)
ntris = -ntris;
//merge hole triangles back to polygons
List<Polygon> mergePolys = new List<Polygon>(ntris + 1);
for (int j = 0; j < ntris; j++)
{
Triangle t = tris[j];
if (t.Index0 != t.Index1 && t.Index0 != t.Index2 && t.Index1 != t.Index2)
{
Polygon p = new Polygon(numVertsPerPoly, areas[t.Index0], regions[t.Index0], 0);
p.Vertices[0] = hole[t.Index0];
p.Vertices[1] = hole[t.Index1];
p.Vertices[2] = hole[t.Index2];
mergePolys.Add(p);
}
}
if (mergePolys.Count == 0)
return;
//merge polygons
if (numVertsPerPoly > 3)
{
while (true)
{
//find best polygons
int bestMergeVal = 0;
int bestPolyA = 0, bestPolyB = 0, bestEa = 0, bestEb = 0;
for (int j = 0; j < mergePolys.Count - 1; j++)
{
int pj = j;
for (int k = j + 1; k < mergePolys.Count; k++)
{
int pk = k;
int edgeA, edgeB;
int v = GetPolyMergeValue(mergePolys, pj, pk, verts, out edgeA, out edgeB);
if (v > bestMergeVal)
{
bestMergeVal = v;
bestPolyA = j;
bestPolyB = k;
bestEa = edgeA;
bestEb = edgeB;
}
}
}
if (bestMergeVal > 0)
{
int polyA = bestPolyA;
int polyB = bestPolyB;
MergePolys(mergePolys, polyA, polyB, bestEa, bestEb);
mergePolys[polyB] = mergePolys[mergePolys.Count - 1];
mergePolys.RemoveAt(mergePolys.Count - 1);
}
else
{
//no more merging
break;
}
}
}
//add merged polys back to the list.
polys.AddRange(mergePolys);
}
/// <summary>
/// A triangle contains three indices.
/// </summary>
private struct Triangle
{
public int Index0;
public int Index1;
public int Index2;
}
/// <summary>
/// Two adjacent vertices form an edge.
/// </summary>
private struct AdjacencyEdge
{
public int Vert0;
public int Vert1;
public int PolyEdge0;
public int PolyEdge1;
public int Poly0;
public int Poly1;
}
/// <summary>
/// Another edge structure, but this one contains the RegionId and AreaId.
/// </summary>
private struct Edge
{
public int Vert0;
public int Vert1;
public RegionId Region;
public Area Area;
/// <summary>
/// Initializes a new instance of the <see cref="Edge"/> struct.
/// </summary>
/// <param name="vert0">Vertex A</param>
/// <param name="vert1">Vertex B</param>
/// <param name="region">Region id</param>
/// <param name="area">Area id</param>
public Edge(int vert0, int vert1, RegionId region, Area area)
{
Vert0 = vert0;
Vert1 = vert1;
Region = region;
Area = area;
}
}
/// <summary>
/// Each polygon is a collection of vertices. It is the basic unit of the PolyMesh
/// </summary>
public class Polygon
{
private int[] vertices; //"numVertsPerPoly" elements
private int[] neighborEdges; //"numVertsPerPoly" elements
private Area area;
private RegionId regionId;
private int flags;
/// <summary>
/// Initializes a new instance of the <see cref="Polygon" /> class.
/// </summary>
/// <param name="numVertsPerPoly">The number of vertices per polygon.</param>
/// <param name="area">The AreaId</param>
/// <param name="regionId">The RegionId</param>
/// <param name="flags">Polygon flags</param>
public Polygon(int numVertsPerPoly, Area area, RegionId regionId, int flags)
{
vertices = new int[numVertsPerPoly];
neighborEdges = new int[numVertsPerPoly];
this.area = area;
this.regionId = regionId;
this.flags = flags;
for (int i = 0; i < numVertsPerPoly; i++)
{
vertices[i] = NullId;
neighborEdges[i] = NullId;
}
}
/// <summary>
/// Gets the indices for the vertices.
/// </summary>
/// <value>The vertices.</value>
public int[] Vertices
{
get
{
return vertices;
}
}
/// <summary>
/// Gets the neighbor edges.
/// </summary>
/// <value>The neighbor edges.</value>
public int[] NeighborEdges
{
get
{
return neighborEdges;
}
}
/// <summary>
/// Gets or sets the area id
/// </summary>
public Area Area
{
get
{
return area;
}
set
{
area = value;
}
}
/// <summary>
/// Gets or sets the region identifier.
/// </summary>
/// <value>The region identifier.</value>
public RegionId RegionId
{
get
{
return regionId;
}
set
{
regionId = value;
}
}
/// <summary>
/// Gets or sets the flags.
/// </summary>
/// <value>The flags.</value>
public int Flags
{
get
{
return flags;
}
set
{
flags = value;
}
}
/// <summary>
/// Gets the the number of vertex.
/// </summary>
/// <value>The vertex count.</value>
public int VertexCount
{
get
{
for (int i = 0; i < vertices.Length; i++)
if (vertices[i] == NullId)
return i;
return vertices.Length;
}
}
/// <summary>
/// Determine if the vertex is in polygon.
/// </summary>
/// <returns><c>true</c>, if vertex was containsed, <c>false</c> otherwise.</returns>
/// <param name="vertex">The Vertex.</param>
public bool ContainsVertex(int vertex)
{
//iterate through all the vertices
for (int i = 0; i < vertices.Length; i++)
{
//find the vertex, return false if at end of defined polygon.
int v = vertices[i];
if (v == vertex)
return true;
else if (v == NullId)
return false;
}
return false;
}
}
}
}