UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
BoneWeights.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Algo/IsSorted.h"
6#include "AnimationCore.h"
7#include "BoneIndices.h"
8#include "Containers/Array.h"
11#include "Containers/Set.h"
13#include "CoreTypes.h"
14#include "GPUSkinPublicDefs.h"
15#include "HAL/PlatformCrt.h"
16#include "Math/NumericLimits.h"
17#include "Math/UnrealMathSSE.h"
20#include "Templates/TypeHash.h"
22
23#include <limits>
24#include <type_traits>
25
26namespace UE {
27namespace AnimationCore {
28
32static constexpr int32 MaxInlineBoneWeightCount = MAX_TOTAL_INFLUENCES;
33
35static constexpr uint16 MaxRawBoneWeight = std::numeric_limits<uint16>::max();
36
38static constexpr float MaxRawBoneWeightFloat = static_cast<float>(std::numeric_limits<uint16>::max());
39
41static constexpr float InvMaxRawBoneWeightFloat = 1.0f / MaxRawBoneWeightFloat;
42
45static constexpr float BoneWeightThreshold = InvMaxRawBoneWeightFloat;
46
48{
49public:
55 {
56 return BoneIndex == InBoneWeight.BoneIndex && RawWeight == InBoneWeight.RawWeight;
57 }
58
64 {
65 return BoneIndex != InBoneWeight.BoneIndex || RawWeight != InBoneWeight.RawWeight;
66 }
67
71 static constexpr uint16 GetMaxRawWeight()
72 {
74 }
75
78 {
79 return A.GetRawWeight() > B.GetRawWeight();
80 }
81
83 FBoneWeight() = default;
84
90 : BoneIndex(InBoneIndex)
91 {
92 RawWeight = uint16( (InWeight << 8) | InWeight );
93 }
94
100 : BoneIndex(InBoneIndex), RawWeight(InRawWeight)
101 {
102 }
103
109 : BoneIndex(InBoneIndex)
110 {
112 }
113
118 {
119 BoneIndex = InBoneIndex;
120 }
121
126 {
127 return BoneIndex;
128 }
129
134 void SetWeight(float InWeight)
135 {
136 InWeight = FMath::Clamp(InWeight, 0.0f, 1.0f);
137 RawWeight = static_cast<uint16>(InWeight * static_cast<float>(GetMaxRawWeight()) + 0.5f);
138 }
139
143 float GetWeight() const
144 {
145 return RawWeight / static_cast<float>(GetMaxRawWeight());
146 }
147
152 {
153 RawWeight = InRawWeight;
154 }
155
161 {
162 return RawWeight;
163 }
164
166 {
167 InArchive << BoneIndex;
168 InArchive << RawWeight;
169 }
170
173 {
174 return HashCombine(::GetTypeHash(static_cast<uint16>(BoneIndex)), ::GetTypeHash(RawWeight));
175 }
176
180 FString ToString() const
181 {
182 return FString::Printf(TEXT("<%d, %g>"), BoneIndex, GetWeight());
183 }
184
185
190 {
191 return static_cast<int32>(static_cast<uint16>(BoneIndex) << 16 | RawWeight);
192 }
193
198 {
200 BW.RawWeight = static_cast<uint16>(static_cast<uint32>(InBoneWeight) & 0xFFFFU);
201 BW.BoneIndex = static_cast<FBoneIndexType>(static_cast<uint32>(InBoneWeight) >> 16);
202 return BW;
203 }
204
205private:
206 FBoneIndexType BoneIndex;
207 uint16 RawWeight;
208};
209
210static_assert(sizeof(FBoneWeight) == sizeof(int32), "FBoneWeight must be 32-bits");
211
216{
218 None,
219
221 AboveOne,
222
224 Always
225};
226
227
229{
230public:
232
239
241 EBoneWeightNormalizeType GetNormalizeType() const { return NormalizeType; }
242
247 {
248 MaxWeightCount = FMath::Max(1, InMaxWeightCount);
249 return *this;
250 }
251
253 int32 GetMaxWeightCount() const { return MaxWeightCount; }
254
261 {
263 WeightThreshold = static_cast<uint16>(InWeightThreshold * FBoneWeight::GetMaxRawWeight() + 0.5f);
264 WeightThreshold = FMath::Max(WeightThreshold, static_cast<uint16>(1));
265 return *this;
266 }
267
269 float GetWeightThreshold() const
270 {
271 return WeightThreshold / static_cast<float>(TNumericLimits<uint16>::Max());
272 }
273
278 {
279 return WeightThreshold;
280 }
281
285 {
286 DefaultBoneIndex = InBoneIndex;
287 bHasDefaultBoneIndex = true;
288 }
289
294 {
295 return DefaultBoneIndex;
296 }
297
301 {
302 bHasDefaultBoneIndex = false;
303 }
304
308 {
309 return bHasDefaultBoneIndex;
310 }
311
317 {
318 bBlendZeroInfluence = bInBlendZeroInfluence;
319 }
320
324 {
325 return bBlendZeroInfluence;
326 }
327
328private:
330 int32 MaxWeightCount = MaxInlineBoneWeightCount;
331 uint16 WeightThreshold = 1;
332 FBoneIndexType DefaultBoneIndex = static_cast<FBoneIndexType>(0);
333 bool bHasDefaultBoneIndex = false;
334 bool bBlendZeroInfluence = true;
335};
336
340{
341 struct Empty {};
343
348 {
349 }
350
352 {
353 return INDEX_NONE;
354 }
355
357 {
358 return {};
359 }
360
364
368
370 {
371 }
372
373 template<typename Predicate>
374 static void Sort(ContainerType& InContainer, Predicate InPredicate)
375 {
376 }
377
378 template<typename Predicate>
380 {
381 return INDEX_NONE;
382 }
383};
384
385
388template<typename ContainerAdapter>
390{
391public:
392 using ContainerType = typename ContainerAdapter::ContainerType;
393
397
398 template<typename OtherContainerAdapter, typename CT = ContainerType>
399 inline typename std::enable_if<!std::is_const<CT>::value, void>::type
402 const FBoneWeightsSettings& InSettings = {});
403
404 template<typename CT = ContainerType>
405 inline typename std::enable_if<!std::is_const<CT>::value, void>::type
408 const FBoneWeightsSettings& InSettings = {});
409
410 template<typename CT = ContainerType>
411 inline typename std::enable_if<!std::is_const<CT>::value, void>::type
413 const FBoneIndexType* InBones,
414 const float* InInfluences,
415 int32 NumEntries,
417 );
418
419 template<typename CT = ContainerType>
420 inline typename std::enable_if<!std::is_const<CT>::value, void>::type
422 const FBoneIndexType InBones[MaxInlineBoneWeightCount],
423 const uint16 InInfluences[MaxInlineBoneWeightCount],
424 const FBoneWeightsSettings& InSettings = {});
425
426 template<typename CT = ContainerType>
427 inline typename std::enable_if<!std::is_const<CT>::value, bool>::type
431 );
432
433 template<typename CT = ContainerType>
434 inline typename std::enable_if<!std::is_const<CT>::value, bool>::type
438 );
439
440 template<typename CT = ContainerType>
441 inline typename std::enable_if<!std::is_const<CT>::value, void>::type
444 );
445
451 template<typename ContainerTypeA, typename ContainerTypeB, typename CT = ContainerType>
452 inline typename std::enable_if<!std::is_const<CT>::value, void>::type
456 float InBias,
457 const FBoneWeightsSettings& InSettings = {});
458
459 int32 Num() const
460 {
461 return ContainerAdapter::Num(Container);
462 }
463
465 {
466 return ContainerAdapter::Get(Container, Index);
467 }
468
470 {
471 return ContainerAdapter::Get(Container, Index);
472 }
473
476 ) const
477 {
478 return Container.IndexOfByPredicate(
479 [InBoneIndex](const FBoneWeight& BW) { return InBoneIndex == BW.GetBoneIndex(); });
480 }
481
483 {
485 for (int32 Index = 0; Index < Num(); Index++)
486 {
488 }
489 return Hash;
490 }
491
492 FString ToString() const
493 {
494 FString Result(TEXT("["));
495 if (Num())
496 {
497 Result += Get(0).ToString();
498 for (int32 Index = 1; Index < Num(); Index++)
499 {
500 Result += TEXT(", ");
501 Result += Get(Index).ToString();
502 }
503 }
504 Result.Append(TEXT("]"));
505 return Result;
506 }
507
508private:
509 inline void SetBoneWeightsInternal(
510 TArrayView<FBoneWeight> BoneWeights,
511 const FBoneWeightsSettings& InSettings = {});
512
513 inline void SortWeights();
514 inline bool CullWeights(const FBoneWeightsSettings& InSettings);
515 inline void NormalizeWeights(EBoneWeightNormalizeType InNormalizeType);
516
517 inline bool Verify() const;
518
519 // The externally owned container we're operating on.
520 ContainerType &Container;
521};
522
523
526{
529
530 template<typename T>
531 struct TArrayAdapter
532 {
533 using ContainerType = T;
534
535 static void SetNum(ContainerType& InContainer, int32 InNum)
536 {
537 InContainer.SetNumUninitialized(InNum, EAllowShrinking::No);
538 }
539
540 static int32 Num(const ContainerType& InContainer)
541 {
542 return InContainer.Num();
543 }
544
545 static FBoneWeight Get(const ContainerType& InContainer, int32 InIndex)
546 {
547 return InContainer.GetData()[InIndex];
548 }
549
550 static void Set(ContainerType& InContainer, int32 InIndex, FBoneWeight InBoneWeight)
551 {
552 InContainer.GetData()[InIndex] = InBoneWeight;
553 }
554
555 static void Add(ContainerType& InContainer, FBoneWeight InBoneWeight)
556 {
558 }
559
560 static void Remove(ContainerType& InContainer, int32 InIndex)
561 {
562 InContainer.RemoveAt(InIndex);
563 }
564
565 template<typename Predicate>
566 static void Sort(ContainerType& InContainer, Predicate InPredicate)
567 {
569 }
570
571 template<typename Predicate>
572 static int32 IndexOf(const ContainerType& InContainer, Predicate InPredicate)
573 {
574 return InContainer.IndexOfByPredicate(InPredicate);
575 }
576 };
578
579public:
580 FBoneWeights() = default;
581
588 {
589 return BoneWeights == InBoneWeight.BoneWeights;
590 }
591
597 {
598 return BoneWeights != InBoneWeight.BoneWeights;
599 }
600
608 inline bool SetBoneWeight(
610 const FBoneWeightsSettings& InSettings = {});
611
619
623 inline bool RemoveBoneWeight(
625 const FBoneWeightsSettings& InSettings = {});
626
630 inline void Renormalize(const FBoneWeightsSettings& InSettings = {});
631
633 static inline FBoneWeights Create(
634 const FBoneIndexType InBones[MaxInlineBoneWeightCount],
635 const uint16 InWeights[MaxInlineBoneWeightCount],
636 const FBoneWeightsSettings& InSettings = {});
637
641 static inline FBoneWeights Create(
642 const FBoneIndexType* InBones,
643 const float* InWeights,
644 int32 NumEntries,
645 const FBoneWeightsSettings& InSettings = {});
646
648 static inline FBoneWeights Create(
650 const FBoneWeightsSettings& InSettings = {});
651
653 template<typename OtherContainerAdapter>
657 {
658 FBoneWeights Result;
659 FArrayWrapper W(Result.BoneWeights);
660 W.SetBoneWeights(InBoneWeights, InSettings);
661 return Result;
662 }
663
668 static inline FBoneWeights Blend(
669 const FBoneWeights& InA,
670 const FBoneWeights& InB,
671 float InBias,
672 const FBoneWeightsSettings& InSettings = {});
673
677 static inline FBoneWeights Blend(
678 const FBoneWeights& InA,
679 const FBoneWeights& InB,
680 const FBoneWeights& InC,
681 float InBaryX,
682 float InBaryY,
683 float InBaryZ,
684 const FBoneWeightsSettings& InSettings = {});
685
686 // Ranged-based for loop compatibility -- but only the const version.
688
689 RangedForConstIteratorType begin() const { return BoneWeights.begin(); }
690 RangedForConstIteratorType end() const { return BoneWeights.end(); }
691
695 int32 Num() const { return BoneWeights.Num(); }
696
702 const FBoneWeight& operator[](int32 Index) const { return BoneWeights.operator[](Index); }
703
706 {
707 return BoneWeights;
708 }
709
710 // -- Helper functions
711
716 {
717 return BoneWeights.IndexOfByPredicate(
718 [InBoneIndex](const FBoneWeight& BW) { return InBoneIndex == BW.GetBoneIndex(); });
719 }
720
722 {
723 InArchive << BoneWeights;
724 }
725
727 {
728 uint32 Hash = ::GetTypeHash(BoneWeights.Num());
729 for (const FBoneWeight& BoneWeight : BoneWeights)
730 {
731 Hash = HashCombine(Hash, BoneWeight.GetTypeHash());
732 }
733 return Hash;
734 }
735
736 FString ToString() const
737 {
738 FString Result(TEXT("["));
739 Result.Append(FString::JoinBy(BoneWeights, TEXT(", "), [](const FBoneWeight& V) { return V.ToString(); }));
740 Result.Append(TEXT("]"));
741 return Result;
742 }
743
744private:
746
748 BoneWeightArrayT BoneWeights;
749};
750
751
753template<typename ContainerAdapter>
754template<typename OtherContainerAdapter, typename CT>
755typename std::enable_if<!std::is_const<CT>::value, void>::type
758 const FBoneWeightsSettings& InSettings /*= {}*/)
759{
762 for (int32 Index = 0; Index < InBoneWeights.Num(); Index++)
763 {
765 if (BW.GetRawWeight() >= InSettings.GetRawWeightThreshold())
766 {
767 StackBoneWeights.Add(BW);
768 }
769 }
770
771 SetBoneWeightsInternal(StackBoneWeights, InSettings);
772}
773
774template<typename ContainerAdapter>
775template<typename CT>
776typename std::enable_if<!std::is_const<CT>::value, void>::type
779 const FBoneWeightsSettings& InSettings /*= {}*/)
780{
782
784 for (const FBoneWeight& BW : InBoneWeights)
785 {
786 if (BW.GetRawWeight() >= InSettings.GetRawWeightThreshold())
787 {
788 StackBoneWeights.Add(BW);
789 }
790 }
791
792 SetBoneWeightsInternal(StackBoneWeights, InSettings);
793}
794
795
796template<typename ContainerAdapter>
797template<typename CT>
798typename std::enable_if<!std::is_const<CT>::value, void>::type
800 const FBoneIndexType* InBones,
801 const float* InInfluences,
802 int32 NumEntries,
803 const FBoneWeightsSettings& InSettings /*= {}*/)
804{
806
807 StackBoneWeights.Reserve(NumEntries);
808 for (int32 Index = 0; Index < NumEntries; Index++)
809 {
811 if (BW.GetRawWeight() >= InSettings.GetRawWeightThreshold())
812 {
813 StackBoneWeights.Add(BW);
814 }
815 }
816
817 SetBoneWeightsInternal(StackBoneWeights, InSettings);
818}
819
820
821template<typename ContainerAdapter>
822template<typename CT>
823typename std::enable_if<!std::is_const<CT>::value, void>::type
825 const FBoneIndexType InBones[MaxInlineBoneWeightCount],
826 const uint16 InInfluences[MaxInlineBoneWeightCount],
827 const FBoneWeightsSettings& InSettings /*= {}*/)
828{
829 // The weights are valid until the first zero influence.
830 int32 NumWeights = 0;
831 for (int32 Index = 0; Index < MaxInlineBoneWeightCount && InInfluences[Index]; Index++)
832 {
834 if (BW.GetRawWeight() >= InSettings.GetRawWeightThreshold())
835 {
836 NumWeights++;
837 }
838 }
839
840 ContainerAdapter::SetNum(Container, NumWeights);
841
842 int32 WeightIndex = 0;
843 for (int32 Index = 0; Index < MaxInlineBoneWeightCount && InInfluences[Index]; Index++)
844 {
846 if (BW.GetRawWeight() >= InSettings.GetRawWeightThreshold())
847 {
848 ContainerAdapter::Set(Container, WeightIndex++, BW);
849 }
850 }
851
852 // Sort the weights by descending weight value before we clip it.
853 SortWeights();
854
855 if (WeightIndex > InSettings.GetMaxWeightCount())
856 {
857 ContainerAdapter::SetNum(Container, InSettings.GetMaxWeightCount());
858 }
859
860 NormalizeWeights(InSettings.GetNormalizeType());
861}
862
863
864template<typename ContainerAdapter>
866 TArrayView<FBoneWeight> BoneWeights,
867 const FBoneWeightsSettings& InSettings /*= {} */
868 )
869{
871
872 int32 NumEntries = FMath::Min(BoneWeights.Num(), InSettings.GetMaxWeightCount());
873 if (NumEntries == 0 && InSettings.HasDefaultBoneIndex())
874 {
875 ContainerAdapter::SetNum(Container, 1);
876 ContainerAdapter::Set(Container, 0, FBoneWeight(InSettings.GetDefaultBoneIndex(), FBoneWeight::GetMaxRawWeight()));
877 return;
878 }
879
880 ContainerAdapter::SetNum(Container, NumEntries);
881
882 for (int32 Index = 0; Index < NumEntries; Index++)
883 {
884 ContainerAdapter::Set(Container, Index, BoneWeights[Index]);
885 }
886 NormalizeWeights(InSettings.GetNormalizeType());
887}
888
889template<typename ContainerAdapter>
890template<typename CT>
891typename std::enable_if<!std::is_const<CT>::value, bool>::type
894 const FBoneWeightsSettings& InSettings /*= {}*/)
895{
896 // Does this bone already exist?
897 int32 WeightIndex = FindWeightIndexByBone(InBoneWeight.GetBoneIndex());
898
899 // If the sum of weights could possibly exceed 1.0, we may need normalization based on
900 // the weight settings.
902
903 if (WeightIndex != INDEX_NONE)
904 {
905 FBoneWeight ExistingBoneWeight = ContainerAdapter::Get(Container, WeightIndex);
906
907 // New weight is below the threshold. Remove the current bone weight altogether.
908 if (InBoneWeight.GetRawWeight() < InSettings.GetRawWeightThreshold())
909 {
910 ContainerAdapter::Remove(Container, WeightIndex);
911
912 // If always normalizing, we need to re-normalize after removing this entry.
913 if (InSettings.GetNormalizeType() == EBoneWeightNormalizeType::Always)
914 {
915 NormalizeWeights(EBoneWeightNormalizeType::Always);
916 }
917
918 return false;
919 }
920
921 if (ExistingBoneWeight.GetRawWeight() == InBoneWeight.GetRawWeight())
922 {
923 return true;
924 }
925
926 bMayNeedNormalization = (ExistingBoneWeight.GetRawWeight() < InBoneWeight.GetRawWeight());
927
928 ExistingBoneWeight.SetRawWeight(InBoneWeight.GetRawWeight());
929
930 ContainerAdapter::Set(Container, WeightIndex, ExistingBoneWeight);
931 }
932 else
933 {
934 // If the new weight is below the threshold, reject and return.
935 if (InBoneWeight.GetRawWeight() < InSettings.GetRawWeightThreshold())
936 {
937 return false;
938 }
939
940 // Are we already at the limit of weights for this container?
941 const int32 NumWeights = ContainerAdapter::Num(Container);
942 if (NumWeights == InSettings.GetMaxWeightCount())
943 {
944 // If the weight is smaller than the smallest weight currently, then we reject.
945 if (InBoneWeight.GetRawWeight() < ContainerAdapter::Get(Container, NumWeights - 1).GetRawWeight())
946 {
947 return false;
948 }
949
950 // Overwrite the last one, we'll put it in its correct place when we sort.
951 ContainerAdapter::Set(Container, NumWeights - 1, InBoneWeight);
952 }
953 else
954 {
955 ContainerAdapter::Add(Container, InBoneWeight);
956 }
957
959 }
960
961 // If we got here, then we updated/added weights. We're contractually obligated to keep the
962 // weights sorted.
963 SortWeights();
964
965 if ((InSettings.GetNormalizeType() == EBoneWeightNormalizeType::Always) ||
967 {
968 Renormalize(InSettings);
969 }
970
971 return true;
972}
973
974
975template<typename ContainerAdapter>
976template<typename CT>
977typename std::enable_if<!std::is_const<CT>::value, bool>::type
980 const FBoneWeightsSettings& InSettings /*= {}*/
981 )
982{
983 int32 WeightIndex = FindWeightIndexByBone(InBoneIndex);
984 if (WeightIndex == INDEX_NONE)
985 {
986 return false;
987 }
988
989 ContainerAdapter::Remove(Container, WeightIndex);
990
991 // Cull all weights that exceed limits set by the settings.
992 CullWeights(InSettings);
993
994 // Removing weights will always cause the weight sum to decrease, so we only have to normalize
995 // if always asked to.
996 if (InSettings.GetNormalizeType() == EBoneWeightNormalizeType::Always)
997 {
998 NormalizeWeights(EBoneWeightNormalizeType::Always);
999 }
1000
1001 return true;
1002}
1003
1004
1005template<typename ContainerAdapter>
1006template<typename CT>
1007typename std::enable_if<!std::is_const<CT>::value, void>::type
1009 const FBoneWeightsSettings& InSettings /*= {}*/
1010 )
1011{
1012 NormalizeWeights(InSettings.GetNormalizeType());
1013
1014 // If entries are now below the threshold, remove them.
1015 if (InSettings.GetNormalizeType() == EBoneWeightNormalizeType::Always && CullWeights(InSettings))
1016 {
1017 NormalizeWeights(EBoneWeightNormalizeType::Always);
1018 }
1019}
1020
1021
1022template<typename ContainerAdapter>
1023template<typename ContainerAdapterA, typename ContainerAdapterB, typename CT>
1024typename std::enable_if<!std::is_const<CT>::value, void>::type
1028 const float InBias,
1029 const FBoneWeightsSettings& InSettings /*= {}*/
1030 )
1031{
1032 checkSlow(InBoneWeightsA.Verify());
1033 checkSlow(InBoneWeightsB.Verify());
1034
1035 // Both empty?
1036 if (InBoneWeightsA.Num() == 0 && InBoneWeightsB.Num() == 0)
1037 {
1038 if (InSettings.HasDefaultBoneIndex())
1039 {
1040 ContainerAdapter::SetNum(Container, 1);
1041 ContainerAdapter::Set(Container, 0, FBoneWeight{InSettings.GetDefaultBoneIndex(), FBoneWeight::GetMaxRawWeight()});
1042 }
1043 else
1044 {
1045 ContainerAdapter::SetNum(Container, 0);
1046 }
1047 return;
1048 }
1049 // FIXME: We can probably special-case a few more fast paths (one on either side, one each).
1050 // But let's collect statistics first.
1051
1052 // To simplify lookup and iteration over the two bone weight arrays, we sort by bone index
1053 // value, but indirectly, since we can't sort them directly, as that would violate the
1054 // sorted-by-descending-weight contract. Instead we create an indirection array on the stack
1055 // and use that to iterate
1057 for (int32 Index = 0; Index < InIndexIndirect.Num(); Index++)
1058 {
1060 }
1062 return InBoneWeights[A].GetBoneIndex() < InBoneWeights[B].GetBoneIndex();
1063 });
1064 };
1065
1069
1073
1075 BoneWeights.Reserve(InBoneWeightsA.Num() + InBoneWeightsB.Num());
1076
1077 const int32 RawBiasB = static_cast<int32>(InBias * static_cast<float>(FBoneWeight::GetMaxRawWeight()));
1079
1080 auto BlendWithZeroInfluenceBone = [&InSettings, &BoneWeights](const FBoneWeight& BW, const int32 RawBias)
1081 {
1082 if (InSettings.GetBlendZeroInfluence())
1083 {
1084 // Treat the missing bone as having a zero weight
1085 uint16 RawWeight = uint16( (int32)BW.GetRawWeight() * RawBias / FBoneWeight::GetMaxRawWeight() );
1086 BoneWeights.Emplace(BW.GetBoneIndex(), RawWeight);
1087 }
1088 else
1089 {
1090 // Ignore the missing bone
1091 BoneWeights.Add(BW);
1092 }
1093 };
1094
1095 int32 IndexA = 0, IndexB = 0;
1096 for (; IndexA < InBoneWeightsA.Num() && IndexB < InBoneWeightsB.Num(); /* */ )
1097 {
1098 const FBoneWeight& BWA = InBoneWeightsA[IndirectIndexA[IndexA]];
1099 const FBoneWeight& BWB = InBoneWeightsB[IndirectIndexB[IndexB]];
1100
1101 // If both have the same bone index, we blend them using the bias given and advance
1102 // both arrays. If the bone indices differ, we copy from the array with the lower bone
1103 // index value, to ensure we can possibly catch up with the other array. We then
1104 // advance until we hit the end of either array after which we blindly copy the remains.
1105 if (BWA.GetBoneIndex() == BWB.GetBoneIndex())
1106 {
1107 uint16 RawWeight = uint16( ((int32)BWA.GetRawWeight() * RawBiasA + (int32)BWB.GetRawWeight() * RawBiasB) / FBoneWeight::GetMaxRawWeight() );
1108
1109 BoneWeights.Emplace(BWA.GetBoneIndex(), RawWeight);
1110 IndexA++;
1111 IndexB++;
1112 }
1113 else if (BWA.GetBoneIndex() < BWB.GetBoneIndex())
1114 {
1116 IndexA++;
1117 }
1118 else
1119 {
1121 IndexB++;
1122 }
1123 }
1124
1125 for (; IndexA < InBoneWeightsA.Num(); IndexA++)
1126 {
1128 }
1129 for (; IndexB < InBoneWeightsB.Num(); IndexB++)
1130 {
1132 }
1133
1134 SetBoneWeightsInternal(BoneWeights, InSettings);
1135}
1136
1137
1138template<typename ContainerAdapter>
1140{
1141 ContainerAdapter::Sort(Container, FBoneWeight::DescSortByWeightPredicate);
1142}
1143
1144
1145template<typename ContainerAdapter>
1146bool TBoneWeights<ContainerAdapter>::CullWeights(
1147 const FBoneWeightsSettings& InSettings
1148 )
1149{
1150 bool bCulled = false;
1151 int32 NumWeights = ContainerAdapter::Num(Container);
1152
1153 // If are are more entries in the container than the settings allow for, indiscriminately
1154 // remove the excess entries.
1155 if (NumWeights > InSettings.GetMaxWeightCount())
1156 {
1157 ContainerAdapter::SetNum(Container, InSettings.GetMaxWeightCount());
1158 NumWeights = InSettings.GetMaxWeightCount();
1159 bCulled = true;
1160 }
1161
1162 // If any remaining entries are now below the threshold, remove them too.
1163 while (NumWeights > 0 && ContainerAdapter::Get(Container, NumWeights - 1).GetRawWeight() < InSettings.GetRawWeightThreshold())
1164 {
1165 ContainerAdapter::SetNum(Container, --NumWeights);
1166 bCulled = true;
1167 }
1168
1169 return bCulled;
1170}
1171
1172
1173template<typename ContainerAdapter>
1174void TBoneWeights<ContainerAdapter>::NormalizeWeights(
1176 )
1177{
1178 const int32 NumWeights = ContainerAdapter::Num(Container);
1179
1180 // Early checks
1182 {
1183 return;
1184 }
1185
1186 // Common case.
1187 if (NumWeights == 1)
1188 {
1190 {
1191 // Set the weight to full for the sole entry if normalizing always.
1192 FBoneWeight BoneWeight = ContainerAdapter::Get(Container, 0);
1193 BoneWeight.SetRawWeight(FBoneWeight::GetMaxRawWeight());
1194 ContainerAdapter::Set(Container, 0, BoneWeight);
1195 }
1196 return;
1197 }
1198
1199 // We operate on int64, since we can easily end up with wraparound issues during one of the
1200 // multiplications below when using int32. This would tank the division by WeightSum.
1201 int64 WeightSum = 0;
1202 for (int32 Index = 0; Index < NumWeights; Index++)
1203 {
1204 WeightSum += ContainerAdapter::Get(Container, Index).GetRawWeight();
1205 }
1206
1209 {
1210 int64 Correction = 0;
1211
1212 // Here we treat the raw weight as a 16.16 fixed point value and ensure that the
1213 // fraction, which would otherwise be lost through rounding, is carried over to the
1214 // subsequent values to maintain a constant sum to the max weight value.
1215 // We do this in descending weight order in an attempt to ensure that weight values
1216 // aren't needlessly lost after scaling.
1217 // It can happen, if all the weights are the same, or similar, that the last weight may
1218 // now be greater in value than the others. In that case we re-sort to ensure that our
1219 // descending weight order invariant holds.
1220 bool bNeedResort = false;
1221 uint16 LastWeight = std::numeric_limits<uint16>::max();
1222 for (int32 Index = 0; Index < NumWeights; Index++)
1223 {
1224 FBoneWeight BW = ContainerAdapter::Get(Container, Index);
1225 const int64 ScaledWeight = static_cast<int64>(BW.GetRawWeight()) * FBoneWeight::GetMaxRawWeight() + Correction;
1226 const uint16 NewWeight = static_cast<uint16>(FMath::Min(ScaledWeight / WeightSum, static_cast<int64>(FBoneWeight::GetMaxRawWeight())));
1227 BW.SetRawWeight(NewWeight);
1228 Correction = ScaledWeight - BW.GetRawWeight() * WeightSum;
1229 ContainerAdapter::Set(Container, Index, BW);
1230 if (NewWeight > LastWeight)
1231 {
1232 bNeedResort = true;
1233 }
1235 }
1236
1237 if (bNeedResort)
1238 {
1239 SortWeights();
1240 }
1241 }
1242}
1243
1244
1245template<typename ContainerAdapter>
1247{
1248 const int32 NumEntries = Num();
1249 if (NumEntries == 0)
1250 {
1251 return true;
1252 }
1253
1254 // Check that bone indexes are unique
1256 BoneIndexes.Reserve(NumEntries);
1257 for (int32 Index = 0; Index < NumEntries; Index++)
1258 {
1259 FBoneWeight BW = Get(Index);
1260 if (BoneIndexes.Find(BW.GetBoneIndex()) != nullptr)
1261 {
1262 // Commented out for now, to avoid linker errors, since this is a header-only file.
1263 // UE_LOG(LogAnimationCore, Error, TEXT("Bone Index %d is duplicated"), static_cast<int>(BW.GetBoneIndex()));
1264 return false;
1265 }
1266 BoneIndexes.Add(BW.GetBoneIndex());
1267 }
1268
1269 // Check that all weights are ordered.
1270 float LastWeight = Get(0).GetWeight();
1271 for (int32 Index = 1; Index < NumEntries; Index++)
1272 {
1273 const float Weight = Get(Index).GetWeight();
1274 if (Weight > LastWeight)
1275 {
1276 //UE_LOG(LogAnimationCore, Error, TEXT("Bone Weight at %d is greater than previous (%g > %g)"),
1277 // Index, Weight, LastWeight);
1278 return false;
1279 }
1281 }
1282
1283 return true;
1284}
1285
1286
1287// FBoneWeights implementations
1288static auto WeightSortPredicate = [](const FBoneWeight& A, const FBoneWeight& B) {
1289 return A.GetRawWeight() > B.GetRawWeight();
1290};
1291
1292
1295 const FBoneWeightsSettings& InSettings /*= {} */
1296)
1297{
1298 FArrayWrapper W(BoneWeights);
1300}
1301
1302
1305 const FBoneWeightsSettings& InSettings /*= {} */
1306)
1307{
1308 FArrayWrapper W(BoneWeights);
1310}
1311
1312
1314{
1315 FArrayWrapper W(BoneWeights);
1316 return W.Renormalize(InSettings);
1317}
1318
1319
1321 const FBoneIndexType InBones[MaxInlineBoneWeightCount],
1322 const uint16 InInfluences[MaxInlineBoneWeightCount],
1323 const FBoneWeightsSettings& InSettings /*= {} */
1324)
1325{
1326 FBoneWeights Result;
1327 FArrayWrapper W(Result.BoneWeights);
1329 return Result;
1330}
1331
1332
1334 const FBoneIndexType* InBones,
1335 const float* InInfluences,
1336 int32 NumEntries,
1337 const FBoneWeightsSettings& InSettings /*= {} */
1338)
1339{
1340 FBoneWeights Result;
1341 FArrayWrapper W(Result.BoneWeights);
1343 return Result;
1344}
1345
1346
1349 const FBoneWeightsSettings& InSettings /*= {} */
1350)
1351{
1352 FBoneWeights Result;
1354 return Result;
1355}
1356
1357
1359 const FBoneWeights& InA,
1360 const FBoneWeights& InB,
1361 float InBias,
1362 const FBoneWeightsSettings& InSettings /*= {} */
1363)
1364{
1365 FBoneWeights Result;
1366 FArrayWrapper W(Result.BoneWeights);
1367 FArrayWrapper A(const_cast<FBoneWeights&>(InA).BoneWeights);
1368 FArrayWrapper B(const_cast<FBoneWeights&>(InB).BoneWeights);
1369 W.Blend(A, B, InBias, InSettings);
1370 return Result;
1371}
1372
1374 const FBoneWeights& InA,
1375 const FBoneWeights& InB,
1376 const FBoneWeights& InC,
1377 float InBaryX,
1378 float InBaryY,
1379 float InBaryZ,
1381{
1382 if (FMath::IsNearlyZero(InBaryY + InBaryZ) == false)
1383 {
1384 const float BCW = InBaryZ / (InBaryY + InBaryZ);
1385
1386 // Use the default settings to make sure we don't normalize, don't prune bones if we exceed the default limit
1387 // and treat the bones with zero influence as having a zero weight during the first blend
1391
1394 return BCA;
1395 }
1396 else
1397 {
1398 return InA;
1399 }
1400}
1401
1402
1403} // namespace AnimationCore
1404} // namespace UE
1405
1406
1411static inline uint32 GetTypeHash(
1413 )
1414{
1415 return InBoneWeight.GetTypeHash();
1416}
1417
1418static inline FArchive& operator<<(
1421 )
1422{
1424 return InArchive;
1425}
1426
1428{
1429 return InBoneWeights.GetTypeHash();
1430}
1431
1432static inline FArchive& operator<<(
1435{
1437 return InArchive;
1438}
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define ensure( InExpression)
Definition AssertionMacros.h:464
uint16 FBoneIndexType
Definition BoneIndices.h:7
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::int64 int64
A 64-bit signed integer.
Definition Platform.h:1127
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
FArchive & operator<<(FArchive &Ar, FEnvQueryDebugProfileData::FStep &Data)
Definition EnvQueryTypes.cpp:489
#define MAX_TOTAL_INFLUENCES
Definition GPUSkinPublicDefs.h:12
@ Num
Definition MetalRHIPrivate.h:234
constexpr uint32 HashCombine(uint32 A, uint32 C)
Definition TypeHash.h:36
uint8_t uint8
Definition binka_ue_file_header.h:8
uint16_t uint16
Definition binka_ue_file_header.h:7
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition Archive.h:1208
virtual void Serialize(void *V, int64 Length)
Definition Archive.h:1689
Definition ArrayView.h:139
constexpr void Sort()
Definition ArrayView.h:773
constexpr SizeType IndexOfByPredicate(Predicate Pred) const
Definition ArrayView.h:644
UE_FORCEINLINE_HINT constexpr ElementType * end() const
Definition ArrayView.h:760
UE_FORCEINLINE_HINT constexpr SizeType Num() const
Definition ArrayView.h:380
UE_FORCEINLINE_HINT constexpr ElementType * begin() const
Definition ArrayView.h:759
TCheckedPointerIterator< const ElementType, SizeType, false > RangedForConstIteratorType
Definition Array.h:3372
UE_FORCEINLINE_HINT SizeType Emplace(ArgsType &&... Args)
Definition Array.h:2561
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
void SetNumUninitialized(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2369
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition ContainerAllocationPolicies.h:894
Definition BoneWeights.h:48
FString ToString() const
Definition BoneWeights.h:180
bool operator!=(const FBoneWeight &InBoneWeight) const
Definition BoneWeights.h:63
int32 ToInt32() const
Definition BoneWeights.h:189
FBoneWeight(FBoneIndexType InBoneIndex, uint8 InWeight)
Definition BoneWeights.h:89
FBoneWeight(FBoneIndexType InBoneIndex, uint16 InRawWeight)
Definition BoneWeights.h:99
void SetWeight(float InWeight)
Definition BoneWeights.h:134
void Serialize(FArchive &InArchive)
Definition BoneWeights.h:165
uint16 GetRawWeight() const
Definition BoneWeights.h:160
bool operator==(const FBoneWeight &InBoneWeight) const
Definition BoneWeights.h:54
static constexpr uint16 GetMaxRawWeight()
Definition BoneWeights.h:71
FBoneIndexType GetBoneIndex() const
Definition BoneWeights.h:125
static FBoneWeight FromInt32(int32 InBoneWeight)
Definition BoneWeights.h:197
FBoneWeight(FBoneIndexType InBoneIndex, float InWeight)
Definition BoneWeights.h:108
static bool DescSortByWeightPredicate(const FBoneWeight &A, const FBoneWeight &B)
Definition BoneWeights.h:77
uint32 GetTypeHash() const
Definition BoneWeights.h:172
void SetBoneIndex(FBoneIndexType InBoneIndex)
Definition BoneWeights.h:117
float GetWeight() const
Definition BoneWeights.h:143
void SetRawWeight(const uint16 InRawWeight)
Definition BoneWeights.h:151
Definition BoneWeights.h:229
void SetBlendZeroInfluence(bool bInBlendZeroInfluence)
Definition BoneWeights.h:316
void ClearDefaultBoneIndex()
Definition BoneWeights.h:300
void SetDefaultBoneIndex(FBoneIndexType InBoneIndex)
Definition BoneWeights.h:284
FBoneIndexType GetDefaultBoneIndex() const
Definition BoneWeights.h:293
EBoneWeightNormalizeType GetNormalizeType() const
Definition BoneWeights.h:241
bool GetBlendZeroInfluence() const
Definition BoneWeights.h:323
int32 GetMaxWeightCount() const
Definition BoneWeights.h:253
FBoneWeightsSettings & SetWeightThreshold(float InWeightThreshold)
Definition BoneWeights.h:260
FBoneWeightsSettings & SetMaxWeightCount(int32 InMaxWeightCount)
Definition BoneWeights.h:246
FBoneWeightsSettings & SetNormalizeType(EBoneWeightNormalizeType InNormalizeType)
Definition BoneWeights.h:234
bool HasDefaultBoneIndex() const
Definition BoneWeights.h:307
uint16 GetRawWeightThreshold() const
Definition BoneWeights.h:277
float GetWeightThreshold() const
Definition BoneWeights.h:269
A simple container for per-vertex influence of bones and their weights.
Definition BoneWeights.h:526
static FBoneWeights Create(TBoneWeights< OtherContainerAdapter > InBoneWeights, const FBoneWeightsSettings &InSettings={})
Definition BoneWeights.h:654
TArrayView< const FBoneWeight > ToArrayView() const
Definition BoneWeights.h:705
RangedForConstIteratorType begin() const
Definition BoneWeights.h:689
static FBoneWeights Create(const FBoneIndexType InBones[MaxInlineBoneWeightCount], const uint16 InWeights[MaxInlineBoneWeightCount], const FBoneWeightsSettings &InSettings={})
Definition BoneWeights.h:1320
int32 FindWeightIndexByBone(FBoneIndexType InBoneIndex) const
Definition BoneWeights.h:715
int32 GetTypeHash() const
Definition BoneWeights.h:726
bool operator!=(const FBoneWeights &InBoneWeight) const
Definition BoneWeights.h:596
int32 Num() const
Definition BoneWeights.h:695
void Renormalize(const FBoneWeightsSettings &InSettings={})
Definition BoneWeights.h:1313
bool SetBoneWeight(FBoneIndexType InBone, float InWeight, const FBoneWeightsSettings &InSettings={})
Definition BoneWeights.h:612
const FBoneWeight & operator[](int32 Index) const
Definition BoneWeights.h:702
RangedForConstIteratorType end() const
Definition BoneWeights.h:690
void Serialize(FArchive &InArchive)
Definition BoneWeights.h:721
FString ToString() const
Definition BoneWeights.h:736
bool RemoveBoneWeight(FBoneIndexType InBone, const FBoneWeightsSettings &InSettings={})
Definition BoneWeights.h:1303
static FBoneWeights Blend(const FBoneWeights &InA, const FBoneWeights &InB, float InBias, const FBoneWeightsSettings &InSettings={})
Definition BoneWeights.h:1358
bool SetBoneWeight(FBoneWeight InBoneWeight, const FBoneWeightsSettings &InSettings={})
Definition BoneWeights.h:1293
bool operator==(const FBoneWeights &InBoneWeight) const
Definition BoneWeights.h:587
Definition BoneWeights.h:390
typename ContainerAdapter::ContainerType ContainerType
Definition BoneWeights.h:392
std::enable_if<!std::is_const< CT >::value, void >::type SetBoneWeights(const FBoneIndexType InBones[MaxInlineBoneWeightCount], const uint16 InInfluences[MaxInlineBoneWeightCount], const FBoneWeightsSettings &InSettings={})
Definition BoneWeights.h:824
FString ToString() const
Definition BoneWeights.h:492
FBoneWeight Get(int32 Index) const
Definition BoneWeights.h:464
std::enable_if<!std::is_const< CT >::value, void >::type SetBoneWeights(TArrayView< const FBoneWeight > BoneWeights, const FBoneWeightsSettings &InSettings={})
Definition BoneWeights.h:777
int32 Num() const
Definition BoneWeights.h:459
std::enable_if<!std::is_const< CT >::value, bool >::type RemoveBoneWeight(FBoneIndexType InBoneIndex, const FBoneWeightsSettings &InSettings={})
Definition BoneWeights.h:978
std::enable_if<!std::is_const< CT >::value, void >::type Renormalize(const FBoneWeightsSettings &InSettings={})
Definition BoneWeights.h:1008
std::enable_if<!std::is_const< CT >::value, void >::type Blend(const TBoneWeights< ContainerTypeA > &InBoneWeightsA, const TBoneWeights< ContainerTypeB > &InBoneWeightsB, float InBias, const FBoneWeightsSettings &InSettings={})
FBoneWeight operator[](int32 Index) const
Definition BoneWeights.h:469
std::enable_if<!std::is_const< CT >::value, void >::type SetBoneWeights(const FBoneIndexType *InBones, const float *InInfluences, int32 NumEntries, const FBoneWeightsSettings &InSettings={})
Definition BoneWeights.h:799
std::enable_if<!std::is_const< CT >::value, bool >::type AddBoneWeight(FBoneWeight InBoneWeight, const FBoneWeightsSettings &InSettings={})
Definition BoneWeights.h:892
TBoneWeights(ContainerType &InContainer)
Definition BoneWeights.h:394
int32 FindWeightIndexByBone(FBoneIndexType InBoneIndex) const
Definition BoneWeights.h:474
int32 GetTypeHash() const
Definition BoneWeights.h:482
std::enable_if<!std::is_const< CT >::value, void >::type SetBoneWeights(const TBoneWeights< OtherContainerAdapter > &InBoneWeights, const FBoneWeightsSettings &InSettings={})
TBoneWeights implementation.
Definition BoneWeights.h:756
UE_STRING_CLASS & Append(const CharType *Str, int32 Count)
Definition UnrealString.h.inl:417
Definition AngularLimit.cpp:6
FORCEINLINE T * Get(const FObjectPtr &ObjectPtr)
Definition ObjectPtr.h:426
EBoneWeightNormalizeType
Definition BoneWeights.h:216
Definition AdvancedWidgetsModule.cpp:13
U16 Index
Definition radfft.cpp:71
static constexpr UE_FORCEINLINE_HINT T Clamp(const T X, const T MinValue, const T MaxValue)
Definition UnrealMathUtility.h:592
static UE_FORCEINLINE_HINT bool IsNearlyZero(float Value, float ErrorTolerance=UE_SMALL_NUMBER)
Definition UnrealMathUtility.h:407
Definition Array.h:206
Definition NumericLimits.h:41
Definition BoneWeights.h:340
static FBoneWeight Get(const ContainerType &InContainer, int32 InIndex)
Definition BoneWeights.h:356
static void Set(ContainerType &InContainer, int32 InIndex, FBoneWeight InBoneWeight)
Definition BoneWeights.h:361
static void Remove(ContainerType &InContainer, int32 InIndex)
Definition BoneWeights.h:369
static int32 Num(const ContainerType &InContainer)
Definition BoneWeights.h:351
static int32 IndexOf(const ContainerType &InContainer, Predicate InPredicate)
Definition BoneWeights.h:379
static void SetNum(ContainerType &InContainer, int32 InNum)
Definition BoneWeights.h:347
static void Sort(ContainerType &InContainer, Predicate InPredicate)
Definition BoneWeights.h:374
static void Add(ContainerType &InContainer, FBoneWeight InBoneWeight)
Definition BoneWeights.h:365