/// \brief 判断点是否在三角形内
/// \param Point 目标点
/// \param A/B/C 组成三角形的三个顶点
/// \return 判别结果
static bool IsPointInTriangle(const FVector& Point, const FVector& A, const FVector& B, const FVector& C)
{
// 使用叉积检查点是否在三角形内部
FVector AB = B - A;
FVector BC = C - B;
FVector CA = A - C;
FVector AP = Point - A;
FVector BP = Point - B;
FVector CP = Point - C;
FVector Cross1 = FVector::CrossProduct(AB, AP);
FVector Cross2 = FVector::CrossProduct(BC, BP);
FVector Cross3 = FVector::CrossProduct(CA, CP);
// 如果点在三角形内,那么所有的叉积应该指向相同的方向
return (Cross1.Z > 0 && Cross2.Z > 0 && Cross3.Z > 0) || (Cross1.Z < 0 && Cross2.Z < 0 && Cross3.Z < 0);
}
/// \brief 判别三角形是否是耳朵
/// \param Vertices 逆时针顺序的顶点数组
/// \param i0/i1/i2 三个点的索引
/// \return 判别结果
static bool IsEar(const TArray<FVector>& Vertices, int32 i0, int32 i1, int32 i2)
{
const FVector& v0 = Vertices[i0];
const FVector& v1 = Vertices[i1];
const FVector& v2 = Vertices[i2];
// Step 1: 检查三角形是否为凸形(目前假设顶点逆时针顺序)
// 使用叉积检查方向,如果结果为正,则该三角形为凸形
FVector Edge1 = v1 - v0;
FVector Edge2 = v2 - v1;
if (FVector::CrossProduct(Edge1, Edge2).Z <= 0)
{
return false; // 凹形或共线,不是耳朵
}
// Step 2: 检查三角形内部是否包含多边形的其他顶点
for (int32 i = 0; i < Vertices.Num(); i++)
{
if (i == i0 || i == i1 || i == i2)
continue;
if (IsPointInTriangle(Vertices[i], v0, v1, v2))
{
return false; // 该三角形包含其他顶点,不是耳朵
}
}
return true; // 满足条件,返回 true,表示这是一个耳朵
}
/// \brief 三角剖分,根据EarCutting算法,对传入的顶点数组进行组网
/// \param Vertices 简单多边形的顶点组
/// \param OutIndeces 三角网的索引数组输出
/// \return 算法执行成功与否(false可能是传入多边形不属于简单多边形,或者数量不够)
static bool TriangulatePolygon(const TArray<FVector>& Vertices, TArray<int32>& OutIndices)
{
if (Vertices.Num() < 3)
return false;
// 复制顶点列表用于操作
TArray<int32> VerticesIndices;
for (int32 i = 0; i < Vertices.Num(); i++)
{
VerticesIndices.Add(i);
}
// 耳切法:找到每个顶点形成的耳朵,并逐步移除
while (VerticesIndices.Num() >= 3)
{
bool bFoundEar = false;
for (int32 i = 0; i < VerticesIndices.Num(); i++)
{
int32 i0 = VerticesIndices[i];
int32 i1 = VerticesIndices[(i + 1) % VerticesIndices.Num()];
int32 i2 = VerticesIndices[(i + 2) % VerticesIndices.Num()];
FVector v0 = Vertices[i0];
FVector v1 = Vertices[i1];
FVector v2 = Vertices[i2];
if (IsEar(Vertices, i0, i1, i2))
{
// 添加三角形
OutIndices.Add(i0);
OutIndices.Add(i1);
OutIndices.Add(i2);
// 移除耳朵(中间那个凸出去的单个点)
VerticesIndices.RemoveAt((i + 1) % VerticesIndices.Num());
bFoundEar = true;
break;
}
}
// 如果未找到耳朵,可能是非简单多边形或者别的原因
if (!bFoundEar)
{
return false;
}
}
return true;
}
耳切法三角网构造代码
于 2024-08-19 18:33:33 首次发布