reworked mesh decimation yes
This commit is contained in:
706
LightlessSync/ThirdParty/Nanomesh/Mesh/ConnectedMesh/ConnectedMesh.cs
vendored
Normal file
706
LightlessSync/ThirdParty/Nanomesh/Mesh/ConnectedMesh/ConnectedMesh.cs
vendored
Normal file
@@ -0,0 +1,706 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
// Let's say F = 2V
|
||||
// Halfedge mesh is V * sizeof(vertex) + 3F * sizeof(Halfedge) + F * sizeof(Face) = 16 * 0.5F + 3F * 20 + 4F = 72F
|
||||
// Connected mesh is V * sizeof(Vector3) + 3F * sizeof(Node) + F * sizeof(Face) = 12 * 0.5F + 3F * 12 + 12F = 54F (without attributes)
|
||||
// Connected mesh no face is V * sizeof(Vector3) + 3F * sizeof(Node) = 12 * 0.5F + 3F * 12 = 42F (without attributes)
|
||||
public partial class ConnectedMesh
|
||||
{
|
||||
// Todo : make this private (can only be modified from the inside)
|
||||
public Vector3[] positions;
|
||||
public MetaAttributeList attributes;
|
||||
public Node[] nodes;
|
||||
public Group[] groups;
|
||||
public AttributeDefinition[] attributeDefinitions;
|
||||
|
||||
public int[] PositionToNode => _positionToNode ?? (_positionToNode = GetPositionToNode());
|
||||
private int[] _positionToNode;
|
||||
|
||||
internal int _faceCount;
|
||||
public int FaceCount => _faceCount;
|
||||
|
||||
public bool AreNodesSiblings(int nodeIndexA, int nodeIndexB)
|
||||
{
|
||||
return nodes[nodeIndexA].position == nodes[nodeIndexB].position;
|
||||
}
|
||||
|
||||
public int[] GetPositionToNode()
|
||||
{
|
||||
int[] positionToNode = new int[positions.Length];
|
||||
|
||||
for (int i = 0; i < positions.Length; i++)
|
||||
{
|
||||
positionToNode[i] = -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
if (!nodes[i].IsRemoved)
|
||||
{
|
||||
positionToNode[nodes[i].position] = i;
|
||||
}
|
||||
}
|
||||
|
||||
return positionToNode;
|
||||
}
|
||||
|
||||
public int GetEdgeCount(int nodeIndex)
|
||||
{
|
||||
return GetRelativesCount(nodeIndex) + 1;
|
||||
}
|
||||
|
||||
public int GetRelativesCount(int nodeIndex)
|
||||
{
|
||||
int k = 0;
|
||||
int relative = nodeIndex;
|
||||
while ((relative = nodes[relative].relative) != nodeIndex)
|
||||
{
|
||||
k++;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
public int GetSiblingsCount(int nodeIndex)
|
||||
{
|
||||
int k = 0;
|
||||
int sibling = nodeIndex;
|
||||
while ((sibling = nodes[sibling].sibling) != nodeIndex)
|
||||
{
|
||||
k++;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
public int ReconnectSiblings(int nodeIndex)
|
||||
{
|
||||
int sibling = nodeIndex;
|
||||
int lastValid = -1;
|
||||
int firstValid = -1;
|
||||
int position = -1;
|
||||
|
||||
do
|
||||
{
|
||||
if (nodes[sibling].IsRemoved)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstValid == -1)
|
||||
{
|
||||
firstValid = sibling;
|
||||
position = nodes[sibling].position;
|
||||
}
|
||||
|
||||
if (lastValid != -1)
|
||||
{
|
||||
nodes[lastValid].sibling = sibling;
|
||||
nodes[lastValid].position = position;
|
||||
}
|
||||
|
||||
lastValid = sibling;
|
||||
}
|
||||
while ((sibling = nodes[sibling].sibling) != nodeIndex);
|
||||
|
||||
if (lastValid == -1)
|
||||
{
|
||||
return -1; // All siblings were removed
|
||||
}
|
||||
|
||||
// Close the loop
|
||||
nodes[lastValid].sibling = firstValid;
|
||||
nodes[lastValid].position = position;
|
||||
|
||||
return firstValid;
|
||||
}
|
||||
|
||||
public int ReconnectSiblings(int nodeIndexA, int nodeIndexB, int position)
|
||||
{
|
||||
int sibling = nodeIndexA;
|
||||
int lastValid = -1;
|
||||
int firstValid = -1;
|
||||
|
||||
do
|
||||
{
|
||||
if (nodes[sibling].IsRemoved)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstValid == -1)
|
||||
{
|
||||
firstValid = sibling;
|
||||
//position = nodes[sibling].position;
|
||||
}
|
||||
|
||||
if (lastValid != -1)
|
||||
{
|
||||
nodes[lastValid].sibling = sibling;
|
||||
nodes[lastValid].position = position;
|
||||
}
|
||||
|
||||
lastValid = sibling;
|
||||
}
|
||||
while ((sibling = nodes[sibling].sibling) != nodeIndexA);
|
||||
|
||||
sibling = nodeIndexB;
|
||||
do
|
||||
{
|
||||
if (nodes[sibling].IsRemoved)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstValid == -1)
|
||||
{
|
||||
firstValid = sibling;
|
||||
//position = nodes[sibling].position;
|
||||
}
|
||||
|
||||
if (lastValid != -1)
|
||||
{
|
||||
nodes[lastValid].sibling = sibling;
|
||||
nodes[lastValid].position = position;
|
||||
}
|
||||
|
||||
lastValid = sibling;
|
||||
}
|
||||
while ((sibling = nodes[sibling].sibling) != nodeIndexB);
|
||||
|
||||
if (lastValid == -1)
|
||||
{
|
||||
return -1; // All siblings were removed
|
||||
}
|
||||
|
||||
// Close the loop
|
||||
nodes[lastValid].sibling = firstValid;
|
||||
nodes[lastValid].position = position;
|
||||
|
||||
return firstValid;
|
||||
}
|
||||
|
||||
public int CollapseEdge(int nodeIndexA, int nodeIndexB)
|
||||
{
|
||||
int posA = nodes[nodeIndexA].position;
|
||||
int posB = nodes[nodeIndexB].position;
|
||||
|
||||
Debug.Assert(posA != posB, "A and B must have different positions");
|
||||
Debug.Assert(!nodes[nodeIndexA].IsRemoved);
|
||||
Debug.Assert(!nodes[nodeIndexB].IsRemoved);
|
||||
|
||||
Debug.Assert(CheckRelatives(nodeIndexA), "A's relatives must be valid");
|
||||
Debug.Assert(CheckRelatives(nodeIndexB), "B's relatives must be valid");
|
||||
Debug.Assert(CheckSiblings(nodeIndexA), "A's siblings must be valid");
|
||||
Debug.Assert(CheckSiblings(nodeIndexB), "B's siblings must be valid");
|
||||
|
||||
int siblingOfA = nodeIndexA;
|
||||
do // Iterates over faces around A
|
||||
{
|
||||
bool isFaceTouched = false;
|
||||
int faceEdgeCount = 0;
|
||||
int nodeIndexC = -1;
|
||||
|
||||
int relativeOfA = siblingOfA;
|
||||
do // Circulate in face
|
||||
{
|
||||
int posC = nodes[relativeOfA].position;
|
||||
if (posC == posB)
|
||||
{
|
||||
isFaceTouched = true;
|
||||
}
|
||||
else if (posC != posA)
|
||||
{
|
||||
nodeIndexC = relativeOfA;
|
||||
}
|
||||
|
||||
faceEdgeCount++;
|
||||
} while ((relativeOfA = nodes[relativeOfA].relative) != siblingOfA);
|
||||
|
||||
if (faceEdgeCount != 3)
|
||||
throw new NotImplementedException();
|
||||
|
||||
if (isFaceTouched && faceEdgeCount == 3)
|
||||
{
|
||||
// Remove face : Mark nodes as removed an reconnect siblings around C
|
||||
|
||||
int posC = nodes[nodeIndexC].position;
|
||||
|
||||
relativeOfA = siblingOfA;
|
||||
do
|
||||
{
|
||||
nodes[relativeOfA].MarkRemoved();
|
||||
|
||||
} while ((relativeOfA = nodes[relativeOfA].relative) != siblingOfA);
|
||||
|
||||
int validNodeAtC = ReconnectSiblings(nodeIndexC);
|
||||
|
||||
if (_positionToNode != null)
|
||||
{
|
||||
_positionToNode[posC] = validNodeAtC;
|
||||
}
|
||||
|
||||
_faceCount--;
|
||||
}
|
||||
} while ((siblingOfA = nodes[siblingOfA].sibling) != nodeIndexA);
|
||||
|
||||
int validNodeAtA = ReconnectSiblings(nodeIndexA, nodeIndexB, posA);
|
||||
|
||||
if (_positionToNode != null)
|
||||
{
|
||||
_positionToNode[posA] = validNodeAtA;
|
||||
_positionToNode[posB] = -1;
|
||||
}
|
||||
|
||||
return validNodeAtA;
|
||||
}
|
||||
|
||||
public double GetEdgeTopo(int nodeIndexA, int nodeIndexB)
|
||||
{
|
||||
if ((uint)nodeIndexA >= (uint)nodes.Length || (uint)nodeIndexB >= (uint)nodes.Length)
|
||||
{
|
||||
return EdgeBorderPenalty;
|
||||
}
|
||||
|
||||
if (nodes[nodeIndexA].IsRemoved || nodes[nodeIndexB].IsRemoved)
|
||||
{
|
||||
return EdgeBorderPenalty;
|
||||
}
|
||||
|
||||
int posB = nodes[nodeIndexB].position;
|
||||
|
||||
int facesAttached = 0;
|
||||
|
||||
int attrAtA = -1;
|
||||
int attrAtB = -1;
|
||||
|
||||
double edgeWeight = 0;
|
||||
|
||||
int siblingOfA = nodeIndexA;
|
||||
do
|
||||
{
|
||||
int relativeOfA = siblingOfA;
|
||||
while ((relativeOfA = nodes[relativeOfA].relative) != siblingOfA)
|
||||
{
|
||||
int posC = nodes[relativeOfA].position;
|
||||
if (posC == posB)
|
||||
{
|
||||
facesAttached++;
|
||||
|
||||
if (attributes != null)
|
||||
{
|
||||
for (int i = 0; i < attributes.CountPerAttribute; i++)
|
||||
{
|
||||
if (attrAtB != -1 && !attributes.Equals(attrAtB, nodes[relativeOfA].attribute, i))
|
||||
{
|
||||
edgeWeight += attributeDefinitions[i].weight;
|
||||
}
|
||||
|
||||
if (attrAtA != -1 && !attributes.Equals(attrAtA, nodes[siblingOfA].attribute, i))
|
||||
{
|
||||
edgeWeight += attributeDefinitions[i].weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attrAtB = nodes[relativeOfA].attribute;
|
||||
attrAtA = nodes[siblingOfA].attribute;
|
||||
}
|
||||
}
|
||||
} while ((siblingOfA = nodes[siblingOfA].sibling) != nodeIndexA);
|
||||
|
||||
if (facesAttached != 2) // Border or non-manifold edge
|
||||
{
|
||||
edgeWeight += EdgeBorderPenalty;
|
||||
}
|
||||
|
||||
return edgeWeight;
|
||||
}
|
||||
|
||||
internal static double EdgeBorderPenalty = 1027.007;
|
||||
|
||||
// TODO : Make it work with any polygon (other than triangle)
|
||||
public Vector3 GetFaceNormal(int nodeIndex)
|
||||
{
|
||||
int posA = nodes[nodeIndex].position;
|
||||
int posB = nodes[nodes[nodeIndex].relative].position;
|
||||
int posC = nodes[nodes[nodes[nodeIndex].relative].relative].position;
|
||||
|
||||
Vector3 normal = Vector3.Cross(
|
||||
positions[posB] - positions[posA],
|
||||
positions[posC] - positions[posA]);
|
||||
|
||||
return normal.Normalized;
|
||||
}
|
||||
|
||||
// TODO : Make it work with any polygon (other than triangle)
|
||||
public double GetFaceArea(int nodeIndex)
|
||||
{
|
||||
int posA = nodes[nodeIndex].position;
|
||||
int posB = nodes[nodes[nodeIndex].relative].position;
|
||||
int posC = nodes[nodes[nodes[nodeIndex].relative].relative].position;
|
||||
|
||||
Vector3 normal = Vector3.Cross(
|
||||
positions[posB] - positions[posA],
|
||||
positions[posC] - positions[posA]);
|
||||
|
||||
return 0.5 * normal.Length;
|
||||
}
|
||||
|
||||
// Only works with triangles !
|
||||
public double GetAngleRadians(int nodeIndex)
|
||||
{
|
||||
int posA = nodes[nodeIndex].position;
|
||||
int posB = nodes[nodes[nodeIndex].relative].position;
|
||||
int posC = nodes[nodes[nodes[nodeIndex].relative].relative].position;
|
||||
|
||||
return Vector3.AngleRadians(
|
||||
positions[posB] - positions[posA],
|
||||
positions[posC] - positions[posA]);
|
||||
}
|
||||
|
||||
public void Compact()
|
||||
{
|
||||
// Rebuild nodes array with only valid nodes
|
||||
{
|
||||
int validNodesCount = 0;
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
if (!nodes[i].IsRemoved)
|
||||
validNodesCount++;
|
||||
|
||||
Node[] newNodes = new Node[validNodesCount];
|
||||
int k = 0;
|
||||
Dictionary<int, int> oldToNewNodeIndex = new Dictionary<int, int>();
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
if (!nodes[i].IsRemoved)
|
||||
{
|
||||
newNodes[k] = nodes[i];
|
||||
oldToNewNodeIndex.Add(i, k);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < newNodes.Length; i++)
|
||||
{
|
||||
newNodes[i].relative = oldToNewNodeIndex[newNodes[i].relative];
|
||||
newNodes[i].sibling = oldToNewNodeIndex[newNodes[i].sibling];
|
||||
}
|
||||
nodes = newNodes;
|
||||
}
|
||||
|
||||
// Remap positions
|
||||
{
|
||||
Dictionary<int, int> oldToNewPosIndex = new Dictionary<int, int>();
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
if (!oldToNewPosIndex.ContainsKey(nodes[i].position))
|
||||
oldToNewPosIndex.Add(nodes[i].position, oldToNewPosIndex.Count);
|
||||
|
||||
nodes[i].position = oldToNewPosIndex[nodes[i].position];
|
||||
}
|
||||
Vector3[] newPositions = new Vector3[oldToNewPosIndex.Count];
|
||||
foreach (KeyValuePair<int, int> oldToNewPos in oldToNewPosIndex)
|
||||
{
|
||||
newPositions[oldToNewPos.Value] = positions[oldToNewPos.Key];
|
||||
}
|
||||
positions = newPositions;
|
||||
}
|
||||
|
||||
// Remap attributes
|
||||
if (attributes != null)
|
||||
{
|
||||
Dictionary<int, int> oldToNewAttrIndex = new Dictionary<int, int>();
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
if (!oldToNewAttrIndex.ContainsKey(nodes[i].attribute))
|
||||
oldToNewAttrIndex.Add(nodes[i].attribute, oldToNewAttrIndex.Count);
|
||||
|
||||
nodes[i].attribute = oldToNewAttrIndex[nodes[i].attribute];
|
||||
}
|
||||
MetaAttributeList newAttributes = attributes.CreateNew(oldToNewAttrIndex.Count);
|
||||
foreach (KeyValuePair<int, int> oldToNewAttr in oldToNewAttrIndex)
|
||||
{
|
||||
newAttributes[oldToNewAttr.Value] = attributes[oldToNewAttr.Key];
|
||||
}
|
||||
attributes = newAttributes;
|
||||
}
|
||||
|
||||
_positionToNode = null; // Invalid now
|
||||
}
|
||||
|
||||
public void MergePositions(double tolerance = 0.01)
|
||||
{
|
||||
Dictionary<Vector3, int> newPositions = new Dictionary<Vector3, int>(tolerance <= 0 ? null : new Vector3Comparer(tolerance));
|
||||
|
||||
for (int i = 0; i < positions.Length; i++)
|
||||
{
|
||||
newPositions.TryAdd(positions[i], newPositions.Count);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
nodes[i].position = newPositions[positions[nodes[i].position]];
|
||||
}
|
||||
|
||||
positions = new Vector3[newPositions.Count];
|
||||
foreach (KeyValuePair<Vector3, int> pair in newPositions)
|
||||
{
|
||||
positions[pair.Value] = pair.Key;
|
||||
}
|
||||
|
||||
newPositions = null;
|
||||
|
||||
// Remapping siblings
|
||||
Dictionary<int, int> posToLastSibling = new Dictionary<int, int>();
|
||||
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
if (posToLastSibling.ContainsKey(nodes[i].position))
|
||||
{
|
||||
nodes[i].sibling = posToLastSibling[nodes[i].position];
|
||||
posToLastSibling[nodes[i].position] = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
nodes[i].sibling = -1;
|
||||
posToLastSibling.Add(nodes[i].position, i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
if (nodes[i].sibling < 0)
|
||||
{
|
||||
// Assign last sibling to close sibling loop
|
||||
nodes[i].sibling = posToLastSibling[nodes[i].position];
|
||||
}
|
||||
}
|
||||
|
||||
_positionToNode = null;
|
||||
|
||||
// Dereference faces that no longer exist
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
if (nodes[i].IsRemoved)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int lastPos = nodes[i].position;
|
||||
int relative = i;
|
||||
while ((relative = nodes[relative].relative) != i) // Circulate around face
|
||||
{
|
||||
int currPos = nodes[relative].position;
|
||||
if (lastPos == currPos)
|
||||
{
|
||||
RemoveFace(relative);
|
||||
break;
|
||||
}
|
||||
lastPos = currPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MergeAttributes()
|
||||
{
|
||||
Dictionary<IMetaAttribute, int> _uniqueAttributes = new Dictionary<IMetaAttribute, int>();
|
||||
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
_uniqueAttributes.TryAdd(attributes[nodes[i].attribute], nodes[i].attribute);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
nodes[i].attribute = _uniqueAttributes[attributes[nodes[i].attribute]];
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveFace(int nodeIndex)
|
||||
{
|
||||
int relative = nodeIndex;
|
||||
do
|
||||
{
|
||||
nodes[relative].MarkRemoved();
|
||||
ReconnectSiblings(relative);
|
||||
} while ((relative = nodes[relative].relative) != nodeIndex);
|
||||
_faceCount--;
|
||||
}
|
||||
|
||||
public void Scale(double factor)
|
||||
{
|
||||
for (int i = 0; i < positions.Length; i++)
|
||||
{
|
||||
positions[i] = positions[i] * factor;
|
||||
}
|
||||
}
|
||||
|
||||
public HashSet<Edge> GetAllEdges()
|
||||
{
|
||||
HashSet<Edge> edges = new HashSet<Edge>();
|
||||
for (int p = 0; p < PositionToNode.Length; p++)
|
||||
{
|
||||
int nodeIndex = PositionToNode[p];
|
||||
if (nodeIndex < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int sibling = nodeIndex;
|
||||
do
|
||||
{
|
||||
int firstRelative = nodes[sibling].relative;
|
||||
int secondRelative = nodes[firstRelative].relative;
|
||||
|
||||
Edge pair = new Edge(nodes[firstRelative].position, nodes[secondRelative].position);
|
||||
|
||||
edges.Add(pair);
|
||||
|
||||
} while ((sibling = nodes[sibling].sibling) != nodeIndex);
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
public SharedMesh ToSharedMesh()
|
||||
{
|
||||
// Compating here is an issue if mesh is being decimated :/
|
||||
//Compact();
|
||||
|
||||
SharedMesh mesh = new SharedMesh();
|
||||
|
||||
List<int> triangles = new List<int>();
|
||||
HashSet<int> browsedNodes = new HashSet<int>();
|
||||
|
||||
Group[] newGroups = new Group[groups?.Length ?? 0];
|
||||
mesh.groups = newGroups;
|
||||
mesh.attributeDefinitions = attributeDefinitions;
|
||||
|
||||
int currentGroup = 0;
|
||||
int indicesInGroup = 0;
|
||||
|
||||
Dictionary<(int, int), int> perVertexMap = new Dictionary<(int, int), int>();
|
||||
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
if (newGroups.Length > 0 && groups[currentGroup].firstIndex == i)
|
||||
{
|
||||
if (currentGroup > 0)
|
||||
{
|
||||
newGroups[currentGroup - 1].indexCount = indicesInGroup;
|
||||
newGroups[currentGroup].firstIndex = indicesInGroup + newGroups[currentGroup - 1].firstIndex;
|
||||
}
|
||||
indicesInGroup = 0;
|
||||
if (currentGroup < groups.Length - 1)
|
||||
{
|
||||
currentGroup++;
|
||||
}
|
||||
}
|
||||
|
||||
if (nodes[i].IsRemoved)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
indicesInGroup++;
|
||||
|
||||
if (browsedNodes.Contains(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only works if all elements are triangles
|
||||
int relative = i;
|
||||
do
|
||||
{
|
||||
if (browsedNodes.Add(relative) && !nodes[relative].IsRemoved)
|
||||
{
|
||||
(int position, int attribute) key = (nodes[relative].position, nodes[relative].attribute);
|
||||
perVertexMap.TryAdd(key, perVertexMap.Count);
|
||||
triangles.Add(perVertexMap[key]);
|
||||
}
|
||||
} while ((relative = nodes[relative].relative) != i);
|
||||
}
|
||||
|
||||
if (newGroups.Length > 0)
|
||||
{
|
||||
newGroups[currentGroup].indexCount = indicesInGroup;
|
||||
}
|
||||
|
||||
// Positions
|
||||
mesh.positions = new Vector3[perVertexMap.Count];
|
||||
foreach (KeyValuePair<(int, int), int> mapping in perVertexMap)
|
||||
{
|
||||
mesh.positions[mapping.Value] = positions[mapping.Key.Item1];
|
||||
}
|
||||
|
||||
// Attributes
|
||||
if (attributes != null && attributeDefinitions.Length > 0)
|
||||
{
|
||||
mesh.attributes = attributes.CreateNew(perVertexMap.Count);
|
||||
foreach (KeyValuePair<(int, int), int> mapping in perVertexMap)
|
||||
{
|
||||
mesh.attributes[mapping.Value] = attributes[mapping.Key.Item2];
|
||||
}
|
||||
}
|
||||
|
||||
mesh.triangles = triangles.ToArray();
|
||||
|
||||
return mesh;
|
||||
}
|
||||
}
|
||||
|
||||
public struct Edge : IEquatable<Edge>
|
||||
{
|
||||
public int posA;
|
||||
public int posB;
|
||||
|
||||
public Edge(int posA, int posB)
|
||||
{
|
||||
this.posA = posA;
|
||||
this.posB = posB;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return posA + posB;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals((Edge)obj);
|
||||
}
|
||||
|
||||
public bool Equals(Edge pc)
|
||||
{
|
||||
if (ReferenceEquals(this, pc))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (posA == pc.posA && posB == pc.posB) || (posA == pc.posB && posB == pc.posA);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(Edge x, Edge y)
|
||||
{
|
||||
return x.Equals(y);
|
||||
}
|
||||
|
||||
public static bool operator !=(Edge x, Edge y)
|
||||
{
|
||||
return !x.Equals(y);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"<A:{posA} B:{posB}>";
|
||||
}
|
||||
}
|
||||
}
|
||||
138
LightlessSync/ThirdParty/Nanomesh/Mesh/ConnectedMesh/Debug.cs
vendored
Normal file
138
LightlessSync/ThirdParty/Nanomesh/Mesh/ConnectedMesh/Debug.cs
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public partial class ConnectedMesh
|
||||
{
|
||||
internal string PrintSiblings(int nodeIndex)
|
||||
{
|
||||
int sibling = nodeIndex;
|
||||
string text = string.Join(" > ", Enumerable.Range(0, 12).Select(x =>
|
||||
{
|
||||
string res = sibling.ToString() + (nodes[sibling].IsRemoved ? "(x)" : $"({nodes[sibling].position})");
|
||||
sibling = nodes[sibling].sibling;
|
||||
return res;
|
||||
}));
|
||||
return text + "...";
|
||||
}
|
||||
|
||||
internal string PrintRelatives(int nodeIndex)
|
||||
{
|
||||
int relative = nodeIndex;
|
||||
string text = string.Join(" > ", Enumerable.Range(0, 12).Select(x =>
|
||||
{
|
||||
string res = relative.ToString() + (nodes[relative].IsRemoved ? "(x)" : $"({nodes[relative].position})");
|
||||
relative = nodes[relative].relative;
|
||||
return res;
|
||||
}));
|
||||
return text + "...";
|
||||
}
|
||||
|
||||
internal bool CheckEdge(int nodeIndexA, int nodeIndexB)
|
||||
{
|
||||
if (nodes[nodeIndexA].position == nodes[nodeIndexB].position)
|
||||
{
|
||||
throw new Exception("Positions must be different");
|
||||
}
|
||||
|
||||
if (nodes[nodeIndexA].IsRemoved)
|
||||
{
|
||||
throw new Exception($"Node A is unreferenced {nodeIndexA}");
|
||||
}
|
||||
|
||||
if (nodes[nodeIndexB].IsRemoved)
|
||||
{
|
||||
throw new Exception($"Node B is unreferenced {nodeIndexB}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool CheckRelatives(int nodeIndex)
|
||||
{
|
||||
if (nodes[nodeIndex].IsRemoved)
|
||||
{
|
||||
throw new Exception($"Node {nodeIndex} is removed");
|
||||
}
|
||||
|
||||
int relative = nodeIndex;
|
||||
int edgecount = 0;
|
||||
int prevPos = -2;
|
||||
do
|
||||
{
|
||||
if (nodes[relative].position == prevPos)
|
||||
{
|
||||
throw new Exception($"Two relatives or more share the same position : {PrintRelatives(nodeIndex)}");
|
||||
}
|
||||
|
||||
if (edgecount > 50)
|
||||
{
|
||||
throw new Exception($"Circularity relative violation : {PrintRelatives(nodeIndex)}");
|
||||
}
|
||||
|
||||
if (nodes[relative].IsRemoved)
|
||||
{
|
||||
throw new Exception($"Node {nodeIndex} is connected to the deleted relative {relative}");
|
||||
}
|
||||
|
||||
prevPos = nodes[relative].position;
|
||||
edgecount++;
|
||||
|
||||
} while ((relative = nodes[relative].relative) != nodeIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool CheckSiblings(int nodeIndex)
|
||||
{
|
||||
if (nodes[nodeIndex].IsRemoved)
|
||||
{
|
||||
throw new Exception($"Node {nodeIndex} is removed");
|
||||
}
|
||||
|
||||
int sibling = nodeIndex;
|
||||
int cardinality = 0;
|
||||
do
|
||||
{
|
||||
if (cardinality > 1000)
|
||||
{
|
||||
//throw new Exception($"Node {i}'s cardinality is superior to 50. It is likely to be that face siblings are not circularily linked");
|
||||
throw new Exception($"Circularity sibling violation : {PrintSiblings(nodeIndex)}");
|
||||
}
|
||||
|
||||
if (nodes[sibling].IsRemoved)
|
||||
{
|
||||
throw new Exception($"Node {nodeIndex} has a deleted sibling {sibling}");
|
||||
}
|
||||
|
||||
cardinality++;
|
||||
|
||||
} while ((sibling = nodes[sibling].sibling) != nodeIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool Check()
|
||||
{
|
||||
for (int nodeIndex = 0; nodeIndex < nodes.Length; nodeIndex++)
|
||||
{
|
||||
if (nodes[nodeIndex].IsRemoved)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CheckRelatives(nodeIndex);
|
||||
|
||||
CheckSiblings(nodeIndex);
|
||||
|
||||
if (GetEdgeCount(nodeIndex) == 2)
|
||||
{
|
||||
throw new Exception($"Node {nodeIndex} is part of a polygon of degree 2");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
LightlessSync/ThirdParty/Nanomesh/Mesh/ConnectedMesh/Node.cs
vendored
Normal file
25
LightlessSync/ThirdParty/Nanomesh/Mesh/ConnectedMesh/Node.cs
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace Nanomesh
|
||||
{
|
||||
public partial class ConnectedMesh
|
||||
{
|
||||
public struct Node
|
||||
{
|
||||
public int position;
|
||||
public int sibling;
|
||||
public int relative;
|
||||
public int attribute;
|
||||
|
||||
public void MarkRemoved()
|
||||
{
|
||||
position = -10;
|
||||
}
|
||||
|
||||
public bool IsRemoved => position == -10;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"sibl:{sibling} rela:{relative} posi:{position}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user