You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
-Removed extra spaces. #rb david.hill #rb halfdan.ingvarsson #rb alexis.matte #jira none #preflight 64343ef4b306e98c632d1bd1 #rnx [CL 24979483 by rinat abdrashitov in ue5-main branch]
460 lines
14 KiB
C++
460 lines
14 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "ConversionUtils/SkinnedMeshToDynamicMesh.h"
|
|
|
|
|
|
#include "Components/SkinnedMeshComponent.h"
|
|
#include "CoreMinimal.h"
|
|
#include "DynamicMesh/DynamicMesh3.h"
|
|
#include "DynamicMesh/DynamicMeshAttributeSet.h"
|
|
#include "Engine/SkinnedAssetCommon.h"
|
|
#include "Engine/SkinnedAsset.h"
|
|
#include "SkeletalRenderPublic.h"
|
|
#include "ToDynamicMesh.h"
|
|
#include "Rendering/SkeletalMeshRenderData.h"
|
|
#include "BoneWeights.h"
|
|
#include "SkeletalMeshAttributes.h"
|
|
|
|
namespace UE
|
|
{
|
|
namespace Conversion
|
|
{
|
|
|
|
|
|
// Wrapper that allows us to use ToDynamicMesh converter
|
|
class FSkinnedComponentWrapper
|
|
{
|
|
public:
|
|
|
|
typedef int32 TriIDType;
|
|
typedef int32 VertIDType;
|
|
typedef int32 WedgeIDType;
|
|
|
|
typedef int32 UVIDType;
|
|
typedef int32 NormalIDType;
|
|
typedef int32 ColorIDType;
|
|
|
|
|
|
FSkinnedComponentWrapper(USkinnedMeshComponent& SkinnedMeshComponent, int32 RequestedLOD, bool UseDisabledSections, bool bWantTangents)
|
|
: Component(&SkinnedMeshComponent)
|
|
, LOD(RequestedLOD)
|
|
, bUseDisabledSections(UseDisabledSections)
|
|
, bHasTangents(bWantTangents)
|
|
, Asset(SkinnedMeshComponent.GetSkinnedAsset())
|
|
, SrcLODInfo(SkinnedMeshComponent.GetSkinnedAsset()->GetLODInfo(RequestedLOD))
|
|
, SkeletalMeshRenderData(SkinnedMeshComponent.MeshObject->GetSkeletalMeshRenderData())
|
|
, IndexBuffer(SkinnedMeshComponent.MeshObject->GetSkeletalMeshRenderData().LODRenderData[RequestedLOD].MultiSizeIndexContainer.GetIndexBuffer())
|
|
|
|
{
|
|
|
|
// This step does the actual skinning (and isn't as const as the api suggests..)
|
|
Component->GetCPUSkinnedVertices(SkinnedVertices, RequestedLOD);
|
|
const FSkeletalMeshLODRenderData& LODData = SkeletalMeshRenderData.LODRenderData[RequestedLOD];
|
|
|
|
|
|
const int32 NumSections = LODData.RenderSections.Num();
|
|
|
|
auto SkipSection = [&](const FSkelMeshRenderSection& SkelMeshSection)
|
|
{
|
|
return ( !bUseDisabledSections && !SkinnedMeshComponent.IsMaterialSectionShown(SkelMeshSection.MaterialIndex, RequestedLOD));
|
|
};
|
|
|
|
// pre-compute number of tris & verts
|
|
int32 NumTris = 0;
|
|
int32 NumVerts = 0;
|
|
{
|
|
|
|
for (int32 SectionIndex = 0; SectionIndex < NumSections; ++SectionIndex)
|
|
{
|
|
const FSkelMeshRenderSection& SkelMeshSection = LODData.RenderSections[SectionIndex];
|
|
|
|
if (SkipSection(SkelMeshSection))
|
|
{
|
|
continue;
|
|
}
|
|
NumTris += SkelMeshSection.NumTriangles;
|
|
NumVerts += SkelMeshSection.NumVertices;
|
|
}
|
|
}
|
|
|
|
// construct a list of all valid VertIDs for this mesh.
|
|
VertIDs.Reserve(NumVerts);
|
|
for (const FSkelMeshRenderSection& Section : LODData.RenderSections)
|
|
{
|
|
if (SkipSection(Section))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const int32 BaseVertexIndex = static_cast<int32>( Section.BaseVertexIndex);
|
|
const int32 NumSectionVtx = static_cast<int32>( Section.NumVertices );
|
|
for (int32 VtxIndex = BaseVertexIndex; VtxIndex < NumSectionVtx + BaseVertexIndex; ++VtxIndex)
|
|
{
|
|
VertIDs.Add(VtxIndex);
|
|
}
|
|
}
|
|
|
|
|
|
// generate vertex weights and remap the indices
|
|
const FSkinWeightVertexBuffer* SkinWeightBuffer = Component->GetSkinWeightBuffer(LOD);
|
|
const bool bSkinWeightsValid = SkinWeightBuffer && SkinWeightBuffer->GetDataVertexBuffer() && SkinWeightBuffer->GetDataVertexBuffer()->IsInitialized() && SkinWeightBuffer->GetDataVertexBuffer()->GetNumVertices() > 0;
|
|
|
|
if (bSkinWeightsValid)
|
|
{
|
|
SkinWeights.Reserve(NumVerts);
|
|
for (const FSkelMeshRenderSection& Section : LODData.RenderSections)
|
|
{
|
|
if (SkipSection(Section))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const int32 BaseVertexIndex = static_cast<int32>(Section.BaseVertexIndex);
|
|
const int32 NumSectionVtx = static_cast<int32>(Section.NumVertices);
|
|
for (int32 VtxIndex = BaseVertexIndex; VtxIndex < NumSectionVtx + BaseVertexIndex; ++VtxIndex)
|
|
{
|
|
FSkinWeightInfo SrcWeights = SkinWeightBuffer->GetVertexSkinWeights(VtxIndex);
|
|
|
|
for (int32 Idx = 0; Idx < MAX_TOTAL_INFLUENCES; ++Idx)
|
|
{
|
|
const int32 SectionBoneIdx = static_cast<int32>(SrcWeights.InfluenceBones[Idx]);
|
|
if (ensure(SectionBoneIdx < Section.BoneMap.Num()))
|
|
{
|
|
SrcWeights.InfluenceBones[Idx] = Section.BoneMap[SectionBoneIdx];
|
|
}
|
|
}
|
|
SkinWeights.Add(SrcWeights);
|
|
}
|
|
}
|
|
}
|
|
|
|
// construct list of all valid TriIDs for this mesh.
|
|
TriIDs.Reserve(NumTris);
|
|
TriIDtoSectionID.Init(-1, NumTris);
|
|
for (int32 s = 0; s < NumSections; ++s)
|
|
{
|
|
const FSkelMeshRenderSection& Section = LODData.RenderSections[s];
|
|
if (SkipSection(Section))
|
|
{
|
|
continue;
|
|
}
|
|
const int32 BaseIndex = Section.BaseIndex;
|
|
const int32 BaseTriIdx = BaseIndex / 3;
|
|
const int32 NumSectionTris = static_cast<int32>(Section.NumTriangles);
|
|
for (int32 j = BaseTriIdx; j < BaseTriIdx + NumSectionTris; ++j)
|
|
{
|
|
TriIDs.Add(j);
|
|
TriIDtoSectionID[j] = s;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
int32 NumTris() const
|
|
{
|
|
return TriIDs.Num();
|
|
}
|
|
int32 NumVerts() const
|
|
{
|
|
return VertIDs.Num();
|
|
}
|
|
|
|
int32 NumUVLayers() const
|
|
{
|
|
const FSkeletalMeshLODRenderData& LODData = SkeletalMeshRenderData.LODRenderData[LOD];
|
|
return static_cast<int32>( LODData.StaticVertexBuffers.StaticMeshVertexBuffer.GetNumTexCoords() );
|
|
}
|
|
|
|
// --"Vertex Buffer" info
|
|
const TArray<VertIDType>& GetVertIDs() const
|
|
{
|
|
return VertIDs;
|
|
}
|
|
|
|
FVector3d GetPosition(VertIDType VtxID) const
|
|
{
|
|
return static_cast<FVector3d>(SkinnedVertices[VtxID].Position);
|
|
}
|
|
|
|
// --"Index Buffer" info
|
|
const TArray<TriIDType>& GetTriIDs() const
|
|
{
|
|
return TriIDs;
|
|
}
|
|
|
|
bool GetTri(TriIDType TriID, VertIDType& VID0, VertIDType& VID1, VertIDType& VID2) const
|
|
{
|
|
int32 Offset = TriID * 3;
|
|
|
|
// index buffer for this LOD.
|
|
const FRawStaticIndexBuffer16or32Interface& Indices = *IndexBuffer;
|
|
auto GetVID = [&Indices](int32 i){ return static_cast<VertIDType>(Indices.Get(i));};
|
|
|
|
VID0 = GetVID( Offset );
|
|
VID1 = GetVID( Offset + 1);
|
|
VID2 = GetVID( Offset + 2);
|
|
|
|
// maybe check IndexBuffer.Num() > Offset+2 ?
|
|
return true;
|
|
}
|
|
|
|
|
|
bool HasNormals() const { return true; }
|
|
bool HasTangents() const { return bHasTangents; }
|
|
bool HasBiTangents() const { return bHasTangents; }
|
|
|
|
bool HasColors() const
|
|
{
|
|
const FSkeletalMeshLODRenderData& LODData = SkeletalMeshRenderData.LODRenderData[LOD];
|
|
return (LODData.StaticVertexBuffers.ColorVertexBuffer.IsInitialized() && LODData.StaticVertexBuffers.ColorVertexBuffer.GetNumVertices() > 0);
|
|
}
|
|
|
|
//-- Access to per-wedge attributes --//
|
|
void GetWedgeIDs(const TriIDType& TriID, WedgeIDType& WID0, WedgeIDType& WID1, WedgeIDType& WID2) const
|
|
{
|
|
int32 Offset = 3 * TriID;
|
|
WID0 = Offset;
|
|
WID1 = Offset + 1;
|
|
WID2 = Offset + 2;
|
|
}
|
|
|
|
FVector2f GetWedgeUV(int32 UVLayerIndex, WedgeIDType WID) const
|
|
{
|
|
const FFinalSkinVertex& SkinnedVertex = GetWedgeVertexInstance(WID);
|
|
return static_cast<FVector2f>( SkinnedVertex.TextureCoordinates[UVLayerIndex] );
|
|
}
|
|
|
|
FVector3f GetWedgeNormal(WedgeIDType WID) const
|
|
{
|
|
const FFinalSkinVertex& SkinnedVertex = GetWedgeVertexInstance(WID);
|
|
return SkinnedVertex.TangentZ.ToFVector3f();
|
|
}
|
|
|
|
FVector3f GetWedgeTangent(WedgeIDType WID) const
|
|
{
|
|
const FFinalSkinVertex& SkinnedVertex = GetWedgeVertexInstance(WID);
|
|
return SkinnedVertex.TangentX.ToFVector3f();
|
|
}
|
|
|
|
FVector3f GetWedgeBiTangent(WedgeIDType WID) const
|
|
{
|
|
const FFinalSkinVertex& SkinnedVertex = GetWedgeVertexInstance(WID);
|
|
const FVector3f TangentX = SkinnedVertex.TangentX.ToFVector3f();
|
|
const FVector3f TangentZ = SkinnedVertex.TangentZ.ToFVector3f();
|
|
const float OrientationSign = SkinnedVertex.TangentZ.ToFVector4f().W;
|
|
const FVector3f TangentY = (TangentZ.Cross(TangentX)).GetSafeNormal() * OrientationSign;
|
|
return TangentY;
|
|
}
|
|
|
|
FVector4f GetWedgeColor(WedgeIDType WID) const
|
|
{
|
|
FLinearColor LinearColor = FLinearColor::White;
|
|
if (HasColors())
|
|
{
|
|
const int32 VID = GetVertID(WID);
|
|
const FSkeletalMeshLODRenderData& LODData = SkeletalMeshRenderData.LODRenderData[LOD];
|
|
const FColor& Color = LODData.StaticVertexBuffers.ColorVertexBuffer.VertexColor(VID);
|
|
LinearColor = Color.ReinterpretAsLinear();
|
|
}
|
|
return FVector4f(LinearColor.R, LinearColor.G, LinearColor.B, LinearColor.A);
|
|
}
|
|
|
|
|
|
int32 GetMaterialIndex(TriIDType TriID) const
|
|
{
|
|
int32 SectionID = TriIDtoSectionID[TriID];
|
|
check(SectionID > -1);
|
|
|
|
const FSkeletalMeshLODRenderData& LODData = SkeletalMeshRenderData.LODRenderData[LOD];
|
|
const FSkelMeshRenderSection& Section = LODData.RenderSections[SectionID];
|
|
|
|
int32 MaterialIndex = Section.MaterialIndex;
|
|
// use the remapping of material indices if there is a valid value
|
|
if (SrcLODInfo->LODMaterialMap.IsValidIndex(SectionID) && SrcLODInfo->LODMaterialMap[SectionID] != INDEX_NONE)
|
|
{
|
|
MaterialIndex = FMath::Clamp<int32>(SrcLODInfo->LODMaterialMap[SectionID], 0, Asset->GetMaterials().Num() - 1);
|
|
}
|
|
|
|
return MaterialIndex;
|
|
}
|
|
|
|
int32 NumSkinWeightAttributes() const
|
|
{
|
|
const bool bSkinWeightsValid = SkinWeights.Num() == SkinnedVertices.Num();
|
|
|
|
return static_cast<int32>(bSkinWeightsValid);
|
|
}
|
|
|
|
UE::AnimationCore::FBoneWeights GetVertexSkinWeight(int32 SkinWeightAttributeIndex, VertIDType VertexID) const
|
|
{
|
|
checkfSlow(SkinWeightAttributeIndex == 0, TEXT("USkinnedMeshComponent can have at most one skin weight attribute"));
|
|
checkfSlow(VertexID < SkinWeights.Num() && VertexID >= 0, TEXT("Invalid vertex id"));
|
|
|
|
const FSkinWeightInfo& WeightInfo = SkinWeights[VertexID];
|
|
|
|
const UE::AnimationCore::FBoneWeights Weights = UE::AnimationCore::FBoneWeights::Create(WeightInfo.InfluenceBones, WeightInfo.InfluenceWeights);
|
|
|
|
return Weights;
|
|
}
|
|
|
|
FName GetSkinWeightAttributeName(int32 SkinWeightAttributeIndex) const
|
|
{
|
|
checkfSlow(SkinWeightAttributeIndex == 0, TEXT("USkinnedMeshComponent can have at most one skin weight attribute"));
|
|
|
|
return FSkeletalMeshAttributes::DefaultSkinWeightProfileName;
|
|
};
|
|
|
|
|
|
int32 GetNumBones() const
|
|
{
|
|
return Asset->GetRefSkeleton().GetRawBoneNum();
|
|
}
|
|
|
|
FName GetBoneName(int32 BoneIdx) const
|
|
{
|
|
if (ensure(BoneIdx >= 0 && BoneIdx < GetNumBones()))
|
|
{
|
|
return Asset->GetRefSkeleton().GetRawRefBoneInfo()[BoneIdx].Name;
|
|
}
|
|
|
|
return NAME_None;
|
|
}
|
|
|
|
int32 GetBoneParentIndex(int32 BoneIdx) const
|
|
{
|
|
if (ensure(BoneIdx >= 0 && BoneIdx < GetNumBones()))
|
|
{
|
|
return Asset->GetRefSkeleton().GetRawRefBoneInfo()[BoneIdx].ParentIndex;
|
|
}
|
|
|
|
return INDEX_NONE;
|
|
}
|
|
|
|
FTransform GetBonePose(int32 BoneIdx) const
|
|
{
|
|
if (ensure(BoneIdx >= 0 && BoneIdx < GetNumBones()))
|
|
{
|
|
return Asset->GetRefSkeleton().GetRawRefBonePose()[BoneIdx];
|
|
}
|
|
|
|
return FTransform::Identity;
|
|
}
|
|
|
|
FVector4f GetBoneColor(int32 BoneIdx) const
|
|
{
|
|
return FVector4f::One();
|
|
}
|
|
|
|
//-- null implementation of shared attributes: Skeletal mesh model doesn't use these --//
|
|
const TArray<int32>& GetUVIDs(int32 LayerID) const { return EmptyArray; }
|
|
FVector2f GetUV(int32 LayerID, UVIDType UVID) const { check(0); return FVector2f(); }
|
|
bool GetUVTri(int32 LayerID, const TriIDType&, UVIDType& ID0, UVIDType& ID1, UVIDType& ID2) const { ID0 = ID1 = ID2 = UVIDType(-1); return false; }
|
|
|
|
const TArray<int32>& GetNormalIDs() const { return EmptyArray; }
|
|
FVector3f GetNormal(NormalIDType ID) const { check(0); return FVector3f(); }
|
|
bool GetNormalTri(const TriIDType&, NormalIDType& ID0, NormalIDType& ID1, NormalIDType& ID2) const { ID0 = ID1 = ID2 = NormalIDType(-1); return false; }
|
|
|
|
const TArray<int32>& GetTangentIDs() const { return EmptyArray; }
|
|
FVector3f GetTangent(NormalIDType ID) const { check(0); return FVector3f(); }
|
|
bool GetTangentTri(const TriIDType&, NormalIDType& ID0, NormalIDType& ID1, NormalIDType& ID2) const { ID0 = ID1 = ID2 = NormalIDType(-1); return false; }
|
|
|
|
|
|
const TArray<int32>& GetBiTangentIDs() const { return EmptyArray; }
|
|
FVector3f GetBiTangent(NormalIDType ID) const { check(0); return FVector3f(); }
|
|
bool GetBiTangentTri(const TriIDType&, NormalIDType& ID0, NormalIDType& ID1, NormalIDType& ID2) const { ID0 = ID1 = ID2 = NormalIDType(-1); return false; }
|
|
|
|
const TArray<int32>& GetColorIDs() const { return EmptyArray; }
|
|
FVector4f GetColor(ColorIDType ID) const { check(0); return FVector4f(); }
|
|
bool GetColorTri(const TriIDType&, ColorIDType& ID0, ColorIDType& ID1, ColorIDType& ID2) const { ID0 = ID1 = ID2 = ColorIDType(-1); return false; }
|
|
|
|
int32 NumWeightMapLayers() const { return 0; }
|
|
float GetVertexWeight(int32 WeightMapIndex, int32 SrcVertID) const { return 0; }
|
|
FName GetWeightMapName(int32 WeightMapIndex) const { return FName(); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
inline const FFinalSkinVertex& GetWedgeVertexInstance(WedgeIDType WID) const
|
|
{
|
|
int32 VertID = GetVertID(WID);
|
|
return SkinnedVertices[VertID];
|
|
}
|
|
|
|
// Convert the WedgeID the ID of the corresponding position vertex.
|
|
inline const VertIDType GetVertID(int32 WedgeID) const
|
|
{
|
|
const FRawStaticIndexBuffer16or32Interface& Indices = *IndexBuffer;
|
|
return static_cast<VertIDType>(Indices.Get(WedgeID));
|
|
}
|
|
|
|
private:
|
|
|
|
TArray<FFinalSkinVertex> SkinnedVertices;
|
|
USkinnedMeshComponent* Component;
|
|
int32 LOD;
|
|
bool bUseDisabledSections;
|
|
bool bHasTangents;
|
|
|
|
USkinnedAsset* Asset;
|
|
const FSkeletalMeshLODInfo* SrcLODInfo;
|
|
const FSkeletalMeshRenderData& SkeletalMeshRenderData;
|
|
const FRawStaticIndexBuffer16or32Interface* IndexBuffer;
|
|
|
|
|
|
TArray<int32> VertIDs;
|
|
TArray<int32> TriIDs;
|
|
|
|
TArray<int32> TriIDtoSectionID;
|
|
TArray<int32> EmptyArray;
|
|
|
|
TArray<FSkinWeightInfo> SkinWeights;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void SkinnedMeshComponentToDynamicMesh(USkinnedMeshComponent& SkinnedMeshComponent, Geometry::FDynamicMesh3& MeshOut, int32 RequestedLOD, bool bWantTangents)
|
|
{
|
|
|
|
MeshOut.Clear();
|
|
|
|
const int32 NumLODs = SkinnedMeshComponent.GetNumLODs();
|
|
const bool bIsValidSkinnedMeshComponent = SkinnedMeshComponent.MeshObject && SkinnedMeshComponent.IsVisible();
|
|
|
|
if (!bIsValidSkinnedMeshComponent)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (RequestedLOD < 0 || RequestedLOD > NumLODs - 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!SkinnedMeshComponent.GetSkinnedAsset())
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
constexpr bool bUseDisabledSections = false;
|
|
FSkinnedComponentWrapper SkinnedComponentWrapper(SkinnedMeshComponent, RequestedLOD, bUseDisabledSections, bWantTangents);
|
|
|
|
// do the actual conversion.
|
|
{
|
|
constexpr bool bCopyTangents = true;
|
|
auto TriToGroupID = [&SkinnedComponentWrapper](const int32& SrcTriID)->int32 { return (SkinnedComponentWrapper.GetMaterialIndex(SrcTriID) + 1); };
|
|
auto TriToMaterialID = [&SkinnedComponentWrapper](const int32& SrcTriID)->int32 { return SkinnedComponentWrapper.GetMaterialIndex(SrcTriID); };
|
|
UE::Geometry::TToDynamicMesh<FSkinnedComponentWrapper> SkinnedComponentConverter;
|
|
|
|
SkinnedComponentConverter.Convert(MeshOut, SkinnedComponentWrapper, TriToGroupID, TriToMaterialID, bCopyTangents);
|
|
}
|
|
|
|
}
|
|
|
|
} // end namespace Conversion
|
|
} // end namespace UE
|
|
|