// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= GeometryCollection.cpp: FGeometryCollection methods. =============================================================================*/ #include "GeometryCollection/Facades/CollectionVertexBoneWeightsFacade.h" #include "CoreMinimal.h" #include "MeshAttributes.h" #include "GeometryCollection/Facades/CollectionKinematicBindingFacade.h" #include "GeometryCollection/GeometryCollection.h" DEFINE_LOG_CATEGORY_STATIC(LogDataflowVertexBoneWeightsFacade, Log, All); namespace GeometryCollection::Facades { // Attributes const FName FVertexBoneWeightsFacade::BoneWeightsAttributeName = "BoneWeights"; const FName FVertexBoneWeightsFacade::BoneIndicesAttributeName = "BoneIndices"; const FName FVertexBoneWeightsFacade::KinematicWeightAttributeName = "KinematicWeight"; const FName FVertexBoneWeightsFacade::SkeletalMeshAttributeName = "SkeletalMesh"; const FName FVertexBoneWeightsFacade::GeometryLODAttributeName = "GeometryLOD"; // Deprecated const FName FVertexBoneWeightsFacade::DeprecatedBoneIndicesAttributeName = "BoneWeightsIndex"; const FName FVertexBoneWeightsFacade::DeprecatedKinematicFlagAttributeName = "Kinematic"; FVertexBoneWeightsFacade::FVertexBoneWeightsFacade(FManagedArrayCollection& InCollection, const bool bInInternalWeights) : ConstCollection(InCollection) , Collection(&InCollection) , BoneIndicesAttribute(InCollection, BoneIndicesAttributeName, FGeometryCollection::VerticesGroup, bInInternalWeights ? FTransformCollection::TransformGroup : NAME_None) , BoneWeightsAttribute(InCollection, BoneWeightsAttributeName, FGeometryCollection::VerticesGroup, bInInternalWeights ? FTransformCollection::TransformGroup : NAME_None) , KinematicWeightAttribute(InCollection, KinematicWeightAttributeName, FGeometryCollection::VerticesGroup) , GeometryLODAttribute(InCollection, GeometryLODAttributeName, FGeometryCollection::GeometryGroup) , SkeletalMeshAttribute(InCollection, SkeletalMeshAttributeName, FGeometryCollection::GeometryGroup) , bInternalWeights(bInInternalWeights) { DefineSchema(); } FVertexBoneWeightsFacade::FVertexBoneWeightsFacade(const FManagedArrayCollection& InCollection, const bool bInInternalWeights) : ConstCollection(InCollection) , Collection(nullptr) , BoneIndicesAttribute(InCollection, BoneIndicesAttributeName, FGeometryCollection::VerticesGroup, bInInternalWeights ? FTransformCollection::TransformGroup : NAME_None) , BoneWeightsAttribute(InCollection, BoneWeightsAttributeName, FGeometryCollection::VerticesGroup, bInInternalWeights ? FTransformCollection::TransformGroup : NAME_None) , KinematicWeightAttribute(InCollection, KinematicWeightAttributeName, FGeometryCollection::VerticesGroup) , GeometryLODAttribute(InCollection, GeometryLODAttributeName, FGeometryCollection::GeometryGroup) , SkeletalMeshAttribute(InCollection, SkeletalMeshAttributeName, FGeometryCollection::GeometryGroup) , bInternalWeights(bInInternalWeights) { } // // Initialization // void FVertexBoneWeightsFacade::DefineSchema() { check(!IsConst()); BoneIndicesAttribute.Add(); BoneWeightsAttribute.Add(); KinematicWeightAttribute.AddAndFill(0.0f); SkeletalMeshAttribute.Add(); GeometryLODAttribute.Add(); if (!BoneIndicesAttribute.IsValid()) { UE_LOG(LogDataflowVertexBoneWeightsFacade, Warning, TEXT("FVertexBoneWeightsFacade failed to initialize because '%s' attribute from '%s' group expected type TArray."), *FVertexBoneWeightsFacade::BoneIndicesAttributeName.ToString(), *FGeometryCollection::VerticesGroup.ToString()); } if (!BoneWeightsAttribute.IsValid()) { UE_LOG(LogDataflowVertexBoneWeightsFacade, Warning, TEXT("FVertexBoneWeightsFacade failed to initialize because '%s' attribute from '%s' group expected type TArray."), *FVertexBoneWeightsFacade::BoneWeightsAttributeName.ToString(), *FGeometryCollection::VerticesGroup.ToString()); } if (!KinematicWeightAttribute.IsValid()) { UE_LOG(LogDataflowVertexBoneWeightsFacade, Warning, TEXT("FVertexBoneWeightsFacade failed to initialize because '%s' attribute from '%s' group expected type ."), *FVertexBoneWeightsFacade::KinematicWeightAttributeName.ToString(), *FGeometryCollection::VerticesGroup.ToString()); } if (!SkeletalMeshAttribute.IsValid()) { UE_LOG(LogDataflowVertexBoneWeightsFacade, Warning, TEXT("FVertexBoneWeightsFacade failed to initialize because '%s' attribute from '%s' group expected type >."), *FVertexBoneWeightsFacade::SkeletalMeshAttributeName.ToString(), *FGeometryCollection::GeometryGroup.ToString()); } if (!GeometryLODAttribute.IsValid()) { UE_LOG(LogDataflowVertexBoneWeightsFacade, Warning, TEXT("FVertexBoneWeightsFacade failed to initialize because '%s' attribute from '%s' group expected type ."), *FVertexBoneWeightsFacade::GeometryLODAttributeName.ToString(), *FGeometryCollection::GeometryGroup.ToString()); } } bool FVertexBoneWeightsFacade::IsValid() const { return BoneIndicesAttribute.IsValid() && BoneWeightsAttribute.IsValid() && KinematicWeightAttribute.IsValid() && SkeletalMeshAttribute.IsValid() && GeometryLODAttribute.IsValid(); } bool FVertexBoneWeightsFacade::HasValidBoneIndicesAndWeights() const { return (ConstCollection.HasAttribute(DeprecatedBoneIndicesAttributeName, FGeometryCollection::VerticesGroup) || BoneIndicesAttribute.IsValid()) && BoneWeightsAttribute.IsValid(); } void FVertexBoneWeightsFacade::ModifyGeometryBinding(const int32 GeometryIndex, const TObjectPtr& SkeletalMesh, const int32 GeometryLOD) { TManagedArray< TObjectPtr >& SkeletalMeshes = SkeletalMeshAttribute.Modify(); TManagedArray< int32 >& GeometryLODs = GeometryLODAttribute.Modify(); if(0 <= GeometryIndex && GeometryIndex < ConstCollection.NumElements(FGeometryCollection::GeometryGroup)) { SkeletalMeshes[GeometryIndex] = SkeletalMesh; GeometryLODs[GeometryIndex] = GeometryLOD; } } // // Add Weights from a bone to a vertex // void FVertexBoneWeightsFacade::AddBoneWeight(int32 VertexIndex, int32 BoneIndex, float BoneWeight) { TManagedArray< TArray >& IndicesArray = BoneIndicesAttribute.Modify(); TManagedArray< TArray >& WeightsArray = BoneWeightsAttribute.Modify(); if (0 <= VertexIndex && VertexIndex < NumVertices()) { if (!bInternalWeights || (0 <= BoneIndex && BoneIndex < NumBones())) { IndicesArray[VertexIndex].Add(BoneIndex); WeightsArray[VertexIndex].Add(BoneWeight); } } } void FVertexBoneWeightsFacade::ModifyKinematicWeight(int32 VertexIndex, const float KinematicWeight) { if (KinematicWeightAttribute.IsValid() && KinematicWeightAttribute.IsValidIndex(VertexIndex)) { KinematicWeightAttribute.ModifyAt(VertexIndex, KinematicWeight); } } void FVertexBoneWeightsFacade::ModifyBoneWeight(int32 VertexIndex, const TArray& VertexBoneIndices, const TArray& VertexBoneWeights) { if(VertexBoneIndices.Num() == VertexBoneWeights.Num()) { TManagedArray< TArray >& IndicesArray = BoneIndicesAttribute.Modify(); TManagedArray< TArray >& WeightsArray = BoneWeightsAttribute.Modify(); if (IndicesArray.IsValidIndex(VertexIndex) && WeightsArray.IsValidIndex(VertexIndex)) { IndicesArray[VertexIndex].Empty(); WeightsArray[VertexIndex].Empty(); float TotalWeight = 0.f; int32 LocalIndex = 0; for (const int32& BoneIndex : VertexBoneIndices) { if (!bInternalWeights || (0 <= BoneIndex && BoneIndex < NumBones())) { const float BoneWeight = VertexBoneWeights[LocalIndex++]; IndicesArray[VertexIndex].Add(BoneIndex); WeightsArray[VertexIndex].Add(BoneWeight); TotalWeight += BoneWeight; } } if (TotalWeight < 1.f - UE_KINDA_SMALL_NUMBER || TotalWeight > 1.f + UE_KINDA_SMALL_NUMBER) { UE_LOG(LogChaos, Warning, TEXT("FVertexBoneWeightsFacade::ModifyBoneWeight: Bone weight sum %f is not 1 on vertex %d"), TotalWeight, VertexIndex); } } } } void FVertexBoneWeightsFacade::SetVertexKinematic(int32 VertexIndex, bool Value) { if (KinematicWeightAttribute.IsValid() && KinematicWeightAttribute.IsValidIndex(VertexIndex)) { KinematicWeightAttribute.ModifyAt(VertexIndex, Value); } } void FVertexBoneWeightsFacade::SetVertexArrayKinematic(const TArray& VertexIndices, bool Value) { if (KinematicWeightAttribute.IsValid()) { TManagedArray& KinematicWeights = KinematicWeightAttribute.Modify(); for (const int32& VertexIndex : VertexIndices) { if (KinematicWeights.IsValidIndex(VertexIndex)) { KinematicWeights[VertexIndex] = Value; } } } } bool FVertexBoneWeightsFacade::IsKinematicVertex(int32 VertexIndex) const { if(ConstCollection.HasAttribute(DeprecatedKinematicFlagAttributeName, FGeometryCollection::VerticesGroup)) { const TManagedArray& KinematicFlag = ConstCollection.GetAttribute(DeprecatedKinematicFlagAttributeName, FGeometryCollection::VerticesGroup); return KinematicFlag.IsValidIndex(VertexIndex) && KinematicFlag.GetConstArray()[VertexIndex]; } else if (KinematicWeightAttribute.IsValid()) { return KinematicWeightAttribute.IsValidIndex(VertexIndex) && (KinematicWeightAttribute.Get()[VertexIndex] == 1.0); } else //backward compatibility for KinematicAttribute added in 5.5 { return BoneIndicesAttribute.IsValid() && BoneIndicesAttribute.IsValidIndex(VertexIndex) && BoneIndicesAttribute.Get()[VertexIndex].Num() && BoneWeightsAttribute.IsValid() && BoneWeightsAttribute.IsValidIndex(VertexIndex) && BoneWeightsAttribute.Get()[VertexIndex].Num(); } }; const TManagedArray< TArray >* FVertexBoneWeightsFacade::FindBoneIndices() const { if(ConstCollection.HasAttribute(DeprecatedBoneIndicesAttributeName, FGeometryCollection::VerticesGroup)) { return ConstCollection.FindAttribute >(DeprecatedBoneIndicesAttributeName, FGeometryCollection::VerticesGroup); } else { return BoneIndicesAttribute.Find(); } } const TManagedArray< TArray >& FVertexBoneWeightsFacade::GetBoneIndices() const { if(ConstCollection.HasAttribute(DeprecatedBoneIndicesAttributeName, FGeometryCollection::VerticesGroup)) { return ConstCollection.GetAttribute >(DeprecatedBoneIndicesAttributeName, FGeometryCollection::VerticesGroup); } else { return BoneIndicesAttribute.Get(); } } const TManagedArray< TArray >* FVertexBoneWeightsFacade::FindBoneWeights() const { return BoneWeightsAttribute.Find(); } const TManagedArray< TArray >& FVertexBoneWeightsFacade::GetBoneWeights() const { return BoneWeightsAttribute.Get(); } // // Add Weights from Selection // void FVertexBoneWeightsFacade::NormalizeBoneWeights() { if (IsValid()) { TManagedArray< TArray >& WeightsArray = BoneWeightsAttribute.Modify(); for(TArray& BoneWeights : WeightsArray) { float TotalWeight = 0.0; for(float& BoneWeight : BoneWeights) { TotalWeight += BoneWeight; } if (TotalWeight > UE_KINDA_SMALL_NUMBER) { for(float& BoneWeight : BoneWeights) { BoneWeight /= TotalWeight; } } } } } void FVertexBoneWeightsFacade::AddBoneWeightsFromKinematicBindings() { check(!IsConst()); DefineSchema(); if (IsValid() && bInternalWeights) { TManagedArray< TArray >& IndicesArray = BoneIndicesAttribute.Modify(); TManagedArray< TArray >& WeightsArray = BoneWeightsAttribute.Modify(); TArray TotalWeights; TotalWeights.Init(0.f, WeightsArray.Num()); int32 VertexIndex = 0; for(TArray& BoneWeights : WeightsArray) { for(float& BoneWeight : BoneWeights) { TotalWeights[VertexIndex] += BoneWeight; } ++VertexIndex; } GeometryCollection::Facades::FKinematicBindingFacade BindingFacade(ConstCollection); for (int32 Kdx = BindingFacade.NumKinematicBindings() - 1; 0 <= Kdx; Kdx--) { int32 BoneIndex; TArray VertexIndices; TArray VertexWeights; BindingFacade.GetBoneBindings(BindingFacade.GetKinematicBindingKey(Kdx), BoneIndex, VertexIndices, VertexWeights); if (0 <= BoneIndex && BoneIndex < NumBones()) { for (int32 LocalVertex = 0; LocalVertex < VertexIndices.Num(); ++LocalVertex) { VertexIndex = VertexIndices[LocalVertex]; const float VertexWeight = VertexWeights[LocalVertex]; if (0 <= VertexIndex && VertexIndex < NumVertices() && !IndicesArray[VertexIndex].Contains(BoneIndex)) { SetVertexKinematic(VertexIndex); int32 LocalBone = IndicesArray[VertexIndex].Find(BoneIndex); if (TotalWeights[VertexIndex] + VertexWeight <= 1.f + UE_KINDA_SMALL_NUMBER) { IndicesArray[VertexIndex].Add(BoneIndex); WeightsArray[VertexIndex].Add(VertexWeight); // Maybe we should re-normalize the weights after the kinematic bindings loop TotalWeights[VertexIndex] += VertexWeight; } else { UE_LOG(LogChaos, Warning, TEXT("Bone weight sum %f exceeds 1 on vertex %d"), TotalWeights[VertexIndex] + VertexWeight, VertexIndex); } } } } } NormalizeBoneWeights(); } } }