Files
UnrealEngine/Engine/Plugins/MeshPainting/Source/MeshPaintingToolset/Private/BaseMeshPaintComponentAdapter.cpp
2025-05-18 13:04:45 +08:00

200 lines
7.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BaseMeshPaintComponentAdapter.h"
#include "Distance/DistLine3Segment3.h"
#include "Distance/DistLine3Triangle3.h"
#include "Distance/DistSegment3Triangle3.h"
bool FBaseMeshPaintComponentAdapter::Initialize()
{
bool bInit = InitializeVertexData() && BuildOctree();
FIndexMeshArrayAdapterd TempAdapter(&MeshVertices, &MeshIndices);
Adapter = TempAdapter;
AABBTree = MakeUnique<UE::Geometry::TMeshAABBTree3<const FIndexMeshArrayAdapterd>>(&Adapter);
return bInit;
}
bool FBaseMeshPaintComponentAdapter::BuildOctree()
{
bool bValidOctree = false;
if (MeshVertices.Num() > 0 && MeshIndices.Num() > 0)
{
bValidOctree = true;
// First determine bounding box of mesh verts
FBox Bounds(ForceInit);
for (const FVector& Vertex : MeshVertices)
{
Bounds += Vertex;
}
MeshTriOctree = MakeUnique<FMeshPaintTriangleOctree>(Bounds.GetCenter(), Bounds.GetExtent().GetMax());
const uint32 NumIndexBufferIndices = MeshIndices.Num();
check(NumIndexBufferIndices % 3 == 0);
const uint32 NumTriangles = NumIndexBufferIndices / 3;
for (uint32 TriIndex = 0; TriIndex < NumTriangles; ++TriIndex)
{
// Grab the vertex indices and points for this triangle
FMeshPaintTriangle MeshTri;
MeshTri.Vertices[0] = MeshVertices[MeshIndices[TriIndex * 3 + 0]];
MeshTri.Vertices[1] = MeshVertices[MeshIndices[TriIndex * 3 + 1]];
MeshTri.Vertices[2] = MeshVertices[MeshIndices[TriIndex * 3 + 2]];
MeshTri.Normal = FVector::CrossProduct(MeshTri.Vertices[1] - MeshTri.Vertices[0], MeshTri.Vertices[2] - MeshTri.Vertices[0]).GetSafeNormal();
MeshTri.Index = TriIndex;
FBox TriBox;
TriBox.Min.X = FMath::Min3(MeshTri.Vertices[0].X, MeshTri.Vertices[1].X, MeshTri.Vertices[2].X);
TriBox.Min.Y = FMath::Min3(MeshTri.Vertices[0].Y, MeshTri.Vertices[1].Y, MeshTri.Vertices[2].Y);
TriBox.Min.Z = FMath::Min3(MeshTri.Vertices[0].Z, MeshTri.Vertices[1].Z, MeshTri.Vertices[2].Z);
TriBox.Max.X = FMath::Max3(MeshTri.Vertices[0].X, MeshTri.Vertices[1].X, MeshTri.Vertices[2].X);
TriBox.Max.Y = FMath::Max3(MeshTri.Vertices[0].Y, MeshTri.Vertices[1].Y, MeshTri.Vertices[2].Y);
TriBox.Max.Z = FMath::Max3(MeshTri.Vertices[0].Z, MeshTri.Vertices[1].Z, MeshTri.Vertices[2].Z);
MeshTri.BoxCenterAndExtent = FBoxCenterAndExtent(TriBox);
MeshTriOctree->AddElement(MeshTri);
}
}
return bValidOctree;
}
const TArray<FVector>& FBaseMeshPaintComponentAdapter::GetMeshVertices() const
{
return MeshVertices;
}
const TArray<uint32>& FBaseMeshPaintComponentAdapter::GetMeshIndices() const
{
return MeshIndices;
}
void FBaseMeshPaintComponentAdapter::GetVertexPosition(int32 VertexIndex, FVector& OutVertex) const
{
OutVertex = MeshVertices[VertexIndex];
}
TArray<uint32> FBaseMeshPaintComponentAdapter::SphereIntersectTriangles(const float ComponentSpaceSquaredBrushRadius, const FVector& ComponentSpaceBrushPosition, const FVector& ComponentSpaceCameraPosition, const bool bOnlyFrontFacing) const
{
TArray<uint32> OutTriangles;
// Use a bit of distance bias to make sure that we get all of the overlapping triangles. We
// definitely don't want our brush to be cut off by a hard triangle edge
const float SquaredRadiusBias = ComponentSpaceSquaredBrushRadius * 0.025f;
MeshTriOctree->FindElementsWithBoundsTest(FBoxCenterAndExtent(ComponentSpaceBrushPosition, FVector(FMath::Sqrt(ComponentSpaceSquaredBrushRadius + SquaredRadiusBias))), [&OutTriangles, bOnlyFrontFacing, &ComponentSpaceCameraPosition](const FMeshPaintTriangle& CurrentTri)
{
const float SignedPlaneDist = FVector::PointPlaneDist(ComponentSpaceCameraPosition, CurrentTri.Vertices[0], CurrentTri.Normal);
if (!bOnlyFrontFacing || SignedPlaneDist < 0.0f)
{
OutTriangles.Add(CurrentTri.Index);
}
});
return OutTriangles;
}
void FBaseMeshPaintComponentAdapter::GetInfluencedVertexIndices(const float ComponentSpaceSquaredBrushRadius, const FVector& ComponentSpaceBrushPosition, const FVector& ComponentSpaceCameraPosition, const bool bOnlyFrontFacing, TSet<int32> &InfluencedVertices) const
{
// Get a list of (optionally front-facing) triangles that are within a reasonable distance to the brush
const TArray<uint32> InfluencedTriangles = SphereIntersectTriangles(
ComponentSpaceSquaredBrushRadius,
ComponentSpaceBrushPosition,
ComponentSpaceCameraPosition,
bOnlyFrontFacing);
// Make sure we're dealing with triangle lists
const int32 NumIndexBufferIndices = MeshIndices.Num();
check(NumIndexBufferIndices % 3 == 0);
InfluencedVertices.Reserve(InfluencedTriangles.Num());
for (const int32 InfluencedTriangle : InfluencedTriangles)
{
for (int32 Index = 0; Index < 3; ++Index)
{
const FVector& VertexPosition = MeshVertices[MeshIndices[InfluencedTriangle * 3 + Index]];
if ((VertexPosition - ComponentSpaceBrushPosition).SizeSquared() <= ComponentSpaceSquaredBrushRadius)
{
InfluencedVertices.Add(MeshIndices[InfluencedTriangle * 3 + Index]);
}
}
}
}
void FBaseMeshPaintComponentAdapter::GetInfluencedVertexData(const float ComponentSpaceSquaredBrushRadius, const FVector& ComponentSpaceBrushPosition, const FVector& ComponentSpaceCameraPosition, const bool bOnlyFrontFacing, TArray<TPair<int32, FVector>>& OutData) const
{
// Get a list of (optionally front-facing) triangles that are within a reasonable distance to the brush
TArray<uint32> InfluencedTriangles = SphereIntersectTriangles(
ComponentSpaceSquaredBrushRadius,
ComponentSpaceBrushPosition,
ComponentSpaceCameraPosition,
bOnlyFrontFacing);
// Make sure we're dealing with triangle lists
const int32 NumIndexBufferIndices = MeshIndices.Num();
check(NumIndexBufferIndices % 3 == 0);
OutData.Reserve(InfluencedTriangles.Num() * 3);
for(int32 InfluencedTriangle : InfluencedTriangles)
{
for(int32 Index = 0; Index < 3; ++Index)
{
const FVector& VertexPosition = MeshVertices[MeshIndices[InfluencedTriangle * 3 + Index]];
if((VertexPosition - ComponentSpaceBrushPosition).SizeSquared() <= ComponentSpaceSquaredBrushRadius)
{
OutData.AddDefaulted();
TPair<int32, FVector>& OutPair = OutData.Last();
OutPair.Key = MeshIndices[InfluencedTriangle * 3 + Index];
OutPair.Value = MeshVertices[OutPair.Key];
}
}
}
}
TArray<FVector> FBaseMeshPaintComponentAdapter::SphereIntersectVertices(const float ComponentSpaceSquaredBrushRadius, const FVector& ComponentSpaceBrushPosition, const FVector& ComponentSpaceCameraPosition, const bool bOnlyFrontFacing) const
{
// Get list of intersecting triangles with given sphere data
const TArray<uint32> IntersectedTriangles = SphereIntersectTriangles(ComponentSpaceSquaredBrushRadius, ComponentSpaceBrushPosition, ComponentSpaceCameraPosition, bOnlyFrontFacing);
// Get a list of unique vertices indexed by the influenced triangles
TSet<int32> InfluencedVertices;
InfluencedVertices.Reserve(IntersectedTriangles.Num());
for (int32 IntersectedTriangle : IntersectedTriangles)
{
InfluencedVertices.Add(MeshIndices[IntersectedTriangle * 3 + 0]);
InfluencedVertices.Add(MeshIndices[IntersectedTriangle * 3 + 1]);
InfluencedVertices.Add(MeshIndices[IntersectedTriangle * 3 + 2]);
}
TArray<FVector> InRangeVertices;
InRangeVertices.Empty(MeshVertices.Num());
for (int32 VertexIndex : InfluencedVertices)
{
const FVector& Vertex = MeshVertices[VertexIndex];
if (FVector::DistSquared(ComponentSpaceBrushPosition, Vertex) <= ComponentSpaceSquaredBrushRadius)
{
InRangeVertices.Add(Vertex);
}
}
return InRangeVertices;
}
bool FBaseMeshPaintComponentAdapter::RayIntersectAdapter(UE::Geometry::FIndex3i& HitTriangle, FVector& HitPosition, const FVector Start, const FVector End) const
{
int32 HitTriangleID;
double NearestT;
FVector3d Direction((FVector3d)End - (FVector3d)Start);
UE::Geometry::Normalize(Direction);
FRay3d Ray((FVector3d)Start, Direction);
bool bHit = AABBTree->FindNearestHitTriangle(Ray, NearestT, HitTriangleID);
if (bHit)
{
HitPosition = (FVector)Ray.PointAt(NearestT);
HitTriangle = Adapter.GetTriangle(HitTriangleID);
}
return bHit;
}