Files
UnrealEngine/Engine/Source/Runtime/SkeletalMeshDescription/Private/SkeletalMeshAttributes.cpp
2025-05-18 13:04:45 +08:00

657 lines
20 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SkeletalMeshAttributes.h"
#include "MeshDescription.h"
namespace MeshAttribute
{
namespace Vertex
{
const FName SkinWeights("SkinWeights");
const FName ImportPointIndex("ImportPointIndex");
}
namespace Bone
{
const FName Name("Name");
const FName ParentIndex("ParentIndex");
const FName Pose("Pose");
const FName Color("Color");
}
namespace SourceGeometryPart
{
const FName Name("Name");
const FName VertexOffsetAndCount("VertexOffsetAndCount");
}
}
FName FSkeletalMeshAttributesShared::DefaultSkinWeightProfileName("Default");
FName FSkeletalMeshAttributesShared::BonesElementName("BonesElementName");
FName FSkeletalMeshAttributesShared::SourceGeometryPartElementName("SourceGeometryPartElementName");
static FString MorphTargetAttributeNamePrefix("Morph-");
static FString SkinWeightAttributeNamePrefix()
{
return MeshAttribute::Vertex::SkinWeights.ToString() + TEXT("-");
}
//
// FSkeletalMeshAttributes
//
FSkeletalMeshAttributes::FSkeletalMeshAttributes(FMeshDescription& InMeshDescription) :
FStaticMeshAttributes(InMeshDescription),
FSkeletalMeshAttributesShared(InMeshDescription)
{
if (MeshDescription.GetElements().Contains(BonesElementName))
{
BoneElements = MeshDescription.GetElements()[BonesElementName].Get();
}
if (MeshDescription.GetElements().Contains(SourceGeometryPartElementName))
{
SourceGeometryPartElements = MeshDescription.GetElements()[SourceGeometryPartElementName].Get();
}
}
void FSkeletalMeshAttributes::Register(bool bKeepExistingAttribute)
{
if (!MeshDescription.VertexAttributes().HasAttribute(MeshAttribute::Vertex::SkinWeights) || !bKeepExistingAttribute)
{
MeshDescription.VertexAttributes().RegisterAttribute<int32[]>(MeshAttribute::Vertex::SkinWeights, 1, 0, EMeshAttributeFlags::Mandatory);
}
if (MeshDescription.GetElements().Contains(BonesElementName) == false)
{
BoneElementsShared = BoneElements = MeshDescription.GetElements().Emplace(BonesElementName).Get();
}
if (!BoneAttributes().HasAttribute(MeshAttribute::Bone::Name) || !bKeepExistingAttribute)
{
BoneAttributes().RegisterAttribute<FName>(MeshAttribute::Bone::Name, 1, NAME_None, EMeshAttributeFlags::Mandatory);
}
if (!BoneAttributes().HasAttribute(MeshAttribute::Bone::ParentIndex) || !bKeepExistingAttribute)
{
BoneAttributes().RegisterAttribute<int32>(MeshAttribute::Bone::ParentIndex, 1, INDEX_NONE, EMeshAttributeFlags::Mandatory);
}
if (!BoneAttributes().HasAttribute(MeshAttribute::Bone::Pose) || !bKeepExistingAttribute)
{
BoneAttributes().RegisterAttribute<FTransform>(MeshAttribute::Bone::Pose, 1, FTransform::Identity, EMeshAttributeFlags::Mandatory);
}
// Call super class
FStaticMeshAttributes::Register(bKeepExistingAttribute);
}
void FSkeletalMeshAttributes::RegisterColorAttribute()
{
checkSlow(MeshDescription.GetElements().Contains(BonesElementName));
BoneAttributes().RegisterAttribute<FVector4f>(MeshAttribute::Bone::Color, 1, FVector4f(1.0f, 1.0f, 1.0f, 1.0f), EMeshAttributeFlags::Mandatory);
}
bool FSkeletalMeshAttributes::RegisterImportPointIndexAttribute()
{
return MeshDescription.VertexAttributes().RegisterAttribute<int32>(MeshAttribute::Vertex::ImportPointIndex, 1, INDEX_NONE).IsValid();
}
void FSkeletalMeshAttributes::UnregisterImportPointIndexAttribute()
{
return MeshDescription.VertexAttributes().UnregisterAttribute(MeshAttribute::Vertex::ImportPointIndex);
}
bool FSkeletalMeshAttributes::RegisterSkinWeightAttribute(const FName InProfileName)
{
if (!IsValidSkinWeightProfileName(InProfileName))
{
return false;
}
const FName AttributeName = CreateSkinWeightAttributeName(InProfileName);
if (!ensure(AttributeName.IsValid()))
{
return false;
}
if (MeshDescription.VertexAttributes().HasAttribute(AttributeName))
{
return true;
}
return MeshDescription.VertexAttributes().RegisterAttribute<int32[]>(AttributeName, 1, 0, EMeshAttributeFlags::None).IsValid();
}
bool FSkeletalMeshAttributes::UnregisterSkinWeightAttribute(const FName InProfileName)
{
if (!IsValidSkinWeightProfileName(InProfileName))
{
return false;
}
const FName AttributeName = CreateSkinWeightAttributeName(InProfileName);
if (!ensure(AttributeName.IsValid()))
{
return false;
}
// Attribute not there?
if (!MeshDescription.VertexAttributes().HasAttribute(AttributeName))
{
return false;
}
MeshDescription.VertexAttributes().UnregisterAttribute(AttributeName);
return true;
}
FSkinWeightsVertexAttributesRef FSkeletalMeshAttributes::GetVertexSkinWeights(const FName InProfileName)
{
return MeshDescription.VertexAttributes().GetAttributesRef<TArrayAttribute<int32>>(CreateSkinWeightAttributeName(InProfileName));
}
FSkinWeightsVertexAttributesRef FSkeletalMeshAttributes::GetVertexSkinWeightsFromAttributeName(const FName InAttributeName)
{
if (IsSkinWeightAttribute(InAttributeName))
{
return MeshDescription.VertexAttributes().GetAttributesRef<TArrayAttribute<int32>>(InAttributeName);
}
else
{
return {};
}
}
bool FSkeletalMeshAttributes::RegisterMorphTargetAttribute(
const FName InMorphTargetName,
const bool bIncludeNormals
)
{
if (InMorphTargetName.IsNone())
{
return false;
}
const FName AttributeName = CreateMorphTargetAttributeName(InMorphTargetName);
if (!ensure(AttributeName.IsValid()))
{
return false;
}
// Register position attribute if it doesn't already exist.
TAttributesSet<FVertexID>& VertexAttributes = MeshDescription.VertexAttributes();
bool bSuccess = VertexAttributes.HasAttribute(AttributeName);
if (!bSuccess)
{
bSuccess = VertexAttributes.RegisterAttribute<FVector3f>(AttributeName, 1, FVector3f::ZeroVector, EMeshAttributeFlags::None).IsValid();
}
TAttributesSet<FVertexInstanceID>& VertexInstanceAttributes = MeshDescription.VertexInstanceAttributes();
if (bSuccess && bIncludeNormals)
{
// Register normal attribute, if requested, if it fails, then we have to unregister the vertex attribute too.
bSuccess = VertexInstanceAttributes.RegisterAttribute<FVector3f>(AttributeName, 1, FVector3f::ZeroVector, EMeshAttributeFlags::None).IsValid();
if (!bSuccess)
{
VertexAttributes.UnregisterAttribute(AttributeName);
}
}
else
{
// Unregister normal attribute if it fails of if it's not needed anymore.
if (VertexInstanceAttributes.HasAttribute(AttributeName))
{
VertexInstanceAttributes.UnregisterAttribute(AttributeName);
}
}
return bSuccess;
}
bool FSkeletalMeshAttributes::UnregisterMorphTargetAttribute(const FName InMorphTargetName)
{
if (InMorphTargetName.IsNone())
{
return false;
}
const FName AttributeName = CreateMorphTargetAttributeName(InMorphTargetName);
if (!ensure(AttributeName.IsValid()))
{
return false;
}
// Attribute not there?
if (!MeshDescription.VertexAttributes().HasAttribute(AttributeName))
{
return false;
}
MeshDescription.VertexAttributes().UnregisterAttribute(AttributeName);
// VertexInstancesAttributes
if (MeshDescription.VertexInstanceAttributes().HasAttribute(AttributeName))
{
MeshDescription.VertexInstanceAttributes().UnregisterAttribute(AttributeName);
}
return true;
}
TVertexAttributesRef<FVector3f> FSkeletalMeshAttributes::GetVertexMorphPositionDelta(const FName InMorphTargetName)
{
return MeshDescription.VertexAttributes().GetAttributesRef<FVector3f>(CreateMorphTargetAttributeName(InMorphTargetName));
}
TVertexInstanceAttributesRef<FVector3f> FSkeletalMeshAttributes::GetVertexInstanceMorphNormalDelta(const FName InMorphTargetName)
{
return MeshDescription.VertexInstanceAttributes().GetAttributesRef<FVector3f>(CreateMorphTargetAttributeName(InMorphTargetName));
}
FSkeletalMeshAttributes::FBoneArray& FSkeletalMeshAttributes::Bones()
{
return static_cast<TMeshElementContainer<FBoneID>&>(BoneElements->Get());
}
TAttributesSet<FBoneID>& FSkeletalMeshAttributes::BoneAttributes()
{
return Bones().GetAttributes();
}
void FSkeletalMeshAttributes::ReserveNewBones(const int InNumBones)
{
BoneElements->Get().Reserve(InNumBones);
}
FBoneID FSkeletalMeshAttributes::CreateBone()
{
return BoneElements->Get().Add();
}
void FSkeletalMeshAttributes::CreateBone(const FBoneID BoneID)
{
BoneElements->Get().Insert(BoneID);
}
void FSkeletalMeshAttributes::DeleteBone(const FBoneID BoneID)
{
BoneElements->Get().Remove(BoneID);
}
FSkeletalMeshAttributes::FBoneNameAttributesRef FSkeletalMeshAttributes::GetBoneNames()
{
return BoneAttributes().GetAttributesRef<FName>(MeshAttribute::Bone::Name);
}
FSkeletalMeshAttributes::FBoneParentIndexAttributesRef FSkeletalMeshAttributes::GetBoneParentIndices()
{
return BoneAttributes().GetAttributesRef<int32>(MeshAttribute::Bone::ParentIndex);
}
FSkeletalMeshAttributes::FBonePoseAttributesRef FSkeletalMeshAttributes::GetBonePoses()
{
return BoneAttributes().GetAttributesRef<FTransform>(MeshAttribute::Bone::Pose);
}
FSkeletalMeshAttributes::FBoneColorAttributesRef FSkeletalMeshAttributes::GetBoneColors()
{
return BoneAttributes().GetAttributesRef<FVector4f>(MeshAttribute::Bone::Color);
}
void FSkeletalMeshAttributes::RegisterSourceGeometryPartsAttributes()
{
if (MeshDescription.GetElements().Contains(SourceGeometryPartElementName) == false)
{
SourceGeometryPartElementsShared = SourceGeometryPartElements = MeshDescription.GetElements().Emplace(SourceGeometryPartElementName).Get();
}
SourceGeometryPartAttributes().RegisterAttribute<FName>(MeshAttribute::SourceGeometryPart::Name, 1, NAME_None, EMeshAttributeFlags::Mandatory);
SourceGeometryPartAttributes().RegisterAttribute<int32[2]>(MeshAttribute::SourceGeometryPart::VertexOffsetAndCount, 1, {0}, EMeshAttributeFlags::Mandatory);
}
FSkeletalMeshAttributesShared::FSourceGeometryPartArray& FSkeletalMeshAttributes::SourceGeometryParts()
{
if (!ensure(HasSourceGeometryParts()))
{
static FSourceGeometryPartArray Empty;
return Empty;
}
return static_cast<FSourceGeometryPartArray&>(SourceGeometryPartElements->Get());
}
TAttributesSet<FSourceGeometryPartID>& FSkeletalMeshAttributes::SourceGeometryPartAttributes()
{
if (!ensure(HasSourceGeometryParts()))
{
static TAttributesSet<FSourceGeometryPartID> Empty;
return Empty;
}
return SourceGeometryParts().GetAttributes();
}
FSourceGeometryPartID FSkeletalMeshAttributes::CreateSourceGeometryPart()
{
if (!ensure(HasSourceGeometryParts()))
{
return {};
}
return SourceGeometryParts().Add();
}
void FSkeletalMeshAttributes::DeleteSourceGeometryPart(FSourceGeometryPartID InSourceGeometryPartID)
{
if (ensure(HasSourceGeometryParts()))
{
return SourceGeometryParts().Remove(InSourceGeometryPartID);
}
}
FSkeletalMeshAttributesShared::FSourceGeometryPartNameRef FSkeletalMeshAttributes::GetSourceGeometryPartNames()
{
if (!ensure(HasSourceGeometryParts()))
{
return FSourceGeometryPartNameRef{};
}
return SourceGeometryPartAttributes().GetAttributesRef<FName>(MeshAttribute::SourceGeometryPart::Name);
}
FSkeletalMeshAttributesShared::FSourceGeometryPartVertexOffsetAndCountRef FSkeletalMeshAttributes::GetSourceGeometryPartVertexOffsetAndCounts()
{
if (!ensure(HasSourceGeometryParts()))
{
return FSourceGeometryPartVertexOffsetAndCountRef{};
}
return SourceGeometryPartAttributes().GetAttributesRef<TArrayView<int32>>(MeshAttribute::SourceGeometryPart::VertexOffsetAndCount);
}
//
// FSkeletalMeshAttributesShared
//
FSkeletalMeshAttributesShared::FSkeletalMeshAttributesShared(const FMeshDescription& InMeshDescription) :
MeshDescriptionShared(InMeshDescription)
{
if (MeshDescriptionShared.GetElements().Contains(BonesElementName))
{
BoneElementsShared = MeshDescriptionShared.GetElements()[BonesElementName].Get();
}
if (MeshDescriptionShared.GetElements().Contains(SourceGeometryPartElementName))
{
SourceGeometryPartElementsShared = MeshDescriptionShared.GetElements()[SourceGeometryPartElementName].Get();
}
}
FSkinWeightsVertexAttributesConstRef FSkeletalMeshAttributesShared::GetVertexSkinWeights(const FName InProfileName) const
{
return MeshDescriptionShared.VertexAttributes().GetAttributesRef<TArrayAttribute<int32>>(CreateSkinWeightAttributeName(InProfileName));
}
FSkinWeightsVertexAttributesConstRef FSkeletalMeshAttributesShared::GetVertexSkinWeightsFromAttributeName(const FName InAttributeName) const
{
if (IsSkinWeightAttribute(InAttributeName))
{
return MeshDescriptionShared.VertexAttributes().GetAttributesRef<TArrayAttribute<int32>>(InAttributeName);
}
return {};
}
TArray<FName> FSkeletalMeshAttributesShared::GetSkinWeightProfileNames(const bool bInUserDefinedOnly) const
{
TArray<FName> AllAttributeNames;
MeshDescriptionShared.VertexAttributes().GetAttributeNames(AllAttributeNames);
TArray<FName> SkinWeightProfileNames;
bool bHasDefault = false;
for (const FName AttributeName: AllAttributeNames)
{
if (AttributeName == MeshAttribute::Vertex::SkinWeights)
{
bHasDefault = true;
}
else if (IsSkinWeightAttribute(AttributeName))
{
SkinWeightProfileNames.Add(GetProfileNameFromAttribute(AttributeName));
}
}
SkinWeightProfileNames.Sort([](const FName A, const FName B) -> bool { return A.FastLess(B); });
if (bHasDefault && !bInUserDefinedOnly)
{
SkinWeightProfileNames.Insert(DefaultSkinWeightProfileName, 0);
}
return SkinWeightProfileNames;
}
bool FSkeletalMeshAttributesShared::IsValidSkinWeightProfileName(const FName InProfileName)
{
return !InProfileName.IsNone() && !InProfileName.IsEqual(DefaultSkinWeightProfileName, ENameCase::IgnoreCase);
}
bool FSkeletalMeshAttributesShared::IsSkinWeightAttribute(const FName InAttributeName)
{
return InAttributeName == MeshAttribute::Vertex::SkinWeights ||
InAttributeName.ToString().StartsWith(SkinWeightAttributeNamePrefix());
}
FName FSkeletalMeshAttributesShared::GetProfileNameFromAttribute(const FName InAttributeName)
{
if (InAttributeName == MeshAttribute::Vertex::SkinWeights)
{
return DefaultSkinWeightProfileName;
}
const FString Prefix = SkinWeightAttributeNamePrefix();
if (InAttributeName.ToString().StartsWith(Prefix))
{
return FName(InAttributeName.ToString().Mid(Prefix.Len()));
}
else
{
return NAME_None;
}
}
FName FSkeletalMeshAttributesShared::CreateSkinWeightAttributeName(const FName InProfileName)
{
// If it's the default profile, then return the base skin weights attribute name.
if (InProfileName.IsNone() || InProfileName.IsEqual(FSkeletalMeshAttributesShared::DefaultSkinWeightProfileName, ENameCase::IgnoreCase))
{
return MeshAttribute::Vertex::SkinWeights;
}
return FName(SkinWeightAttributeNamePrefix() + InProfileName.ToString());
}
FName FSkeletalMeshAttributesShared::CreateMorphTargetAttributeName(const FName InMorphTargetName)
{
if (ensure(!InMorphTargetName.IsNone()))
{
return FName(MorphTargetAttributeNamePrefix + InMorphTargetName.ToString());
}
return NAME_None;
}
TArray<FName> FSkeletalMeshAttributesShared::GetMorphTargetNames() const
{
TArray<FName> AllAttributeNames;
MeshDescriptionShared.VertexAttributes().GetAttributeNames(AllAttributeNames);
TArray<FName> AllMorphTargetNames;
for (const FName AttributeName: AllAttributeNames)
{
if (IsMorphTargetAttribute(AttributeName))
{
AllMorphTargetNames.Add(GetMorphTargetNameFromAttribute(AttributeName));
}
}
return AllMorphTargetNames;
}
bool FSkeletalMeshAttributesShared::IsMorphTargetAttribute(const FName InAttributeName)
{
return InAttributeName.ToString().StartsWith(MorphTargetAttributeNamePrefix);
}
FName FSkeletalMeshAttributesShared::GetMorphTargetNameFromAttribute(const FName InAttributeName)
{
if (ensure(IsMorphTargetAttribute(InAttributeName)))
{
return FName(InAttributeName.ToString().Mid(MorphTargetAttributeNamePrefix.Len()));
}
return NAME_None;
}
TVertexAttributesConstRef<FVector3f> FSkeletalMeshAttributesShared::GetVertexMorphPositionDelta(const FName InMorphTargetName) const
{
return MeshDescriptionShared.VertexAttributes().GetAttributesRef<FVector3f>(CreateMorphTargetAttributeName(InMorphTargetName));
}
TVertexInstanceAttributesConstRef<FVector3f> FSkeletalMeshAttributesShared::GetVertexInstanceMorphNormalDelta(const FName InMorphTargetName) const
{
return MeshDescriptionShared.VertexInstanceAttributes().GetAttributesRef<FVector3f>(CreateMorphTargetAttributeName(InMorphTargetName));
}
bool FSkeletalMeshAttributesShared::HasMorphTargetPositionsAttribute(const FName InMorphTargetName) const
{
return MeshDescriptionShared.VertexAttributes().HasAttribute(CreateMorphTargetAttributeName(InMorphTargetName));
}
bool FSkeletalMeshAttributesShared::HasMorphTargetNormalsAttribute(const FName InMorphTargetName) const
{
return MeshDescriptionShared.VertexInstanceAttributes().HasAttribute(CreateMorphTargetAttributeName(InMorphTargetName));
}
bool FSkeletalMeshAttributesShared::HasBoneColorAttribute() const
{
return BoneAttributes().HasAttribute(MeshAttribute::Bone::Color);
}
bool FSkeletalMeshAttributesShared::HasBoneNameAttribute() const
{
return BoneAttributes().HasAttribute(MeshAttribute::Bone::Name);
}
bool FSkeletalMeshAttributesShared::HasBonePoseAttribute() const
{
return BoneAttributes().HasAttribute(MeshAttribute::Bone::Pose);
}
bool FSkeletalMeshAttributesShared::HasBoneParentIndexAttribute() const
{
return BoneAttributes().HasAttribute(MeshAttribute::Bone::ParentIndex);
}
const FSkeletalMeshAttributesShared::FBoneArray& FSkeletalMeshAttributesShared::Bones() const
{
return static_cast<const TMeshElementContainer<FBoneID>&>(BoneElementsShared->Get());
}
const TAttributesSet<FBoneID>& FSkeletalMeshAttributesShared::BoneAttributes() const
{
return Bones().GetAttributes();
}
int32 FSkeletalMeshAttributesShared::GetNumBones() const
{
return HasBones() ? BoneElementsShared->Get().Num() : 0;
}
bool FSkeletalMeshAttributesShared::IsBoneValid(const FBoneID BoneID) const
{
return BoneElementsShared->Get().IsValid(BoneID.GetValue());
}
FSkeletalMeshAttributesShared::FBoneNameAttributesConstRef FSkeletalMeshAttributesShared::GetBoneNames() const
{
return BoneAttributes().GetAttributesRef<FName>(MeshAttribute::Bone::Name);
}
FSkeletalMeshAttributesShared::FBoneParentIndexAttributesConstRef FSkeletalMeshAttributesShared::GetBoneParentIndices() const
{
return BoneAttributes().GetAttributesRef<int32>(MeshAttribute::Bone::ParentIndex);
}
FSkeletalMeshAttributesShared::FBonePoseAttributesConstRef FSkeletalMeshAttributesShared::GetBonePoses() const
{
return BoneAttributes().GetAttributesRef<FTransform>(MeshAttribute::Bone::Pose);
}
FSkeletalMeshAttributesShared::FBoneColorAttributesConstRef FSkeletalMeshAttributesShared::GetBoneColors() const
{
return BoneAttributes().GetAttributesRef<FVector4f>(MeshAttribute::Bone::Color);
}
const FSkeletalMeshAttributesShared::FSourceGeometryPartArray& FSkeletalMeshAttributesShared::SourceGeometryParts() const
{
if (!ensure(HasSourceGeometryParts()))
{
static FSourceGeometryPartArray Empty;
return Empty;
}
return static_cast<const FSourceGeometryPartArray&>(SourceGeometryPartElementsShared->Get());
}
const TAttributesSet<FSourceGeometryPartID>& FSkeletalMeshAttributesShared::SourceGeometryPartAttributes() const
{
if (!ensure(HasSourceGeometryParts()))
{
static TAttributesSet<FSourceGeometryPartID> Empty;
return Empty;
}
return SourceGeometryParts().GetAttributes();
}
int32 FSkeletalMeshAttributesShared::GetNumSourceGeometryParts() const
{
return HasSourceGeometryParts() ? SourceGeometryPartElementsShared->Get().Num() : 0;
}
bool FSkeletalMeshAttributesShared::IsSourceGeometryPartValid(const FSourceGeometryPartID InSourceGeometryPartID) const
{
if (!ensure(HasSourceGeometryParts()))
{
return false;
}
return SourceGeometryParts().IsValid(InSourceGeometryPartID);
}
FSkeletalMeshAttributesShared::FSourceGeometryPartNameConstRef FSkeletalMeshAttributesShared::GetSourceGeometryPartNames() const
{
if (!ensure(HasSourceGeometryParts()))
{
return FSourceGeometryPartNameConstRef{};
}
return SourceGeometryPartAttributes().GetAttributesRef<FName>(MeshAttribute::SourceGeometryPart::Name);
}
FSkeletalMeshAttributesShared::FSourceGeometryPartVertexOffsetAndCountConstRef FSkeletalMeshAttributesShared::GetSourceGeometryPartVertexOffsetAndCounts() const
{
if (!ensure(HasSourceGeometryParts()))
{
return FSourceGeometryPartVertexOffsetAndCountConstRef{};
}
return SourceGeometryPartAttributes().GetAttributesRef<TArrayView<int32>>(MeshAttribute::SourceGeometryPart::VertexOffsetAndCount);
}