// 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>(&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(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& FBaseMeshPaintComponentAdapter::GetMeshVertices() const { return MeshVertices; } const TArray& FBaseMeshPaintComponentAdapter::GetMeshIndices() const { return MeshIndices; } void FBaseMeshPaintComponentAdapter::GetVertexPosition(int32 VertexIndex, FVector& OutVertex) const { OutVertex = MeshVertices[VertexIndex]; } TArray FBaseMeshPaintComponentAdapter::SphereIntersectTriangles(const float ComponentSpaceSquaredBrushRadius, const FVector& ComponentSpaceBrushPosition, const FVector& ComponentSpaceCameraPosition, const bool bOnlyFrontFacing) const { TArray 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 &InfluencedVertices) const { // Get a list of (optionally front-facing) triangles that are within a reasonable distance to the brush const TArray 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>& OutData) const { // Get a list of (optionally front-facing) triangles that are within a reasonable distance to the brush TArray 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& OutPair = OutData.Last(); OutPair.Key = MeshIndices[InfluencedTriangle * 3 + Index]; OutPair.Value = MeshVertices[OutPair.Key]; } } } } TArray 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 IntersectedTriangles = SphereIntersectTriangles(ComponentSpaceSquaredBrushRadius, ComponentSpaceBrushPosition, ComponentSpaceCameraPosition, bOnlyFrontFacing); // Get a list of unique vertices indexed by the influenced triangles TSet 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 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; }