Files
UnrealEngine/Engine/Plugins/Runtime/GeometryProcessing/Source/DynamicMesh/Private/Operations/MeshAttributeTransfer.cpp
2025-05-18 13:04:45 +08:00

123 lines
3.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Operations/MeshAttributeTransfer.h"
#include "DynamicMesh/DynamicMesh3.h"
#include "DynamicMesh/DynamicMeshAttributeSet.h"
#include "TriangleTypes.h"
#include "Util/IndexUtil.h"
#include "Async/ParallelFor.h"
using namespace UE::Geometry;
FMeshAttributeTransfer::FMeshAttributeTransfer(const FDynamicMesh3* SourceMeshIn, FDynamicMesh3* TargetMeshIn)
: SourceMesh(SourceMeshIn), TargetMesh(TargetMeshIn)
{
ensure(SourceMeshIn);
ensure(TargetMeshIn);
}
bool FMeshAttributeTransfer::Apply()
{
if (SourceMesh == nullptr)
{
Errors.Add(EMeshAttributeTransferError::InvalidSource);
return false;
}
if (TargetMesh == nullptr)
{
Errors.Add(EMeshAttributeTransferError::InvalidTarget);
return false;
}
if ( TransferType == EMeshAttributeTransferType::MaterialID)
{
return Apply_MaterialID();
}
else
{
Errors.Add(EMeshAttributeTransferError::InvalidOperationForInputs);
return false;
}
}
template<typename AllocType>
int32 SelectMostFrequentElement(TArray<int32, AllocType>& Samples)
{
Samples.Sort();
int32 N = Samples.Num();
int32 MaxVotes = 0;
int32 MaxVoteValue = Samples[0];
int32 CurValue = Samples[0];
int32 CurCount = 1;
for (int32 k = 1; k < N; ++k)
{
if (Samples[k] == CurValue)
{
CurCount++;
}
else
{
if (CurCount > MaxVotes)
{
MaxVotes = CurCount;
MaxVoteValue = CurValue;
}
CurCount = 1;
CurValue = Samples[k];
}
}
return MaxVoteValue;
}
bool FMeshAttributeTransfer::Apply_MaterialID()
{
const FDynamicMeshMaterialAttribute* SourceMaterialID = SourceMesh->HasAttributes() ? SourceMesh->Attributes()->GetMaterialID() : nullptr;
if (SourceMaterialID == nullptr)
{
Errors.Add(EMeshAttributeTransferError::SourceMissingAttribute);
return false;
}
FDynamicMeshMaterialAttribute* TargetMaterialID = TargetMesh->HasAttributes() ? TargetMesh->Attributes()->GetMaterialID() : nullptr;
if (TargetMaterialID == nullptr)
{
Errors.Add(EMeshAttributeTransferError::TargetMissingAttribute);
return false;
}
SourceSpatial = MakeUnique<FDynamicMeshAABBTree3>(SourceMesh, true);
static const FVector3d BarySamplePoints[7] = {
{1, 0, 0}, { 0,1,0 }, { 0,0,1 },
{ 0.5,0.5,0 }, {0.5,0,0.5}, {0,0.5,0.5},
{1.0/3.0, 1.0/3.0, 1.0/3.0}
};
constexpr int32 NumSamplePoints = 7; // must change Samples array below too!
// generate initial assignments
ParallelFor(TargetMesh->MaxTriangleID(), [&](int32 tid)
{
FTriangle3d TargetTriangle;
TargetMesh->GetTriVertices(tid, TargetTriangle.V[0], TargetTriangle.V[1], TargetTriangle.V[2]);
TArray<int32, TFixedAllocator<7>> Samples;
for (int32 k = 0; k < NumSamplePoints; ++k)
{
FVector3d SamplePoint = TargetTriangle.BarycentricPoint(BarySamplePoints[k]);
double DistSqr;
int32 SourceTriID = SourceSpatial->FindNearestTriangle(SamplePoint, DistSqr);
Samples.Add(SourceMaterialID->GetValue(SourceTriID));
}
int32 BestValue = SelectMostFrequentElement(Samples);
TargetMaterialID->SetValue(tid, BestValue);
});
return true;
}