效果参考如下:
代码参考如下:
using System.Collections.Generic;
using UnityEngine;
public class BorderFind : MonoBehaviour
{
[SerializeField]
MeshFilter meshFilter;
[SerializeField]
Material matLine;
void Start()
{
if (meshFilter == null) return;
Mesh mesh = OptimizeMesh(meshFilter.sharedMesh, true);
if (mesh == null) return;
List<List<Vector3>> borders = GetBorders(mesh);
foreach (List<Vector3> b in borders)
{
GameObject go = new GameObject();
go.transform.parent = transform;
LineRenderer lineRenderer = go.AddComponent<LineRenderer>();
lineRenderer.positionCount = b.Count;
lineRenderer.SetPositions(b.ToArray());
lineRenderer.loop = true;
lineRenderer.material = Instantiate(matLine);
lineRenderer.material.color = Random.ColorHSV();
}
}
List<List<Vector3>> GetBorders(Mesh meshOriginal)
{
List<Edge> listEdgeNoSharedAll = new();
Mesh mesh = OptimizeMesh(meshOriginal, true);
Vector3[] vs = mesh.vertices;
int[] ts = mesh.triangles;
for (int i = 0; i < ts.Length; i += 3)
{
int edge01Start = ts[i];
int edge01End = ts[i + 1];
int edge02Start = ts[i + 1];
int edge02End = ts[i + 2];
int edge03Start = ts[i + 2];
int edge03End = ts[i];
bool isEdge01Shared = false;
bool isEdge02Shared = false;
bool isEdge03Shared = false;
for (int j = 0; j < ts.Length; j +=3)
{
if (j == i) continue;
int e01Start = ts[j];
int e01End = ts[j + 1];
int e02Start = ts[j + 1];
int e02End = ts[j + 2];
int e03Start = ts[j + 2];
int e03End = ts[j];
if ((e01Start == edge01Start && e01End == edge01End) || (e01End == edge01Start && e01Start == edge01End))
{
isEdge01Shared = true;
break;
}
if ((e02Start == edge01Start && e02End == edge01End) || (e02End == edge01Start && e02Start == edge01End))
{
isEdge01Shared = true;
break;
}
if ((e03Start == edge01Start && e03End == edge01End) || (e03End == edge01Start && e03Start == edge01End))
{
isEdge01Shared = true;
break;
}
}
for (int j = 0; j < ts.Length; j += 3)
{
if (j == i) continue;
int e01Start = ts[j];
int e01End = ts[j + 1];
int e02Start = ts[j + 1];
int e02End = ts[j + 2];
int e03Start = ts[j + 2];
int e03End = ts[j];
if ((e01Start == edge02Start && e01End == edge02End) || (e01End == edge02Start && e01Start == edge02End))
{
isEdge02Shared = true;
break;
}
if ((e02Start == edge02Start && e02End == edge02End) || (e02End == edge02Start && e02Start == edge02End))
{
isEdge02Shared = true;
break;
}
if ((e03Start == edge02Start && e03End == edge02End) || (e03End == edge02Start && e03Start == edge02End))
{
isEdge02Shared = true;
break;
}
}
for (int j = 0; j < ts.Length; j += 3)
{
if (j == i) continue;
int e01Start = ts[j];
int e01End = ts[j + 1];
int e02Start = ts[j + 1];
int e02End = ts[j + 2];
int e03Start = ts[j + 2];
int e03End = ts[j];
if ((e01Start == edge03Start && e01End == edge03End) || (e01End == edge03Start && e01Start == edge03End))
{
isEdge03Shared = true;
break;
}
if ((e02Start == edge03Start && e02End == edge03End) || (e02End == edge03Start && e02Start == edge03End))
{
isEdge03Shared = true;
break;
}
if ((e03Start == edge03Start && e03End == edge03End) || (e03End == edge03Start && e03Start == edge03End))
{
isEdge03Shared = true;
break;
}
}
if (!isEdge01Shared)
{
listEdgeNoSharedAll.Add(new Edge(edge01Start, edge01End));
}
if (!isEdge02Shared)
{
listEdgeNoSharedAll.Add(new Edge(edge02Start, edge02End));
}
if (!isEdge03Shared)
{
listEdgeNoSharedAll.Add(new Edge(edge03Start, edge03End));
}
}
List<List<Vector3>> listVerts = new();
Debug.Log(listEdgeNoSharedAll.Count);
if (listEdgeNoSharedAll.Count == 0) return listVerts;
do
{
List<Edge> listEdgeNoShared = new();
Edge edgeFirst = listEdgeNoSharedAll[0];
Edge curEdge = edgeFirst;
listEdgeNoSharedAll.RemoveAt(0);
listEdgeNoShared.Add(edgeFirst);
bool isClosed = false;
while (!isClosed || listEdgeNoSharedAll.Count > 0)
{
bool hasConnect = false;
foreach (Edge e in listEdgeNoSharedAll)
{
if (curEdge.end == e.start)
{
curEdge = e;
listEdgeNoSharedAll.Remove(e);
listEdgeNoShared.Add(e);
if (edgeFirst.start == e.end)
{
isClosed = true;
}
hasConnect = true;
break;
}
if (curEdge.end == e.end)
{
e.Switch();
curEdge = e;
listEdgeNoSharedAll.Remove(e);
listEdgeNoShared.Add(e);
if (edgeFirst.start == e.end)
{
isClosed = true;
}
hasConnect = true;
break;
}
}
if (!hasConnect) break;
}
List<Vector3> listVert = new List<Vector3>();
foreach (Edge e in listEdgeNoShared)
{
listVert.Add(vs[e.start]);
}
listVerts.Add(listVert);
} while (listEdgeNoSharedAll.Count > 0);
return listVerts;
}
Mesh OptimizeMesh(Mesh originalMesh, bool recalculate = false)
{
Vector3[] originalVertices = originalMesh.vertices;
int[] originalTriangles = originalMesh.triangles;
Dictionary<Vector3, int> uniqueVertices = new Dictionary<Vector3, int>();
List<Vector3> newVertices = new List<Vector3>();
List<int> newTriangles = new List<int>();
for (int i = 0; i < originalVertices.Length; i++)
{
Vector3 vertex = originalVertices[i];
if (!uniqueVertices.ContainsKey(vertex))
{
uniqueVertices[vertex] = newVertices.Count;
newVertices.Add(vertex);
}
}
for (int i = 0; i < originalTriangles.Length; i++)
{
int oldIndex = originalTriangles[i];
int newIndex = uniqueVertices[originalVertices[oldIndex]];
newTriangles.Add(newIndex);
}
Mesh optimizedMesh = new()
{
vertices = newVertices.ToArray(),
triangles = newTriangles.ToArray()
};
if (recalculate)
{
optimizedMesh.RecalculateNormals();
optimizedMesh.RecalculateBounds();
}
return optimizedMesh;
}
struct Edge
{
public int start;
public int end;
public Edge(int start, int end) { this.start = start; this.end = end; }
public void Switch() { (start, end) = (end, start); }
}
}