Files
marc audy b5b72b7935 Disable false positives for
V557: Array overrun is possible. The 'GetOnPhaseStart' function processes value '[0..6]'. Inspect the first argument.
V621 Consider inspecting the 'for' operator. It's possible that the loop will be executed incorrectly or won't be executed at all.
V654 The condition loop is always false

[CL 29494726 by marc audy in ue5-main branch]
2023-11-06 14:04:14 -05: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