Files
UnrealEngine/Engine/Source/Runtime/MeshConversion/Public/ToDynamicMesh.h
2025-05-18 13:04:45 +08:00

782 lines
28 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "DynamicMesh/DynamicMesh3.h"
#include "DynamicMesh/DynamicMeshAttributeSet.h"
#include "DynamicMesh/DynamicMeshOverlay.h"
#include "DynamicMesh/DynamicVertexSkinWeightsAttribute.h"
#include "DynamicMesh/DynamicBoneAttribute.h"
#include "VectorTypes.h"
#include "Async/Async.h"
namespace UE
{
namespace Geometry
{
/**
*
* Class used to convert a mesh without attributes (e.g. normals, uvs etc) to a FDynamicMesh3
*
* The Source Mesh has to implement this interface
*
* struct FSrcMeshInterface
* {
* // ID types: must be castable to int32
* typedef SrcVertIDType VertIDType;
* typedef SrcTriIDType TriIDType;
*
* // accounting.
* int32 NumTris() const;
* int32 NumVerts() const;
*
* // --"Vertex Buffer" info
* const Iterable_VertIDType& GetVertIDs() const;
* const FVector GetPosition(const VertIDType VtxID) const;
*
* // --"Index Buffer" info
* const Iterable_TriIDType& GetTriIDs() const
* // return false if this TriID is not contained in mesh.
* bool GetTri(const TriIDType TriID, VertIDType& VID0, VertIDType& VID1, VertIDType& VID2) const;
* };
*
*/
template<typename SrcMeshType>
class TToDynamicMeshBase
{
public:
using SrcVertIDType = typename SrcMeshType::VertIDType;
using SrcTriIDType = typename SrcMeshType::TriIDType;
TToDynamicMeshBase()
{
}
// Mapping data captured by conversion
TArray<SrcVertIDType> ToSrcVertIDMap;
TArray<SrcTriIDType> ToSrcTriIDMap;
FDateTime Time_AfterVertices;
FDateTime Time_AfterTriangles;
// Produces a DynamicMesh3 w/o attributes from the MeshIn.
void Convert(FDynamicMesh3& MeshOut, const SrcMeshType& MeshIn, TFunctionRef<int32(const SrcTriIDType& SrcTrID)> GroupIDFunction)
{
MeshOut.Clear();
MeshOut.DiscardAttributes();
MeshOut.EnableTriangleGroups(0);
ToSrcVertIDMap.Reset();
ToSrcTriIDMap.Reset();
const int32 NumSrcVerts = MeshIn.NumVerts();
const int32 NumSrcTris = MeshIn.NumTris();
if (NumSrcVerts == 0 || NumSrcTris == 0)
{
return;
}
// allocate for map data
ToSrcVertIDMap.AddUninitialized(NumSrcVerts);
ToSrcTriIDMap.AddUninitialized(NumSrcTris);
// copy vertex positions. Later we may have to append duplicate vertices to resolve non-manifold structures.
int32 MaxSrcVertID = -1;
for (const SrcVertIDType& SrcVertID : MeshIn.GetVertIDs())
{
const FVector3d Position = MeshIn.GetPosition(SrcVertID);
const int32 DstVertID = MeshOut.AppendVertex(Position);
ToSrcVertIDMap[DstVertID] = SrcVertID;
MaxSrcVertID = FMath::Max((int32)SrcVertID, MaxSrcVertID);
}
// map the other direction
TArray<int32> FromSrcVertIDMap;
FromSrcVertIDMap.AddUninitialized(MaxSrcVertID+1);
for (int32 i = 0; i < ToSrcVertIDMap.Num(); ++i)
{
int32 SrcVertID = (int32)ToSrcVertIDMap[i];
FromSrcVertIDMap[SrcVertID] = i;
}
Time_AfterVertices = FDateTime::Now();
// copy the index buffer. Note, this may add vertices if a non-manifold state has to be resolved.
for (const SrcTriIDType& SrcTriID : MeshIn.GetTriIDs())
{
const int32 DstGroupID = GroupIDFunction(SrcTriID);
SrcVertIDType SrcTriVerts[3];
MeshIn.GetTri(SrcTriID, SrcTriVerts[0], SrcTriVerts[1], SrcTriVerts[2]);
FIndex3i DstTriVerts(FromSrcVertIDMap[SrcTriVerts[0]],
FromSrcVertIDMap[SrcTriVerts[1]],
FromSrcVertIDMap[SrcTriVerts[2]]);
// attemp to add tri. this may fail if DynamicMesh is more topologically restrictive than the src mesh.
int32 DstTriangleID = MeshOut.AppendTriangle(DstTriVerts, DstGroupID);
//-- already seen this triangle for some reason.. or the src mesh had a degenerate tri
if (DstTriangleID == FDynamicMesh3::DuplicateTriangleID || DstTriangleID == FDynamicMesh3::InvalidID)
{
continue;
}
//-- non manifold
// if append failed due to non-manifold, duplicate verts
if (DstTriangleID == FDynamicMesh3::NonManifoldID)
{
// determine which verts need to be duplicated
bool bDuplicate[3] = { false, false, false };
for (int i = 0; i < 3; ++i)
{
int ii = (i + 1) % 3;
int EID = MeshOut.FindEdge(DstTriVerts[i], DstTriVerts[ii]);
if (EID != FDynamicMesh3::InvalidID && MeshOut.IsBoundaryEdge(EID) == false)
{
bDuplicate[i] = true;
bDuplicate[ii] = true;
}
}
for (int i = 0; i < 3; ++i)
{
if (bDuplicate[i])
{
const FVector3d Position = MeshOut.GetVertex(DstTriVerts[i]);
const int32 NewDstVertID = MeshOut.AppendVertex(Position);
DstTriVerts[i] = NewDstVertID;
// grow the map to make room
ToSrcVertIDMap.SetNumUninitialized(NewDstVertID + 1);
ToSrcVertIDMap[NewDstVertID] = SrcTriVerts[i];
}
}
// add the fixed tri
DstTriangleID = MeshOut.AppendTriangle(DstTriVerts, DstGroupID);
checkSlow(DstTriangleID != FDynamicMesh3::NonManifoldID);
}
// record in map
ToSrcTriIDMap[DstTriangleID] = SrcTriID;
}
const int32 MaxDstTriID = MeshOut.MaxTriangleID(); // really MaxTriID+1
// if the source mesh had duplicates then TriIDMap will be too long, this will trim excess
ToSrcTriIDMap.SetNum(MaxDstTriID);
Time_AfterTriangles = FDateTime::Now();
}
};
// Used for exact attribute value welding.
template <typename AttrType>
struct TVertexAttr
{
int VID;
AttrType AttrValue;
bool operator==(const TVertexAttr& o) const
{
return VID == o.VID && AttrValue == o.AttrValue;
}
};
template <typename AttrType>
FORCEINLINE uint32 GetTypeHash(const TVertexAttr<AttrType>& Vector)
{
// ugh copied from FVector clearly should not be using CRC for hash!!
return FCrc::MemCrc32(&Vector, sizeof(Vector));
}
template <typename AttrType> struct TOverlayTraits {};
template<> struct TOverlayTraits<FVector3f> { typedef FDynamicMeshNormalOverlay OverlayType; };
template<> struct TOverlayTraits<FVector2f> { typedef FDynamicMeshUVOverlay OverlayType; };
template<> struct TOverlayTraits<FVector4f> { typedef FDynamicMeshColorOverlay OverlayType; };
// Welder used for exact attribute value welding when constructing overlay
template <typename AttrType>
class TAttrWelder
{
public:
using OverlayType = typename TOverlayTraits<AttrType>::OverlayType;
using FVertexAttr = TVertexAttr<AttrType>;
TMap<FVertexAttr, int> UniqueVertexAttrs;
OverlayType* Overlay;
TAttrWelder() : Overlay(nullptr){}
TAttrWelder(OverlayType* OverlayIn)
{
check(OverlayIn);
Overlay = OverlayIn;
}
int FindOrAddUnique(const AttrType& AttrValue, int VertexID)
{
FVertexAttr VertAttr = { VertexID, AttrValue };
const int32* FoundIndex = UniqueVertexAttrs.Find(VertAttr);
if (FoundIndex != nullptr)
{
return *FoundIndex;
}
int32 NewIndex = Overlay->AppendElement(AttrValue);
UniqueVertexAttrs.Add(VertAttr, NewIndex);
return NewIndex;
}
};
/**
*
* Class used to convert a mesh with attributes (e.g. normals, uvs etc) to a FDynamicMesh3
*
* The Source Mesh has to implement this interface
*
* struct FSrcMeshInterface
* {
* // ID types: must be castable to int32
* typedef SrcVertIDType VertIDType;
* typedef SrcTriIDType TriIDType;
* typedef SrcUVIDType UVIDType;
* typedef SrcNormalIDType NormalIDType;
* typedef SrcColorIDType ColorIDType
* typedef SrcWedgeIDType WedgeIDType;
*
* // accounting.
* int32 NumTris() const;
* int32 NumVerts() const;
* int32 NumUVLayers() const;
*
* // --"Vertex Buffer" info
* const Iterable_VertIDType& GetVertIDs() const;
* const FVector3d GetPosition(const VertIDType VtxID) const;
*
* // --"Index Buffer" info
* const Iterable_TriIDType& GetTriIDs() const
* // return false if this TriID is not contained in mesh.
* bool GetTri(const TriIDType TriID, VertIDType& VID0, VertIDType& VID1, VertIDType& VID2) const;
*
* bool HasNormals() const;
* bool HasTangents() cosnt;
* bool HasBiTangents() const;
*
* // Each triangle corner is a wedge
* void GetWedgeID(const TriIDType& TriID, WedgeIDType& WID0, WedgeIDType& WID1, WedgeIDType& WID2) const;
*
* // attribute access per-wedge
* // NB: ToDynamicMesh will attempt to weld identical attributes that are associated with the same vertex
* FVector2f GetWedgeUV(int32 UVLayerIndex, WedgeIDType WID) const;
* FVector3f GetWedgeNormal(WedgeIDType WID) const;
* FVector3f GetWedgeTangent(WedgeIDType WID) const;
* FVector3f GetWedgeBiTangent(WedgeIDType WID) const;
* FVector4f GetWedgeColor(WedgeIDType WID) const;
*
* // attribute access that exploits shared attributes.
* // each group of shared attributes presents itself as a mesh with its own attribute vertex buffer.
* // NB: If the mesh has no shared Attr attributes, then Get{Attr}IDs() should return an empty array.
* // NB: Get{Attr}Tri() functions should return false if the triangle is not set in the attribute mesh.
*
* const TArray<UVIDType>& GetUVIDs(int32 LayerID) const;
* FVector2f GetUV(int32 LayerID, UVIDType UVID) const;
* bool GetUVTri(int32 LayerID, const TriIDType& TID, UVIDType& ID0, UVIDType& ID1, UVIDType& ID2) const;
*
* const TArray<NormalIDType>& GetNormalIDs() const;
* FVector3f GetNormal(NormalIDType ID) const;
* bool GetNormalTri(const TriIDType& TID, NormalIDType& NID0, NormalIDType& NID1, NormalIDType& NID2) const;
*
* const TArray<NormalIDType>& GetTangentIDs() const;
* FVector3f GetTangent(NormalIDType ID) const;
* bool GetTangentTri(const TriIDType& TID, NormalIDType& NID0, NormalIDType& NID1, NormalIDType& NID2) const;
*
* const TArray<NormalIDType>& GetBiTangentIDs() const;
* FVector3f GetBiTangent(NormalIDType ID) const;
* bool GetBiTangentTri(const TriIDType& TID, NormalIDType& NID0, NormalIDType& NID1, NormalIDType& NID2) const;
*
* const TArray<ColorIDType>& GetColorIDs() const;
* FVector4f GetColor(ColorIDType ID) const;
* bool GetColorTri(const TriIDType& TID, ColorIDType& NID0, ColorIDType& NID1, ColorIDType& NID2) const;
*
* // weight maps information
* int32 NumWeightMapLayers() const;
* float GetVertexWeight(int32 WeightMapIndex, int32 SrcVertID) const;
* FName GetWeightMapName(int32 WeightMapIndex) const;
*
* // skin weight attributes information
* int32 NumSkinWeightAttributes() const;
* UE::AnimationCore::FBoneWeights GetVertexSkinWeight(int32 SkinWeightAttributeIndex, VertIDType VtxID) const;
* FName GetSkinWeightAttributeName(int32 SkinWeightAttributeIndex) const;
*
* // bone attributes information
* int32 GetNumBones() const;
* FName GetBoneName(int32 BoneIdx) const;
* int32 GetBoneParentIndex(int32 BoneIdx) const;
* FTransform GetBonePose(int32 BoneIdx) const;
* FVector4f GetBoneColor(int32 BoneIdx) const;
* };
*
*/
template <typename SrcMeshType>
class TToDynamicMesh : public TToDynamicMeshBase<SrcMeshType>
{
public:
typedef TToDynamicMeshBase<SrcMeshType> MyBase;
using SrcTriIDType = typename MyBase::SrcTriIDType;
using SrcVertIDType = typename MyBase::SrcVertIDType;
using SrcUVIDType = typename SrcMeshType::UVIDType;
using SrcNormalIDType = typename SrcMeshType::NormalIDType;
using SrcColorIDType = typename SrcMeshType::ColorIDType;
using SrcWedgeIDType = typename SrcMeshType::WedgeIDType;
TToDynamicMesh() :MyBase() {}
// Convert To FDynamicMesh w/o attributes
void ConvertWOAttributes(FDynamicMesh3& MeshOut,
const SrcMeshType& MeshIn,
TFunctionRef<int32(const SrcTriIDType& SrcTrID)> GroupIDFunction)
{
MyBase::Convert(MeshOut, MeshIn, GroupIDFunction);
}
// Convert To FDynamicMesh. Will Copy GroupID to the mesh, and will create overlays with UVs, Normal, MaterialID (additionally Tangents and BiTangents if requested)
void Convert(FDynamicMesh3& MeshOut,
const SrcMeshType& MeshIn,
TFunctionRef<int32(const SrcTriIDType& SrcTrID)> GroupIDFunction,
TFunctionRef<int32(const SrcTriIDType& SrcTrID)> MaterialIDFunction,
bool bCopyTangents)
{
MyBase::Convert(MeshOut, MeshIn, GroupIDFunction); // convert the mesh. Must call before populate overlays
PopulateOverlays(MeshOut, MeshIn, MaterialIDFunction, bCopyTangents); // convert the attributes
}
protected:
// Populates overlays for UVs, Normal, Color, Material ID. Also Tangent and BiTangent if bCopyTangents == true.
// NB: This does not copy any polygroup information.
void PopulateOverlays(FDynamicMesh3& MeshOut, const SrcMeshType& MeshIn, TFunctionRef<int32(const SrcTriIDType& SrcTrID)> MaterialIDFunction, bool bCopyTangents)
{
// Attributes we want to transfer
FDynamicMeshNormalOverlay* NormalOverlay = nullptr;
FDynamicMeshNormalOverlay* TangentOverlay = nullptr;
FDynamicMeshNormalOverlay* BiTangentOverlay = nullptr;
FDynamicMeshColorOverlay* ColorOverlay = nullptr;
FDynamicMeshMaterialAttribute* MaterialIDAttrib = nullptr;
// only copy tangents if they exist
bCopyTangents = bCopyTangents && MeshIn.HasTangents() && MeshIn.HasBiTangents();
// -- Create Overlays
const int32 NumUVLayers = MeshIn.NumUVLayers();
MeshOut.EnableAttributes(); // by default 1-UV layer and 1-normal layer
// create any addition Normal overlays and reserve space.
int32 NumRequiredNormalLayers = (bCopyTangents) ? 3 : 1;
if (MeshOut.Attributes()->NumNormalLayers() < NumRequiredNormalLayers)
{
MeshOut.Attributes()->SetNumNormalLayers(NumRequiredNormalLayers);
}
for (int32 i = 0; i < NumRequiredNormalLayers; ++i)
{
MeshOut.Attributes()->GetNormalLayer(i)->InitializeTriangles(MeshOut.MaxTriangleID());
}
NormalOverlay = MeshOut.Attributes()->PrimaryNormals();
if (bCopyTangents)
{
TangentOverlay = MeshOut.Attributes()->PrimaryTangents();
BiTangentOverlay = MeshOut.Attributes()->PrimaryBiTangents();
}
// create UV overlays
MeshOut.Attributes()->SetNumUVLayers(FMath::Max(1, NumUVLayers));
// reserve space in any new UV layers.
for (int32 i = 1; i < NumUVLayers; ++i) //-V654 //-V621 (The static analyzer complains if it knows NumUVLayers is 0 for a given SrcMeshType)
{
MeshOut.Attributes()->GetUVLayer(i)->InitializeTriangles(MeshOut.MaxTriangleID());
}
// create color overlay
if (MeshIn.HasColors())
{
MeshOut.Attributes()->EnablePrimaryColors();
ColorOverlay = MeshOut.Attributes()->PrimaryColors();
ColorOverlay->InitializeTriangles(MeshOut.MaxTriangleID());
}
// always enable Material ID if there are any attributes
MeshOut.Attributes()->EnableMaterialID();
MaterialIDAttrib = MeshOut.Attributes()->GetMaterialID();
// do the conversions.
// we will populate all the attributes simultaneously, hold on to futures in this array and then Wait for them at the end
TArray<TFuture<void>> Pending;
// populate UV overlays
for (int UVLayerIndex = 0; UVLayerIndex < NumUVLayers; UVLayerIndex++) //-V654 //-V621 (The static analyzer complains if it knows NumUVLayers is 0 for a given SrcMeshType)
{
auto UVFuture = Async(EAsyncExecution::ThreadPool, [&, UVLayerIndex]()
{
FDynamicMeshUVOverlay* Overlay = MeshOut.Attributes()->GetUVLayer(UVLayerIndex);
PopulateOverlay<FVector2f, SrcUVIDType>(
Overlay,
MeshIn.GetUVIDs(UVLayerIndex),
[&MeshIn, UVLayerIndex](const SrcUVIDType& UVID)->FVector2f { return MeshIn.GetUV(UVLayerIndex, UVID); },
[&MeshIn, UVLayerIndex](const SrcTriIDType& TriID, SrcUVIDType& UV0, SrcUVIDType& UV1, SrcUVIDType& UV2)->bool {return MeshIn.GetUVTri(UVLayerIndex, TriID, UV0, UV1, UV2); },
[&MeshIn, UVLayerIndex](const SrcWedgeIDType& WID)->FVector2f {return MeshIn.GetWedgeUV(UVLayerIndex, WID); },
[&MeshIn](const SrcTriIDType& TriID, SrcWedgeIDType& WID0, SrcWedgeIDType& WID1, SrcWedgeIDType& WID2)->void { MeshIn.GetWedgeIDs(TriID, WID0, WID1, WID2); }
);
});
Pending.Add(MoveTemp(UVFuture));
}
// populate Normal overlay
if (NormalOverlay != nullptr)
{
auto NormalFuture = Async(EAsyncExecution::ThreadPool, [&]()
{
PopulateOverlay<FVector3f, SrcNormalIDType>(
NormalOverlay,
MeshIn.GetNormalIDs(),
[&MeshIn](const SrcNormalIDType& NID)->FVector3f { return MeshIn.GetNormal(NID); },
[&MeshIn](const SrcTriIDType& TriID, SrcNormalIDType& N0, SrcNormalIDType& N1, SrcNormalIDType& N2)->bool {return MeshIn.GetNormalTri(TriID, N0, N1, N2); },
[&MeshIn](const SrcWedgeIDType& WID)->FVector3f {return MeshIn.GetWedgeNormal(WID); },
[&MeshIn](const SrcTriIDType& TriID, SrcWedgeIDType& WID0, SrcWedgeIDType& WID1, SrcWedgeIDType& WID2)->void { MeshIn.GetWedgeIDs(TriID, WID0, WID1, WID2); }
);
});
Pending.Add(MoveTemp(NormalFuture));
}
// populate Tangent overlay
if (TangentOverlay != nullptr)
{
auto TangentFuture = Async(EAsyncExecution::ThreadPool, [&]()
{
PopulateOverlay<FVector3f, SrcNormalIDType>(
TangentOverlay,
MeshIn.GetTangentIDs(),
[&MeshIn](const SrcNormalIDType& NID)->FVector3f { return MeshIn.GetTangent(NID); },
[&MeshIn](const SrcTriIDType& TriID, SrcNormalIDType& N0, SrcNormalIDType& N1, SrcNormalIDType& N2)->bool {return MeshIn.GetTangentTri(TriID, N0, N1, N2); },
[&MeshIn](const SrcWedgeIDType& WID)->FVector3f {return MeshIn.GetWedgeTangent(WID); },
[&MeshIn](const SrcTriIDType& TriID, SrcWedgeIDType& WID0, SrcWedgeIDType& WID1, SrcWedgeIDType& WID2)->void { MeshIn.GetWedgeIDs(TriID, WID0, WID1, WID2); }
);
});
Pending.Add(MoveTemp(TangentFuture));
}
//populate BiTangent overlay
if (BiTangentOverlay != nullptr)
{
auto BiTangentFuture = Async(EAsyncExecution::ThreadPool, [&]()
{
PopulateOverlay<FVector3f, SrcNormalIDType>(
BiTangentOverlay,
MeshIn.GetBiTangentIDs(),
[&MeshIn](const SrcNormalIDType& NID)->FVector3f { return MeshIn.GetBiTangent(NID); },
[&MeshIn](const SrcTriIDType& TriID, SrcNormalIDType& N0, SrcNormalIDType& N1, SrcNormalIDType& N2)->bool {return MeshIn.GetBiTangentTri(TriID, N0, N1, N2); },
[&MeshIn](const SrcWedgeIDType& WID)->FVector3f {return MeshIn.GetWedgeBiTangent(WID); },
[&MeshIn](const SrcTriIDType& TriID, SrcWedgeIDType& WID0, SrcWedgeIDType& WID1, SrcWedgeIDType& WID2)->void { MeshIn.GetWedgeIDs(TriID, WID0, WID1, WID2); }
);
});
Pending.Add(MoveTemp(BiTangentFuture));
}
if (ColorOverlay != nullptr)
{
auto ColorFuture = Async(EAsyncExecution::ThreadPool, [&]()
{
PopulateOverlay<FVector4f, SrcColorIDType>(
ColorOverlay,
MeshIn.GetColorIDs(),
[&MeshIn](const SrcColorIDType& CID)->FVector4f { return MeshIn.GetColor(CID); },
[&MeshIn](const SrcTriIDType& TriID, SrcColorIDType& C0, SrcColorIDType& C1, SrcColorIDType& C2)->bool {return MeshIn.GetColorTri(TriID, C0, C1, C2); },
[&MeshIn](const SrcWedgeIDType& WID)->FVector4f {return MeshIn.GetWedgeColor(WID); },
[&MeshIn](const SrcTriIDType& TriID, SrcWedgeIDType& WID0, SrcWedgeIDType& WID1, SrcWedgeIDType& WID2)->void { MeshIn.GetWedgeIDs(TriID, WID0, WID1, WID2); }
);
});
Pending.Add(MoveTemp(ColorFuture));
}
//populate MaterialID overlay
if (MaterialIDAttrib != nullptr)
{
auto MaterialFuture = Async(EAsyncExecution::ThreadPool, [&]()
{
for (int32 TriangleID : MeshOut.TriangleIndicesItr())
{
SrcTriIDType SrcTriID = MyBase::ToSrcTriIDMap[TriangleID];
MaterialIDAttrib->SetValue(TriangleID, MaterialIDFunction(SrcTriID));
}
});
Pending.Add(MoveTemp(MaterialFuture));
}
//populate WeightMap attribute
const int32 NumWeightMaps = MeshIn.NumWeightMapLayers();
MeshOut.Attributes()->SetNumWeightLayers(NumWeightMaps);
for (int WeightMapIndex = 0; WeightMapIndex < NumWeightMaps; WeightMapIndex++) //-V654 //-V621 (The static analyzer complains if it knows NumWeightMaps is 0 for a given SrcMeshType)
{
auto WeightMapFuture = Async(EAsyncExecution::ThreadPool, [this, &MeshIn, &MeshOut, WeightMapIndex]()
{
FDynamicMeshWeightAttribute* WeightMapAttrib = MeshOut.Attributes()->GetWeightLayer(WeightMapIndex);
for (int32 VertexID : MeshOut.VertexIndicesItr())
{
SrcVertIDType SrcVertID = MyBase::ToSrcVertIDMap[VertexID];
float Weight = MeshIn.GetVertexWeight(WeightMapIndex, SrcVertID);
WeightMapAttrib->SetValue(VertexID, &Weight);
}
WeightMapAttrib->SetName( MeshIn.GetWeightMapName(WeightMapIndex) );
});
Pending.Add(MoveTemp(WeightMapFuture));
}
// populate skinning weight attribute
const int32 NumSkinWeightAttributes = MeshIn.NumSkinWeightAttributes();
// first, attach all the skinning weight attributes (this also allocates memory to store skinning weights for each attribute)
for (int AttributeIndex = 0; AttributeIndex < NumSkinWeightAttributes; ++AttributeIndex) //-V621 //-V654
{
UE::Geometry::FDynamicMeshVertexSkinWeightsAttribute* Attribute = new UE::Geometry::FDynamicMeshVertexSkinWeightsAttribute(&MeshOut);
const FName AttribName = MeshIn.GetSkinWeightAttributeName(AttributeIndex);
Attribute->SetName(AttribName);
MeshOut.Attributes()->AttachSkinWeightsAttribute(AttribName, Attribute);
}
// now set all of the skin weight data
for (int AttributeIndex = 0; AttributeIndex < NumSkinWeightAttributes; ++AttributeIndex) //-V621
{
auto SkinAttributeFeature = Async(EAsyncExecution::ThreadPool, [this, &MeshIn, &MeshOut, AttributeIndex]()
{
const FName AttribName = MeshIn.GetSkinWeightAttributeName(AttributeIndex);
UE::Geometry::FDynamicMeshVertexSkinWeightsAttribute* OutAttribute = MeshOut.Attributes()->GetSkinWeightsAttribute(AttribName);
checkSlow(OutAttribute != nullptr);
if (OutAttribute)
{
for (int32 VertexID : MeshOut.VertexIndicesItr())
{
SrcVertIDType SrcVertID = MyBase::ToSrcVertIDMap[VertexID];
UE::AnimationCore::FBoneWeights SkinWeight = MeshIn.GetVertexSkinWeight(AttributeIndex, SrcVertID);
OutAttribute->SetValue(VertexID, SkinWeight);
}
}
});
Pending.Add(MoveTemp(SkinAttributeFeature));
}
// populate bones attribute
const int NumBones = MeshIn.GetNumBones();
if (MeshIn.GetNumBones())
{
MeshOut.Attributes()->EnableBones(NumBones);
FDynamicMeshBoneNameAttribute* BoneNameAttrib = MeshOut.Attributes()->GetBoneNames();
FDynamicMeshBoneParentIndexAttribute* BoneParentIndices = MeshOut.Attributes()->GetBoneParentIndices();
FDynamicMeshBonePoseAttribute* BonePoses = MeshOut.Attributes()->GetBonePoses();
FDynamicMeshBoneColorAttribute* BoneColors = MeshOut.Attributes()->GetBoneColors();
auto BoneAttributeFeature = Async(EAsyncExecution::ThreadPool, [this, &MeshIn, &MeshOut, BoneNameAttrib, BoneParentIndices, BonePoses, BoneColors, NumBones]()
{
for (int32 BoneIdx = 0; BoneIdx < NumBones; ++BoneIdx)
{
BoneNameAttrib->SetValue(BoneIdx, MeshIn.GetBoneName(BoneIdx));
BoneParentIndices->SetValue(BoneIdx, MeshIn.GetBoneParentIndex(BoneIdx));
BonePoses->SetValue(BoneIdx, MeshIn.GetBonePose(BoneIdx));
BoneColors->SetValue(BoneIdx, MeshIn.GetBoneColor(BoneIdx));
}
});
Pending.Add(MoveTemp(BoneAttributeFeature));
}
// TODO: PolyGroup layers
// wait for all work to be done
for (TFuture<void>& Future : Pending)
{
Future.Wait();
}
}
// Access the attributes in the Source Mesh on a per-wedge granularity (each corner of each triangle is a wedge), and weld them based on strict equality when creating the overlay.
template<typename AttrType>
void PopulateOverlayFromWedgeAttr(typename TOverlayTraits<AttrType>::OverlayType* Overlay,
TFunctionRef<AttrType(const SrcWedgeIDType& WedgeID)> WedgeAttrs,
TFunctionRef<void(const SrcTriIDType& SrcTriID, SrcWedgeIDType& WID0, SrcWedgeIDType& WID1, SrcWedgeIDType& WID2)> TriToWedges,
bool bWeldIdenticalAttrs = true)
{
FDynamicMesh3& MeshOut = *Overlay->GetParentMesh();
TAttrWelder<AttrType> AttrWelder(Overlay);
for (int32 TriangleID : MeshOut.TriangleIndicesItr())
{
// skip overlay triangles that a prior method has populated.
// NB: this function may have been called after PopulateOverlayFromSharedAttr
if (Overlay->IsSetTriangle(TriangleID))
{
continue;
}
const SrcTriIDType& SrcTriID = MyBase::ToSrcTriIDMap[TriangleID];
SrcWedgeIDType WID[3];
TriToWedges(SrcTriID, WID[0], WID[1], WID[2]);
FIndex3i Tri = MeshOut.GetTriangle(TriangleID);
FIndex3i AttrTri;
for (int j = 0; j < 3; ++j)
{
const AttrType& AttrValue = WedgeAttrs(WID[j]);
if (bWeldIdenticalAttrs)
{
AttrTri[j] = AttrWelder.FindOrAddUnique(AttrValue, Tri[j]);
}
else
{
AttrTri[j] = Overlay->AppendElement(AttrValue);
}
}
Overlay->SetTriangle(TriangleID, AttrTri);
}
}
template <typename AttrType, typename SrcAttrIDType>
void PopulateOverlayFromSharedAttr(typename TOverlayTraits<AttrType>::OverlayType* Overlay,
const TArray<SrcAttrIDType>& SrcAttrIDs,
TFunctionRef<AttrType(const SrcAttrIDType& AttrID)> SharedAttrValues,
TFunctionRef<bool(const SrcTriIDType& SrcTriID, SrcAttrIDType& AttrID0, SrcAttrIDType& AttrID1, SrcAttrIDType& AttrID2)> AttrTriIndices)
{
FDynamicMesh3& MeshOut = *Overlay->GetParentMesh();
// return if no shared attributes.
if (SrcAttrIDs.Num() == 0)
{
return;
}
int32 MaxSrcAttrID = -1;
for (const SrcAttrIDType& SrcAttrID : SrcAttrIDs)
{
MaxSrcAttrID = FMath::Max(MaxSrcAttrID, (int32)SrcAttrID);
}
TArray<int32> FromSrcAttrIDMap;
FromSrcAttrIDMap.Init(-1, MaxSrcAttrID+1);
// copy attr values into the overlay - these are vertices in the attr mesh.
for (const SrcAttrIDType& SrcAttrID : SrcAttrIDs)
{
const AttrType AttrValue = SharedAttrValues(SrcAttrID);
const int32 NewIndex = Overlay->AppendElement(AttrValue);
int32 SrcAttrID32 = (int32)SrcAttrID;
FromSrcAttrIDMap[SrcAttrID32] = NewIndex;
}
// use the attr index buffer to make triangles in the overlay
for (int32 TriID : MeshOut.TriangleIndicesItr())
{
const SrcTriIDType SrcTriID = MyBase::ToSrcTriIDMap[TriID];
SrcAttrIDType SrcAttrTri[3];
bool bHasValidAttrTri = AttrTriIndices(SrcTriID, SrcAttrTri[0], SrcAttrTri[1], SrcAttrTri[2]);
if (!bHasValidAttrTri)
{
// don't set this tri in the overlay
continue;
}
// translate to Overlay indicies
FIndex3i AttrTri(FromSrcAttrIDMap[(int32)SrcAttrTri[0]],
FromSrcAttrIDMap[(int32)SrcAttrTri[1]],
FromSrcAttrIDMap[(int32)SrcAttrTri[2]]);
///-- We have to do some clean-up on the shared Attrs because the MeshIn format might support wild stuff.. --///
{
// MeshIn may attach multiple mesh vertices to the same attribute element. DynamicMesh does not.
// if we have already used this element for a different mesh vertex, split it.
const FIndex3i ParentTriangle = MeshOut.GetTriangle(TriID);
for (int i = 0; i < 3; ++i)
{
int32 ParentVID = Overlay->GetParentVertex(AttrTri[i]);
if (ParentVID != FDynamicMesh3::InvalidID && ParentVID != ParentTriangle[i])
{
const AttrType AttrValue = SharedAttrValues(SrcAttrTri[i]);
AttrTri[i] = Overlay->AppendElement(AttrValue);
}
}
// MeshIn may have degenerate attr tris. Dynamic Mesh does not.
// if the attr tri is degenerate we split the degenerate attr edge by adding two new attrs
// in its place, or if it is totally degenerate we add 3 new attrs
if (AttrTri[0] == AttrTri[1] && AttrTri[0] == AttrTri[2])
{
const AttrType AttrValue = SharedAttrValues(SrcAttrTri[0]);
AttrTri[0] = Overlay->AppendElement(AttrValue);
AttrTri[1] = Overlay->AppendElement(AttrValue);
AttrTri[2] = Overlay->AppendElement(AttrValue);
}
else
{
if (AttrTri[0] == AttrTri[1])
{
const AttrType AttrValue = SharedAttrValues(SrcAttrTri[0]);
AttrTri[0] = Overlay->AppendElement(AttrValue);
AttrTri[1] = Overlay->AppendElement(AttrValue);
}
if (AttrTri[0] == AttrTri[2])
{
const AttrType AttrValue = SharedAttrValues(SrcAttrTri[0]);
AttrTri[0] = Overlay->AppendElement(AttrValue);
AttrTri[2] = Overlay->AppendElement(AttrValue);
}
if (AttrTri[1] == AttrTri[2])
{
const AttrType AttrValue = SharedAttrValues(SrcAttrTri[1]);
AttrTri[1] = Overlay->AppendElement(AttrValue);
AttrTri[2] = Overlay->AppendElement(AttrValue);
}
}
}
// set the triangle in the overlay
Overlay->SetTriangle(TriID, AttrTri);
}
}
template <typename AttrType, typename SrcAttrIDType>
void PopulateOverlay(typename TOverlayTraits<AttrType>::OverlayType* Overlay,
const TArray<SrcAttrIDType>& SrcAttrIDs,
TFunctionRef<AttrType(const SrcAttrIDType& AttrID)> SharedAttrValues,
TFunctionRef<bool(const SrcTriIDType& SrcTriID, SrcAttrIDType& AttrID0, SrcAttrIDType& AttrID1, SrcAttrIDType& AttrID2)> AttrTriIndices,
TFunctionRef<AttrType(const SrcWedgeIDType& WegdeID)> WedgeAttrs,
TFunctionRef<void(const SrcTriIDType& SrcTriID, SrcWedgeIDType& WID0, SrcWedgeIDType& WID1, SrcWedgeIDType& WID2)> TriToWedges)
{
PopulateOverlayFromSharedAttr(Overlay, SrcAttrIDs, SharedAttrValues, AttrTriIndices);
// Populate the overlay tris that were left empty by the shared attrs
PopulateOverlayFromWedgeAttr(Overlay, WedgeAttrs, TriToWedges);
}
};
} } // end namespace UE::Geometry