问题描述
我有一个 3D 网格,我想要做的是绘制顶部 2D 面的轮廓,所以在这里我可以访问顶部网格顶点和三角形索引,我想要的是对顶点进行排序,以便我可以画出我的轮廓。
List<Vector3> GetSortedVerticesTest(Dictionary<CreateMesh.Faces,MeshData> mesh,Mesh mainMesh)
{
List<Vector3> borderVertices = mesh[CreateMesh.Faces.Top].vertices.Where(p => mesh[CreateMesh.Faces.other].vertices.Any(e => e == p)).ToList();
borderVertices = borderVertices.distinct().ToList();
List<Vector3> sortedVertices = new List<Vector3>();
List<PointAngle> candidateAngles = new List<PointAngle>();
Vector3 startingPoint = borderVertices[0];
sortedVertices.Add(startingPoint);
borderVertices.Remove(startingPoint);
while (borderVertices.Count > 0)
{
List<Vector3> candidates = new List<Vector3>();
for (int i = 0; i < mesh[CreateMesh.Faces.Top].triangles.Count(); i += 3)
{
bool commonTriangle = false;
// verifie que le vertice courant est compris dans le triangle
for (int k = 0; k < 3; k++)
{
int triIndex = mesh[CreateMesh.Faces.Top].triangles[i + k];
Vector3 posToCompare = mainMesh.vertices[triIndex];
if (startingPoint == posToCompare)
{
commonTriangle = true;
}
}
if (commonTriangle)
{
for (int k = 0; k < 3; k++)
{
int triIndex = mesh[CreateMesh.Faces.Top].triangles[i + k];
Vector3 posToCompare = mainMesh.vertices[triIndex];
if (borderVertices.Contains(posToCompare))//epsilon
candidates.Add(posToCompare);
}
candidates.Remove(startingPoint);
if (candidates.Count != 0)
{
candidates.ForEach(c => candidateAngles.Add(new PointAngle { angle = Vector3.SignedAngle(c - startingPoint,transform.forward,Vector3.up),point = c }));
double min = candidateAngles.Min(kvp => kvp.angle);
var a = candidateAngles[0].point;
PointAngle pointsToKeep = new PointAngle();
pointsToKeep = candidateAngles.Where(s => s.angle == min).First();
sortedVertices.Add(pointsToKeep.point);
borderVertices.Remove(pointsToKeep.point);
print("vert coun" + borderVertices.Count);
startingPoint = pointsToKeep.point;
candidateAngles.Clear();
}
}
}
}
return sortedVertices;
}
解决方法
我没有方便地访问 unity3D,以下示例将使用 MeshGeometry3D。但我希望这些课程足够相似,以便于翻译。
这应该为通过一些任意标准的三角形生成边界边缘列表。我假设这是您要实现的实际目标。边被表示为网格顶点列表的一对索引。
应该可以简单地绘制每条边。还应该可以通过一些额外的处理将边缘连接到线带。
public static IEnumerable<(int e1,int e2)> GetBorderEdges(MeshGeometry3D mesh,Func<Vector3D,bool > triangleNormalFilter )
{
var numTriangles = mesh.TriangleIndices.Count / 3;
// use a value tuple to represent edges,// value tuples have a default equal/getHashCode,so can be used in HashSet
// Since (1,2) and (2,1) are considered the same edge,// all edges should be ordered smallest index first.
var allEdges = new HashSet<(int e1,int e2)>();
var nonBorderEdges = new HashSet<(int e1,int e2)>();
for (int i = 0; i < numTriangles; i++)
{
var normal = mesh.Normals[i];
// Only process triangles that pass some test,// like normals pointing up (assuming normals exist)
if (triangleNormalFilter(normal))
{
// Multiply by three to get the index of the first value in the TriangleIndices list
var triangleIndex = i * 3;
foreach (var edge in GetEdges(mesh,triangleIndex))
{
// If the edge is already in allEdges,Add will return false
if (!allEdges.Add(edge))
{
// Since the edge must be part of two triangles,it is a non border edge
nonBorderEdges.Add(edge);
}
}
}
}
// Remove non border edges and return the result
allEdges.ExceptWith(nonBorderEdges);
return allEdges;
}
// return all edges for a triangle
private static (int e1,int e2)[] GetEdges(MeshGeometry3D mesh,int triangleIndex)
{
var t = triangleIndex;
var ti = mesh.TriangleIndices;
return new[]
{
CreateEdge(ti[triangleIndex],ti[t + 1]),CreateEdge(ti[triangleIndex + 1],ti[t + 2]),CreateEdge(ti[triangleIndex + 2],ti[t]),};
}
// ensures edges are always created smaller index first
private static (int e1,int e2) CreateEdge(int e1,int e2)
{
if (e2 < e1)
{
return (e2,e1);
}
return (e1,e2);
}