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

330 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MeshPaintGeometryCollectionAdapter.h"
#include "GeometryCollection/GeometryCollection.h"
#include "GeometryCollection/GeometryCollectionComponent.h"
#include "GeometryCollection/GeometryCollectionObject.h"
#include "RenderingThread.h"
DEFINE_LOG_CATEGORY_STATIC(LogMeshPaintGeometryCollectionComponentAdapter, Log, All);
//////////////////////////////////////////////////////////////////////////
// FMeshPaintGeometryCollectionComponentAdapter
bool FMeshPaintGeometryCollectionComponentAdapter::Construct(UMeshComponent* InComponent, int32 InMeshLODIndex)
{
GeometryCollectionComponent = Cast<UGeometryCollectionComponent>(InComponent);
if (HasValidGeometryCollection())
{
GeometryCollectionChangedHandle = GeometryCollectionComponent->RegisterOnGeometryCollectionPropertyChanged(UGeometryCollectionComponent::FOnGeometryCollectionPropertyChanged::CreateSP(this, &FMeshPaintGeometryCollectionComponentAdapter::OnGeometryCollectionChanged));
// MeshLODIndex = InMeshLODIndex; // TODO: Store the LOD index to work on, once GeometryCollection supports LODs
const bool bSuccess = Initialize();
return bSuccess;
}
return false;
}
FMeshPaintGeometryCollectionComponentAdapter::~FMeshPaintGeometryCollectionComponentAdapter()
{
if (GeometryCollectionComponent.IsValid())
{
GeometryCollectionComponent->UnregisterOnGeometryCollectionPropertyChanged(GeometryCollectionChangedHandle);
}
}
UGeometryCollection* FMeshPaintGeometryCollectionComponentAdapter::GetGeometryCollectionObject() const
{
checkSlow(GeometryCollectionComponent.IsValid()); // expect this to be called only when the component is already verified as valid
// Note: We bypass the FGeometryCollectionEdit machinery here because it doesn't fit with the model of the Mesh Paint Component Adapter
// Instead, we will manually call Modify() as needed
return const_cast<UGeometryCollection*>(GeometryCollectionComponent->GetRestCollection());
}
void FMeshPaintGeometryCollectionComponentAdapter::OnGeometryCollectionChanged()
{
// Remove and re-initialize/add the geometry collection to make sure it is up to date after something changed
OnRemoved();
if (HasValidGeometryCollection())
{
Initialize();
OnAdded();
}
}
bool FMeshPaintGeometryCollectionComponentAdapter::Initialize()
{
bool bInitializationResult = false;
if (HasValidGeometryCollection())
{
bInitializationResult = FBaseMeshPaintComponentAdapter::Initialize(); // Calls InitializeVertexData() then builds octree, etc
}
return bInitializationResult;
}
bool FMeshPaintGeometryCollectionComponentAdapter::HasValidGeometryCollection() const
{
return GeometryCollectionComponent.IsValid() && GetGeometryCollectionObject() && GetGeometryCollectionObject()->GetGeometryCollection();
}
bool FMeshPaintGeometryCollectionComponentAdapter::IsValid() const
{
return GeometryCollectionComponent.IsValid() && GetGeometryCollectionObject() && GetGeometryCollectionObject()->GetGeometryCollection() // Pointers valid
&& GetGeometryCollectionObject()->GetGeometryCollection()->Vertex.Num() == MeshVertices.Num(); // Data matches what we've converted
}
bool FMeshPaintGeometryCollectionComponentAdapter::InitializeVertexData()
{
// Retrieve mesh vertex and index data
FGeometryCollection* Collection = GetGeometryCollectionObject()->GetGeometryCollection().Get();
const TManagedArray<FVector3f>& Vertices = Collection->Vertex;
// TODO: Revisit this when we support the same geometry mapping to multiple bones
const TManagedArray<int32>& BoneMap = Collection->BoneMap;
const TManagedArray<int32>& SimTypes = Collection->SimulationType;
const TManagedArray<bool>& Visible = Collection->Visible;
const TArray<FTransform3f>& ComponentSpaceTransforms = GeometryCollectionComponent->GetComponentSpaceTransforms3f();
const int32 NumVertices = Vertices.Num();
MeshVertices.Reset();
MeshVertices.AddZeroed(NumVertices);
for (int32 Index = 0; Index < NumVertices; ++Index)
{
const int32 BoneIndex = BoneMap[Index];
const FTransform3f& Transform = ComponentSpaceTransforms[BoneIndex];
const FVector Position = FVector(Transform.TransformPosition(Vertices[Index]));
MeshVertices[Index] = Position;
}
const TManagedArray<FIntVector>& ManagedIndices = Collection->Indices;
int32 NumTris = ManagedIndices.Num();
for (int32 TriIdx = 0; TriIdx < NumTris; ++TriIdx)
{
if (Visible[TriIdx])
{
MeshIndices.Add(ManagedIndices[TriIdx].X);
MeshIndices.Add(ManagedIndices[TriIdx].Y);
MeshIndices.Add(ManagedIndices[TriIdx].Z);
}
}
return (MeshVertices.Num() >= 0 && MeshIndices.Num() > 0);
}
void FMeshPaintGeometryCollectionComponentAdapter::InitializeAdapterGlobals()
{
static bool bInitialized = false;
if (!bInitialized)
{
bInitialized = true;
}
}
void FMeshPaintGeometryCollectionComponentAdapter::AddReferencedObjectsGlobals(FReferenceCollector& Collector)
{
}
void FMeshPaintGeometryCollectionComponentAdapter::AddReferencedObjects(FReferenceCollector& Collector)
{
}
void FMeshPaintGeometryCollectionComponentAdapter::CleanupGlobals()
{
}
void FMeshPaintGeometryCollectionComponentAdapter::OnAdded()
{
// We shouldn't assume that the cached geometry collection component remains valid.
// Components may be destroyed by editor ticks, and be forcibly removed by GC.
if (!GeometryCollectionComponent.IsValid())
{
return;
}
bSavedShowBoneColors = GeometryCollectionComponent->GetShowBoneColors();
GeometryCollectionComponent->SetShowBoneColors(false);
checkf(GetGeometryCollectionObject(), TEXT("Geometry Collection Component did not have a valid geometry collection object attached"));
checkf(GetGeometryCollectionObject()->GetGeometryCollection(), TEXT("Geometry Collection Component did not have valid geometry collection data attached"));
}
void FMeshPaintGeometryCollectionComponentAdapter::OnRemoved()
{
if (GeometryCollectionComponent.IsValid())
{
GeometryCollectionComponent->SetShowBoneColors(bSavedShowBoneColors);
bSavedShowBoneColors = false;
}
}
int32 FMeshPaintGeometryCollectionComponentAdapter::GetNumUVChannels() const
{
if (!IsValid())
{
return 0;
}
return GetGeometryCollectionObject()->GetGeometryCollection()->NumUVLayers();
}
bool FMeshPaintGeometryCollectionComponentAdapter::LineTraceComponent(struct FHitResult& OutHit, const FVector Start, const FVector End, const struct FCollisionQueryParams& Params) const
{
// Note: This is 99% the same as the skeletal mesh component adapter version, and could likely be pushed up to the base class if we added a generalized way to get the Component
if (!IsValid())
{
return false;
}
const bool bHitBounds = FMath::LineSphereIntersection(Start, End.GetSafeNormal(), (End - Start).SizeSquared(), GeometryCollectionComponent->Bounds.Origin, GeometryCollectionComponent->Bounds.SphereRadius);
const float SqrRadius = FMath::Square(GeometryCollectionComponent->Bounds.SphereRadius);
const bool bInsideBounds = (GeometryCollectionComponent->Bounds.ComputeSquaredDistanceFromBoxToPoint(Start) <= SqrRadius) || (GeometryCollectionComponent->Bounds.ComputeSquaredDistanceFromBoxToPoint(End) <= SqrRadius);
bool bHitTriangle = false;
if (bHitBounds || bInsideBounds)
{
const FTransform& ComponentTransform = GeometryCollectionComponent->GetComponentTransform();
const FVector LocalStart = ComponentTransform.InverseTransformPosition(Start);
const FVector LocalEnd = ComponentTransform.InverseTransformPosition(End);
float MinDistance = FLT_MAX;
FVector Intersect;
FVector Normal;
UE::Geometry::FIndex3i FoundTriangle;
FVector HitPosition;
if (!RayIntersectAdapter(FoundTriangle, HitPosition, LocalStart, LocalEnd))
{
return false;
}
// Compute the normal of the triangle
const FVector& P0 = MeshVertices[FoundTriangle.A];
const FVector& P1 = MeshVertices[FoundTriangle.B];
const FVector& P2 = MeshVertices[FoundTriangle.C];
const FVector TriNorm = (P1 - P0) ^ (P2 - P0);
//check collinearity of A,B,C
if (TriNorm.SizeSquared() > SMALL_NUMBER)
{
FVector IntersectPoint;
FVector HitNormal;
bool bHit = FMath::SegmentTriangleIntersection(LocalStart, LocalEnd, P0, P1, P2, IntersectPoint, HitNormal);
if (bHit)
{
const float Distance = (LocalStart - IntersectPoint).SizeSquared();
if (Distance < MinDistance)
{
MinDistance = Distance;
Intersect = IntersectPoint;
Normal = HitNormal;
}
}
}
if (MinDistance != FLT_MAX)
{
OutHit.Component = GeometryCollectionComponent;
OutHit.Normal = ComponentTransform.TransformVector(Normal).GetSafeNormal();
OutHit.ImpactNormal = OutHit.Normal;
OutHit.ImpactPoint = ComponentTransform.TransformPosition(Intersect);
OutHit.Location = OutHit.ImpactPoint;
OutHit.bBlockingHit = true;
OutHit.Distance = MinDistance;
bHitTriangle = true;
}
}
return bHitTriangle;
}
void FMeshPaintGeometryCollectionComponentAdapter::QueryPaintableTextures(int32 MaterialIndex, int32& OutDefaultIndex, TArray<struct FPaintableTexture>& InOutTextureList)
{
if (GeometryCollectionComponent.IsValid())
{
DefaultQueryPaintableTextures(MaterialIndex, GeometryCollectionComponent.Get(), OutDefaultIndex, InOutTextureList);
}
}
void FMeshPaintGeometryCollectionComponentAdapter::ApplyOrRemoveTextureOverride(UTexture* SourceTexture, UTexture* OverrideTexture) const
{
if (GeometryCollectionComponent.IsValid())
{
TextureOverridesState.ApplyOrRemoveTextureOverride(GeometryCollectionComponent.Get(), SourceTexture, OverrideTexture);
}
}
void FMeshPaintGeometryCollectionComponentAdapter::GetTextureCoordinate(int32 VertexIndex, int32 ChannelIndex, FVector2D& OutTextureCoordinate) const
{
const TManagedArray<FVector2f>* UVLayer = GetGeometryCollectionObject()->GetGeometryCollection()->FindUVLayer(ChannelIndex);
if (UVLayer)
{
OutTextureCoordinate = (FVector2D)(*UVLayer)[VertexIndex];
}
}
void FMeshPaintGeometryCollectionComponentAdapter::PreEdit()
{
if (!IsValid())
{
return;
}
FlushRenderingCommands();
GeometryCollectionComponent->Modify();
GetGeometryCollectionObject()->Modify();
}
void FMeshPaintGeometryCollectionComponentAdapter::PostEdit()
{
if (!GeometryCollectionComponent.IsValid())
{
return;
}
GetGeometryCollectionObject()->RebuildRenderData();
}
void FMeshPaintGeometryCollectionComponentAdapter::GetVertexColor(int32 VertexIndex, FColor& OutColor, bool bInstance /*= true*/) const
{
OutColor = GetGeometryCollectionObject()->GetGeometryCollection()->Color[VertexIndex].ToFColor(true);
}
void FMeshPaintGeometryCollectionComponentAdapter::SetVertexColor(int32 VertexIndex, FColor Color, bool bInstance /*= true*/)
{
GetGeometryCollectionObject()->GetGeometryCollection()->Color[VertexIndex] = FLinearColor(Color);
}
FMatrix FMeshPaintGeometryCollectionComponentAdapter::GetComponentToWorldMatrix() const
{
if (!GeometryCollectionComponent.IsValid())
{
return FMatrix::Identity;
}
return GeometryCollectionComponent->GetComponentToWorld().ToMatrixWithScale();
}
//////////////////////////////////////////////////////////////////////////
// FMeshPaintGeometryCollectionComponentAdapterFactory
TSharedPtr<IMeshPaintComponentAdapter> FMeshPaintGeometryCollectionComponentAdapterFactory::Construct(class UMeshComponent* InComponent, int32 InMeshLODIndex) const
{
if (UGeometryCollectionComponent* GeometryCollectionComponent = Cast<UGeometryCollectionComponent>(InComponent))
{
if (GeometryCollectionComponent->GetRestCollection() != nullptr)
{
TSharedRef<FMeshPaintGeometryCollectionComponentAdapter> Result = MakeShareable(new FMeshPaintGeometryCollectionComponentAdapter());
if (Result->Construct(InComponent, InMeshLODIndex))
{
return Result;
}
}
}
return nullptr;
}