Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

338 lines
13 KiB
C++

// 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<int32>."), *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<float>."), *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 <float>."), *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 <TObjectPtr<UObject>>."), *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 <int32>."), *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<UObject>& SkeletalMesh, const int32 GeometryLOD)
{
TManagedArray< TObjectPtr<UObject> >& 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<int32> >& IndicesArray = BoneIndicesAttribute.Modify();
TManagedArray< TArray<float> >& 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<int32>& VertexBoneIndices, const TArray<float>& VertexBoneWeights)
{
if(VertexBoneIndices.Num() == VertexBoneWeights.Num())
{
TManagedArray< TArray<int32> >& IndicesArray = BoneIndicesAttribute.Modify();
TManagedArray< TArray<float> >& 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<int32>& VertexIndices, bool Value)
{
if (KinematicWeightAttribute.IsValid())
{
TManagedArray<float>& 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<bool>& KinematicFlag = ConstCollection.GetAttribute<bool>(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<int32> >* FVertexBoneWeightsFacade::FindBoneIndices() const
{
if(ConstCollection.HasAttribute(DeprecatedBoneIndicesAttributeName, FGeometryCollection::VerticesGroup))
{
return ConstCollection.FindAttribute<TArray<int32> >(DeprecatedBoneIndicesAttributeName, FGeometryCollection::VerticesGroup);
}
else
{
return BoneIndicesAttribute.Find();
}
}
const TManagedArray< TArray<int32> >& FVertexBoneWeightsFacade::GetBoneIndices() const
{
if(ConstCollection.HasAttribute(DeprecatedBoneIndicesAttributeName, FGeometryCollection::VerticesGroup))
{
return ConstCollection.GetAttribute<TArray<int32> >(DeprecatedBoneIndicesAttributeName, FGeometryCollection::VerticesGroup);
}
else
{
return BoneIndicesAttribute.Get();
}
}
const TManagedArray< TArray<float> >* FVertexBoneWeightsFacade::FindBoneWeights() const
{
return BoneWeightsAttribute.Find();
}
const TManagedArray< TArray<float> >& FVertexBoneWeightsFacade::GetBoneWeights() const
{
return BoneWeightsAttribute.Get();
}
//
// Add Weights from Selection
//
void FVertexBoneWeightsFacade::NormalizeBoneWeights()
{
if (IsValid())
{
TManagedArray< TArray<float> >& WeightsArray = BoneWeightsAttribute.Modify();
for(TArray<float>& 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<int32> >& IndicesArray = BoneIndicesAttribute.Modify();
TManagedArray< TArray<float> >& WeightsArray = BoneWeightsAttribute.Modify();
TArray<float> TotalWeights;
TotalWeights.Init(0.f, WeightsArray.Num());
int32 VertexIndex = 0;
for(TArray<float>& 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<int32> VertexIndices;
TArray<float> 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();
}
}
}