// 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(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(MeshAttribute::Bone::Name, 1, NAME_None, EMeshAttributeFlags::Mandatory); } if (!BoneAttributes().HasAttribute(MeshAttribute::Bone::ParentIndex) || !bKeepExistingAttribute) { BoneAttributes().RegisterAttribute(MeshAttribute::Bone::ParentIndex, 1, INDEX_NONE, EMeshAttributeFlags::Mandatory); } if (!BoneAttributes().HasAttribute(MeshAttribute::Bone::Pose) || !bKeepExistingAttribute) { BoneAttributes().RegisterAttribute(MeshAttribute::Bone::Pose, 1, FTransform::Identity, EMeshAttributeFlags::Mandatory); } // Call super class FStaticMeshAttributes::Register(bKeepExistingAttribute); } void FSkeletalMeshAttributes::RegisterColorAttribute() { checkSlow(MeshDescription.GetElements().Contains(BonesElementName)); BoneAttributes().RegisterAttribute(MeshAttribute::Bone::Color, 1, FVector4f(1.0f, 1.0f, 1.0f, 1.0f), EMeshAttributeFlags::Mandatory); } bool FSkeletalMeshAttributes::RegisterImportPointIndexAttribute() { return MeshDescription.VertexAttributes().RegisterAttribute(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(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>(CreateSkinWeightAttributeName(InProfileName)); } FSkinWeightsVertexAttributesRef FSkeletalMeshAttributes::GetVertexSkinWeightsFromAttributeName(const FName InAttributeName) { if (IsSkinWeightAttribute(InAttributeName)) { return MeshDescription.VertexAttributes().GetAttributesRef>(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& VertexAttributes = MeshDescription.VertexAttributes(); bool bSuccess = VertexAttributes.HasAttribute(AttributeName); if (!bSuccess) { bSuccess = VertexAttributes.RegisterAttribute(AttributeName, 1, FVector3f::ZeroVector, EMeshAttributeFlags::None).IsValid(); } TAttributesSet& 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(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 FSkeletalMeshAttributes::GetVertexMorphPositionDelta(const FName InMorphTargetName) { return MeshDescription.VertexAttributes().GetAttributesRef(CreateMorphTargetAttributeName(InMorphTargetName)); } TVertexInstanceAttributesRef FSkeletalMeshAttributes::GetVertexInstanceMorphNormalDelta(const FName InMorphTargetName) { return MeshDescription.VertexInstanceAttributes().GetAttributesRef(CreateMorphTargetAttributeName(InMorphTargetName)); } FSkeletalMeshAttributes::FBoneArray& FSkeletalMeshAttributes::Bones() { return static_cast&>(BoneElements->Get()); } TAttributesSet& 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(MeshAttribute::Bone::Name); } FSkeletalMeshAttributes::FBoneParentIndexAttributesRef FSkeletalMeshAttributes::GetBoneParentIndices() { return BoneAttributes().GetAttributesRef(MeshAttribute::Bone::ParentIndex); } FSkeletalMeshAttributes::FBonePoseAttributesRef FSkeletalMeshAttributes::GetBonePoses() { return BoneAttributes().GetAttributesRef(MeshAttribute::Bone::Pose); } FSkeletalMeshAttributes::FBoneColorAttributesRef FSkeletalMeshAttributes::GetBoneColors() { return BoneAttributes().GetAttributesRef(MeshAttribute::Bone::Color); } void FSkeletalMeshAttributes::RegisterSourceGeometryPartsAttributes() { if (MeshDescription.GetElements().Contains(SourceGeometryPartElementName) == false) { SourceGeometryPartElementsShared = SourceGeometryPartElements = MeshDescription.GetElements().Emplace(SourceGeometryPartElementName).Get(); } SourceGeometryPartAttributes().RegisterAttribute(MeshAttribute::SourceGeometryPart::Name, 1, NAME_None, EMeshAttributeFlags::Mandatory); SourceGeometryPartAttributes().RegisterAttribute(MeshAttribute::SourceGeometryPart::VertexOffsetAndCount, 1, {0}, EMeshAttributeFlags::Mandatory); } FSkeletalMeshAttributesShared::FSourceGeometryPartArray& FSkeletalMeshAttributes::SourceGeometryParts() { if (!ensure(HasSourceGeometryParts())) { static FSourceGeometryPartArray Empty; return Empty; } return static_cast(SourceGeometryPartElements->Get()); } TAttributesSet& FSkeletalMeshAttributes::SourceGeometryPartAttributes() { if (!ensure(HasSourceGeometryParts())) { static TAttributesSet 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(MeshAttribute::SourceGeometryPart::Name); } FSkeletalMeshAttributesShared::FSourceGeometryPartVertexOffsetAndCountRef FSkeletalMeshAttributes::GetSourceGeometryPartVertexOffsetAndCounts() { if (!ensure(HasSourceGeometryParts())) { return FSourceGeometryPartVertexOffsetAndCountRef{}; } return SourceGeometryPartAttributes().GetAttributesRef>(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>(CreateSkinWeightAttributeName(InProfileName)); } FSkinWeightsVertexAttributesConstRef FSkeletalMeshAttributesShared::GetVertexSkinWeightsFromAttributeName(const FName InAttributeName) const { if (IsSkinWeightAttribute(InAttributeName)) { return MeshDescriptionShared.VertexAttributes().GetAttributesRef>(InAttributeName); } return {}; } TArray FSkeletalMeshAttributesShared::GetSkinWeightProfileNames(const bool bInUserDefinedOnly) const { TArray AllAttributeNames; MeshDescriptionShared.VertexAttributes().GetAttributeNames(AllAttributeNames); TArray 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 FSkeletalMeshAttributesShared::GetMorphTargetNames() const { TArray AllAttributeNames; MeshDescriptionShared.VertexAttributes().GetAttributeNames(AllAttributeNames); TArray 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 FSkeletalMeshAttributesShared::GetVertexMorphPositionDelta(const FName InMorphTargetName) const { return MeshDescriptionShared.VertexAttributes().GetAttributesRef(CreateMorphTargetAttributeName(InMorphTargetName)); } TVertexInstanceAttributesConstRef FSkeletalMeshAttributesShared::GetVertexInstanceMorphNormalDelta(const FName InMorphTargetName) const { return MeshDescriptionShared.VertexInstanceAttributes().GetAttributesRef(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&>(BoneElementsShared->Get()); } const TAttributesSet& 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(MeshAttribute::Bone::Name); } FSkeletalMeshAttributesShared::FBoneParentIndexAttributesConstRef FSkeletalMeshAttributesShared::GetBoneParentIndices() const { return BoneAttributes().GetAttributesRef(MeshAttribute::Bone::ParentIndex); } FSkeletalMeshAttributesShared::FBonePoseAttributesConstRef FSkeletalMeshAttributesShared::GetBonePoses() const { return BoneAttributes().GetAttributesRef(MeshAttribute::Bone::Pose); } FSkeletalMeshAttributesShared::FBoneColorAttributesConstRef FSkeletalMeshAttributesShared::GetBoneColors() const { return BoneAttributes().GetAttributesRef(MeshAttribute::Bone::Color); } const FSkeletalMeshAttributesShared::FSourceGeometryPartArray& FSkeletalMeshAttributesShared::SourceGeometryParts() const { if (!ensure(HasSourceGeometryParts())) { static FSourceGeometryPartArray Empty; return Empty; } return static_cast(SourceGeometryPartElementsShared->Get()); } const TAttributesSet& FSkeletalMeshAttributesShared::SourceGeometryPartAttributes() const { if (!ensure(HasSourceGeometryParts())) { static TAttributesSet 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(MeshAttribute::SourceGeometryPart::Name); } FSkeletalMeshAttributesShared::FSourceGeometryPartVertexOffsetAndCountConstRef FSkeletalMeshAttributesShared::GetSourceGeometryPartVertexOffsetAndCounts() const { if (!ensure(HasSourceGeometryParts())) { return FSourceGeometryPartVertexOffsetAndCountConstRef{}; } return SourceGeometryPartAttributes().GetAttributesRef>(MeshAttribute::SourceGeometryPart::VertexOffsetAndCount); }