Hello,
There is a crash when we do slice procedural mesh component too many time, see :
So i’m recreating a function that do the slice, so the assertion crash does not happens. However, when i’m recreating the the face where the plane has been sliced, i always have 1 or 2 missing triangle, at least visually.
Missing triangle example
the code
static void SliceSectionsByPlane(
TArray<FProcMeshVertex>& Vertices,
const FVector& PlaneLocation,
const FVector& PlaneNormal,
const TArray<uint32>& Triangles,
TArray<uint32>& MainHalf,
TArray<uint32>& OtherHalf
)
{
TSet<uint32> UniqueCapVertices;
/**
* Each triangle are stored in an array of uint32,
* Each uint32 is an index withing the Vertices array :
* [Triangles] = { index vertex 1, index vertex 2, index vertex 3, index vertex 1, ... }
* So a triangle is defined by 3 consecutive elements
*
* that's why 'i += 3'
*/
for (int32 i = 0; i + 2 < Triangles.Num(); i += 3)
{
uint32 p0 = Triangles[i];
uint32 p1 = Triangles[i + 1];
uint32 p2 = Triangles[i + 2];
if (!Vertices.IsValidIndex(p0) || !Vertices.IsValidIndex(p1) || !Vertices.IsValidIndex(p2))
continue;
const FProcMeshVertex& V0 = Vertices[p0];
const FProcMeshVertex& V1 = Vertices[p1];
const FProcMeshVertex& V2 = Vertices[p2];
// Check if each vertex is in the part that needs to be kept
bool v0 = IsPointValid(V0, PlaneNormal, PlaneLocation);
bool v1 = IsPointValid(V1, PlaneNormal, PlaneLocation);
bool v2 = IsPointValid(V2, PlaneNormal, PlaneLocation);
// triangle needs to be entirely kept
if (v0 && v1 && v2) {
MainHalf.Append({ p0, p1, p2 });
continue;
}
// the triangle needs to be totally erased
if (!v0 && !v1 && !v2) {
OtherHalf.Append({ p0, p1, p2 });
continue;
}
/**
* @brief Computes the intersection vertex between an edge and the slicing plane.
*
* Given two vertex indices defining an edge, this function computes the
* intersection point with the slicing plane, interpolates the vertex attributes
* (UVs and normals), and appends the resulting vertex to the mesh.
*
* A second copy of the vertex is also created with its normal aligned to the
* plane normal. This duplicate is stored in the cap vertex list and is later
* used to generate the closing cap geometry.
*
* @param A Index of the first vertex of the edge.
* @param B Index of the second vertex of the edge.
*
* @return The index of the newly created intersection vertex in the mesh vertex array.
*
* @note The cap vertex created internally is added to UniqueCapVertices but its
* index is not returned.
*/
auto Intersect = [&](uint32 A, uint32 B) {
const FProcMeshVertex& VA = Vertices[A];
const FProcMeshVertex& VB = Vertices[B];
FVector P = ReplacePointOnPlaneLocal(VA.Position, VB.Position, PlaneLocation, PlaneNormal);
float EdgeLength = (VB.Position - VA.Position).Size();
float Alpha = 0.f;
if (EdgeLength > KINDA_SMALL_NUMBER)
Alpha = (P - VA.Position).Size() / EdgeLength;
FProcMeshVertex VNew;
VNew.Position = P;
VNew.UV0 = FMath::Lerp(VA.UV0, VB.UV0, Alpha);
VNew.Normal = FMath::Lerp(VA.Normal, VB.Normal, Alpha).GetSafeNormal();
uint32 MeshIndex = Vertices.Add(VNew);
FProcMeshVertex CapVertex = VNew;
CapVertex.Normal = PlaneNormal;
uint32 CapIndex = Vertices.Add(CapVertex);
UniqueCapVertices.Add(CapIndex);
return MeshIndex;
};
// configuration : 1 vertex is in the good side
if (v0 && !v1 && !v2) {
uint32 i01 = Intersect(p0, p1);
uint32 i02 = Intersect(p0, p2);
MainHalf.Append({ p0, i01, i02 });
OtherHalf.Append({ p1, p2, i02 });
OtherHalf.Append({ p1, i02, i01 });
}
else if (!v0 && v1 && !v2) {
uint32 i10 = Intersect(p1, p0);
uint32 i12 = Intersect(p1, p2);
MainHalf.Append({ p1, i12, i10 });
OtherHalf.Append({ p0, i10, p2 });
OtherHalf.Append({ p2, i10, i12 });
}
else if (!v0 && !v1 && v2) {
uint32 i20 = Intersect(p2, p0);
uint32 i21 = Intersect(p2, p1);
MainHalf.Append({ p2, i20, i21 });
OtherHalf.Append({ p0, p1, i21 });
OtherHalf.Append({ p0, i21, i20 });
}
// configuration inverse : 2 vertices are in the good side
else if (!v0 && v1 && v2) {
uint32 i01 = Intersect(p0, p1);
uint32 i02 = Intersect(p0, p2);
OtherHalf.Append({ p0, i01, i02 });
MainHalf.Append({ p1, p2, i02 });
MainHalf.Append({ p1, i02, i01 });
}
else if (v0 && !v1 && v2) {
uint32 i10 = Intersect(p1, p0);
uint32 i12 = Intersect(p1, p2);
OtherHalf.Append({ p1, i12, i10 });
MainHalf.Append({ p0, i10, p2 });
MainHalf.Append({ p2, i10, i12 });
}
else if (v0 && v1 && !v2)
{
uint32 i20 = Intersect(p2, p0);
uint32 i21 = Intersect(p2, p1);
OtherHalf.Append({ p2, i20, i21 });
MainHalf.Append({ p0, p1, i21 });
MainHalf.Append({ p0, i21, i20 });
}
}
// This recreate the face where the plane sliced the mesh
if (UniqueCapVertices.Num() >= 3) {
TArray<uint32> CapVertices = UniqueCapVertices.Array();
// Compute center location
FVector CenterPos = FVector::ZeroVector;
for (uint32 Index : CapVertices) {
CenterPos += Vertices[Index].Position;
}
CenterPos /= CapVertices.Num();
// Creating the center vertex
FProcMeshVertex CenterVertex;
CenterVertex.Position = CenterPos;
CenterVertex.Normal = PlaneNormal;
CenterVertex.UV0 = FVector2D(0.5f, 0.5f);
uint32 Center = Vertices.Add(CenterVertex);
for (int32 i = 1; i < CapVertices.Num(); ++i) {
uint32 A = CapVertices[i % CapVertices.Num()];
uint32 B = CapVertices[(i - 1) % CapVertices.Num()];
MainHalf.Append({ Center, B, A });
OtherHalf.Append({ Center, A, B });
}
}
}
I tried sorting the cap vertices angularly around the center, but it made the face disappear entirely.
