UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
BoneHierarchy.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2#pragma once
3
5
7//DEFINE_LOG_CATEGORY_STATIC(USkeletalMeshSimulationComponentLogging, Log, All);
8
13// @todo(ccaulfield): we should not have to create bodies/aggregates for all parents of a simulated bone
14// @todo(ccaulfield): fix dirty flag propagation and support setting of component-space poses and initialization from anim fo local and component poses
15// @todo(ccaulfield): should have better separation of mapping from anim to physics and physics i/o data
16// @todo(ccaulfield): actually I think this class only needs local-space transforms for non-physics child bones (or interstitials). The rest just need "simulation space"
18{
19public:
23 FBoneHierarchy(const FBoneHierarchy &) = delete;
25
28 {
29 ImplicitGroups = MoveTemp(Other.ImplicitGroups);
30 BoneIndices = MoveTemp(Other.BoneIndices);
31 SocketIndices = MoveTemp(Other.SocketIndices);
32
33 BoneToShapeGroup = MoveTemp(Other.BoneToShapeGroup);
34 Roots = MoveTemp(Other.Roots);
35
36 BoneToTransformIndex = MoveTemp(Other.BoneToTransformIndex);
37 TransformToBoneIndex = MoveTemp(Other.TransformToBoneIndex);
38
39 LocalSpaceTransforms = MoveTemp(Other.LocalSpaceTransforms);
40 WorldSpaceTransforms = MoveTemp(Other.WorldSpaceTransforms);
41 PrevWorldSpaceTransforms = MoveTemp(Other.PrevWorldSpaceTransforms);
42
43 AnimDirty = MoveTemp(Other.AnimDirty);
44
45 ParentIndices = MoveTemp(Other.ParentIndices);
46 ChildIndices = MoveTemp(Other.ChildIndices);
47
48 ActorLocalToWorld = MoveTemp(Other.ActorLocalToWorld);
49 ActorLocalToWorldDirty = MoveTemp(Other.ActorLocalToWorldDirty);
50
51 TempTargetIndices = MoveTemp(Other.TempTargetIndices);
52
53 return *this;
54 }
55
56 //--------------------------------------------------------------------------
57 // Initialization
58 //--------------------------------------------------------------------------
59
63 void InitPreAdd(const int32 NumBones)
64 {
66 ImplicitGroups.Reserve(NumBones);
68 BoneIndices.Reserve(NumBones);
69 BoneToShapeGroup.Reset();
70 BoneToShapeGroup.Reserve(NumBones);
71 }
72
77 {
78 // Make sure the shape group is valid (has a bone id).
79 check(AnalyticShapeGroupIn->IsValid());
80
83
85 BoneIndices.Add(AnalyticShapeGroup->GetBoneIndex());
86 check(BoneToShapeGroup.Contains(AnalyticShapeGroup->GetBoneIndex()) == false);
87 BoneToShapeGroup.Add(AnalyticShapeGroup->GetBoneIndex(), AnalyticShapeGroup.Get());
88 check(BoneToShapeGroup.Num() == BoneIndices.Num()); // Duplicate bone index or hash collision?
89 }
90
96 {
98 }
99
100 bool HasBoneIndex(const uint32 BoneIndex) const
101 {
102 return BoneToShapeGroup.Contains(BoneIndex);
103 }
104
106 {
107 return BoneIndices;
108 }
109
110 void SetSocketIndexForBone(const uint32 BoneIndex, const int32 SocketIndex)
111 {
112 SocketIndices[BoneToTransformIndex[BoneIndex]] = SocketIndex;
113 }
114
115 int32 GetSocketIndexForBone(const uint32 BoneIndex) const
116 {
117 return SocketIndices[BoneToTransformIndex[BoneIndex]];
118 }
119
124
129
131 {
132 return BoneToShapeGroup[BoneIndex];
133 }
134
136 {
137 return BoneToShapeGroup[BoneIndex];
138 }
139
140public:
141 //--------------------------------------------------------------------------
142 // Anim update interface
143 //--------------------------------------------------------------------------
144
146 {
147 for (int32 Index = 0; Index < WorldSpaceTransforms.Num(); Index++)
148 {
150 }
151 }
152
159 void SetAnimLocalSpaceTransform(const int32 BoneIndex, const FTransform &BoneXf)
160 {
161 check(BoneToTransformIndex.Contains(BoneIndex));
162 const int32 TransformIndex = BoneToTransformIndex[BoneIndex];
163 FTransform& TargetXf = LocalSpaceTransforms[TransformIndex];
164 // FTransform::Equals() is flakey.
165 //if (!TargetXf.Equals(BoneXf, 1.0e-8))
166 if (!TargetXf.GetTranslation().Equals(BoneXf.GetTranslation(), 1.0e-8f) ||
167 !TargetXf.GetRotation().Equals(BoneXf.GetRotation(), 1.0e-8f) ||
168 !TargetXf.GetScale3D().Equals(BoneXf.GetScale3D(), 1.0e-8f))
169 {
171 SetAnimLocalDirty(TransformIndex);
172 check(GetAnimLocalDirty(TransformIndex) == true);
173 }
174 }
175
181 {
182 // FTransform::Equals() is flakey.
183 //ActorLocalToWorldDirty |= !ActorLocalToWorld.FTransform::Equals(InActorLocalToWorld);
185 !ActorLocalToWorld.GetTranslation().Equals(InActorLocalToWorld.GetTranslation(), 1.0e-8f) ||
186 !ActorLocalToWorld.GetRotation().Equals(InActorLocalToWorld.GetRotation(), 1.0e-8f) ||
187 !ActorLocalToWorld.GetScale3D().Equals(InActorLocalToWorld.GetScale3D(), 1.0e-8f);
189 }
190
191public:
192 //--------------------------------------------------------------------------
193 // Anim query interface
194 //--------------------------------------------------------------------------
195
207
216 {
217 // Make sure this bone is in the hierarchy.
218 check(BoneToTransformIndex.Contains(BoneIndex));
219 // Make sure PrepareWorldSpaceTransforms() has been called.
220 check(ActorLocalToWorldDirty == false);
221 return &WorldSpaceTransforms[BoneToTransformIndex[BoneIndex]];
222 }
223
225 {
226 const int32 TransformIndex = BoneToTransformIndex[BoneIndex];
227 return PrevWorldSpaceTransforms.IsValidIndex(TransformIndex) ? &PrevWorldSpaceTransforms[TransformIndex] : nullptr;
228 }
229
230 int32 GetTransformIndex(const int32 BoneIndex) const
231 {
232 return BoneToTransformIndex[BoneIndex];
233 }
234
235public:
236 //--------------------------------------------------------------------------
237 // Sim interface
238 //--------------------------------------------------------------------------
239
240
241protected:
251 {
252 // Sort the bone Indices. Bones with physics (kinematic or dynamic) first, then by Index
254 [this](const int32 BoneIndexL, const int32 BoneIndexR)
255 {
258 const bool bHasBodyL = GroupL->NumStructures() > 0;
259 const bool bHasBodyR = GroupR->NumStructures() > 0;
260
261 if (bHasBodyL && !bHasBodyR)
262 {
263 return true;
264 }
265 else if (!bHasBodyL && bHasBodyR)
266 {
267 return false;
268 }
269
270 return BoneIndexL < BoneIndexR;
271 });
272
274 {
275 // Remove parents and children, but not parent bone index.
276 Group->ClearHierarchy();
277 }
278 Roots.Reset();
279 int32 NumTransforms = 0;
281 {
282 const int32 ParentBoneIndex = Group->GetParentBoneIndex();
283 if (ParentBoneIndex == INDEX_NONE)
284 {
285 Roots.Add(Group.Get());
286 }
287 else
288 {
289 check(BoneToShapeGroup.Contains(ParentBoneIndex));
291 check(Parent != nullptr);
292 Parent->AddChild(Group.Get());
293 Group->SetParent(Parent);
294 check(Group->GetParent() == Parent);
295 }
296
297 // Count the number of transforms we have.
298 NumTransforms++;
299 }
300 check(Roots.Num() != 0);
301 check(NumTransforms == ImplicitGroups.Num());
303 TEXT("USkeletalMeshPhysicsProxy::InitHierarchy() - this: %p - "
304 "Implicit groups: %d, num transforms: %d, num roots: %d"),
305 this,
307 NumTransforms,
308 Roots.Num());
309
310 // Initialize transform buffers
311 LocalSpaceTransforms.Init(FTransform::Identity, NumTransforms);
312 WorldSpaceTransforms.Init(FTransform::Identity, NumTransforms);
314 AnimDirty.Init(0, NumTransforms);
315
316 SocketIndices.Init(INDEX_NONE, NumTransforms);
317 ParentIndices.Init(INDEX_NONE, NumTransforms);
319 ChildIndices.AddDefaulted(NumTransforms);
320
321 // Iterate over all nodes in hierarchical order, starting with the root(s).
322 // As we go, we append the children to our traversal set.
326 int32 TransformIndex = 0;
327 for (int32 i = 0; i < TraversalGroups.Num(); i++)
328 {
330 for (FAnalyticImplicitGroup* Group : *Groups)
331 {
332 const int32 BoneIndex = Group->GetBoneIndex();
333 BoneToTransformIndex.Add(BoneIndex, TransformIndex);
334 TransformToBoneIndex.Add(TransformIndex, BoneIndex);
335
336 // Add this group to its parents list of children.
338 {
339 const int32 ParentBoneIndex = ParentGroup->GetBoneIndex();
340 check(ParentBoneIndex != INDEX_NONE);
341 check(BoneToTransformIndex.Contains(ParentBoneIndex));
342 const int32 ParentTransformIndex = BoneToTransformIndex[ParentBoneIndex];
343 ChildIndices[ParentTransformIndex].Add(TransformIndex);
344 ParentIndices[TransformIndex] = ParentTransformIndex;
345 }
346 else
347 {
348 check(Group->GetParentBoneIndex() == INDEX_NONE);
350 }
351
352 const TArray<FAnalyticImplicitGroup*>& Children = Group->GetChildren();
353 ChildIndices[TransformIndex].Reserve(/*Group->NumStructures() + */Children.Num());
354 TransformIndex++;
355
356 // The children haven't been assigned a transform index yet, so
357 // all we can do is allocate memory. We'll populate the ChildIndices
358 // lists with children when we iterate over them.
359
360 if (Children.Num())
361 {
362 // Add the children of this bone to the traversal set.
363 TraversalGroups.Add(&Children);
364 }
365
366 }
367 }
368 }
369
370protected:
375 {
376 // * R
377 // |
378 // A *-------* B
379 // | |
380 // | |
381 // C * * D
382 // |
383 // E *-------* F
384 // |
385 // |
386 // G *
387 //
388 // World space G = T(RA) * T(AC) * T(CE) * T(EG)
389
390 for(const int32 TransformIndex : TargetIndices)
391 {
392 check(GetAnimLocalDirty(TransformIndex) == true);
393
394 const int32 ParentTransformIndex = ParentIndices[TransformIndex];
395 const FTransform& ChildXf = LocalSpaceTransforms[TransformIndex];
398 {
399 // This is a root in our hierarchy. Transform by the actor's Xf.
400 // T(RA)
402 }
403 else
404 {
405 // This group has a parent. Multiply by the parent transform.
406 // T(AC)
410 }
411
412 SetAnimLocalClean(TransformIndex);
413 check(GetAnimLocalDirty(TransformIndex) == false);
414 }
416 return (TargetIndices.Num() > 0);
417 }
418
423 {
425 {
426 for (int32 TransformIndex = 0; TransformIndex < AnimDirty.Num(); TransformIndex++)
427 SetAnimLocalDirty(TransformIndex);
428 }
429 else
430 {
431 for (int32 TransformIndex = 0; TransformIndex < ChildIndices.Num(); TransformIndex++)
432 if (GetAnimLocalDirty(TransformIndex))
433 {
434 for (const int32 ChildTransformIndex : ChildIndices[TransformIndex])
436 }
437 }
438 }
439
444 {
445 // Collect indices that need an update, in order.
446 TargetIndices.Reset();
447 for (int i = 0; i < AnimDirty.Num(); i++)
448 {
449 if (AnimDirty[i] & Mask)
450 TargetIndices.Add(i);
451 }
452 }
453
454 // @todo(ccaulfield): remove hard-coded bit fields
455 // @todo(ccaulfield): this needs to separate local and component-space dirty flags.
456 // @todo(ccaulfield): Changing a local-space pose should dirty the Component-space children
457 // @todo(ccaulfield): Changing a component-space pose should dirty the local-space pose (which will dirty child component space poses)
460
461 bool GetAnimLocalDirty(const int32 TransformIndex) const
462 { return AnimDirty[TransformIndex] & 0b0001; }
463 void SetAnimLocalDirty(const int32 TransformIndex)
464 { AnimDirty[TransformIndex] |= 0b0001; }
465 void SetAnimLocalClean(const int32 TransformIndex)
466 { AnimDirty[TransformIndex] &= 0b1110; }
467protected:
468 //friend class USkeletalMeshSimulationComponent;
469
470 // Owner of all implicit shape groups.
474
475 // A mapping from Bone index to implicit shape group.
477 // All implicit groups in the hierarchy that have no parents.
479
480 // Mapping from Bone index to our local transform array indices.
483
484 // Local, component, and world space transforms of each bone in the hierarchy,
485 // plus sub group structures.
487 //TArray<FTransform> ComponentSpaceTransforms;
490 // Dirty flags for each bone in the hierarchy, plus sub group structures.
492
493 // Parenting hierarchy for each bone and sub group structure
496
497 // The current top level local-to-world transform.
500
501 // The working set of indices.
503};
#define check(expr)
Definition AssertionMacros.h:314
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#define TEXT(x)
Definition Platform.h:1272
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
#define DEFINE_LOG_CATEGORY_STATIC(CategoryName, DefaultVerbosity, CompileTimeVerbosity)
Definition LogMacros.h:380
#define UE_LOG(CategoryName, Verbosity, Format,...)
Definition LogMacros.h:270
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
uint8_t uint8
Definition binka_ue_file_header.h:8
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition AnalyticImplicitGroup.h:24
const FAnalyticImplicitGroup * GetParent() const
Definition AnalyticImplicitGroup.h:469
Definition BoneHierarchy.h:18
TArray< TUniquePtr< FAnalyticImplicitGroup > > ImplicitGroups
Definition BoneHierarchy.h:471
TArray< FTransform > LocalSpaceTransforms
Definition BoneHierarchy.h:486
void InitPostAdd()
Definition BoneHierarchy.h:95
void PrepareForUpdate()
Definition BoneHierarchy.h:145
void SetAnimLocalDirty(const int32 TransformIndex)
Definition BoneHierarchy.h:463
TArray< FTransform > WorldSpaceTransforms
Definition BoneHierarchy.h:488
void InitPreAdd(const int32 NumBones)
Definition BoneHierarchy.h:63
bool ActorLocalToWorldDirty
Definition BoneHierarchy.h:499
TArray< uint8 > AnimDirty
Definition BoneHierarchy.h:491
TArray< int32 > BoneIndices
Definition BoneHierarchy.h:472
bool HasBoneIndex(const uint32 BoneIndex) const
Definition BoneHierarchy.h:100
FBoneHierarchy()
Definition BoneHierarchy.h:20
FBoneHierarchy & operator=(const FBoneHierarchy &)=delete
FBoneHierarchy(FBoneHierarchy &&Other)
Definition BoneHierarchy.h:24
bool UpdateAnimWorldSpaceTransforms(const TArray< int32 > &TargetIndices)
Definition BoneHierarchy.h:374
int32 GetTransformIndex(const int32 BoneIndex) const
Definition BoneHierarchy.h:230
TArray< FTransform > PrevWorldSpaceTransforms
Definition BoneHierarchy.h:489
void SetActorWorldSpaceTransform(const FTransform &InActorLocalToWorld)
Definition BoneHierarchy.h:180
TArray< int32 > SocketIndices
Definition BoneHierarchy.h:473
FBoneHierarchy(const FBoneHierarchy &)=delete
TMap< int32, FAnalyticImplicitGroup * > BoneToShapeGroup
Definition BoneHierarchy.h:476
const TArray< TUniquePtr< FAnalyticImplicitGroup > > & GetAnalyticShapeGroups() const
Definition BoneHierarchy.h:120
bool PrepareAnimWorldSpaceTransforms()
Definition BoneHierarchy.h:199
int32 GetSocketIndexForBone(const uint32 BoneIndex) const
Definition BoneHierarchy.h:115
void SetAnimLocalClean(const int32 TransformIndex)
Definition BoneHierarchy.h:465
TArray< FAnalyticImplicitGroup * > Roots
Definition BoneHierarchy.h:478
void GetDirtyAnimIndices(TArray< int32 > &TargetIndices)
Definition BoneHierarchy.h:458
TMap< int32, int32 > BoneToTransformIndex
Definition BoneHierarchy.h:481
const TArray< int32 > & GetBoneIndices() const
Definition BoneHierarchy.h:105
TArray< int32 > ParentIndices
Definition BoneHierarchy.h:494
TArray< TUniquePtr< FAnalyticImplicitGroup > > & GetAnalyticShapeGroups()
Definition BoneHierarchy.h:125
const FTransform * GetAnimWorldSpaceTransformsForBone(const int32 BoneIndex) const
Definition BoneHierarchy.h:215
FTransform ActorLocalToWorld
Definition BoneHierarchy.h:498
void SetSocketIndexForBone(const uint32 BoneIndex, const int32 SocketIndex)
Definition BoneHierarchy.h:110
TArray< int32 > TempTargetIndices
Definition BoneHierarchy.h:502
const FAnalyticImplicitGroup * GetAnalyticShapeGroup(const int32 BoneIndex) const
Definition BoneHierarchy.h:135
void PropagateAnimDirtyFlags()
Definition BoneHierarchy.h:422
bool GetAnimLocalDirty(const int32 TransformIndex) const
Definition BoneHierarchy.h:461
void Add(TUniquePtr< FAnalyticImplicitGroup > &&AnalyticShapeGroupIn)
Definition BoneHierarchy.h:76
FBoneHierarchy & operator=(FBoneHierarchy &&Other)
Definition BoneHierarchy.h:27
TArray< TArray< int32 > > ChildIndices
Definition BoneHierarchy.h:495
TMap< int32, int32 > TransformToBoneIndex
Definition BoneHierarchy.h:482
void SetAnimLocalSpaceTransform(const int32 BoneIndex, const FTransform &BoneXf)
Definition BoneHierarchy.h:159
FAnalyticImplicitGroup * GetAnalyticShapeGroup(const int32 BoneIndex)
Definition BoneHierarchy.h:130
void InitHierarchy()
Definition BoneHierarchy.h:250
void GetDirtyIndices(TArray< int32 > &TargetIndices, const uint8 Mask) const
Definition BoneHierarchy.h:443
const FTransform * GetPrevAnimWorldSpaceTransformForBone(const int32 BoneIndex) const
Definition BoneHierarchy.h:224
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
void Reset(SizeType NewSize=0)
Definition Array.h:2246
SizeType AddDefaulted()
Definition Array.h:2795
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
void Init(const ElementType &Element, SizeType Number)
Definition Array.h:3043
UE_NODEBUG UE_FORCEINLINE_HINT bool Find(const ElementType &Item, SizeType &Index) const
Definition Array.h:1302
UE_NODEBUG void Sort()
Definition Array.h:3418
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition UnrealString.h.inl:34
Definition UniquePtr.h:107
@ false
Definition radaudio_common.h:23
U16 Index
Definition radfft.cpp:71
bool Equals(const TQuat< T > &Q, T Tolerance=UE_KINDA_SMALL_NUMBER) const
Definition Quat.h:985
static CORE_API const TTransform< double > Identity
Definition TransformNonVectorized.h:58
TVector< T > GetTranslation() const
Definition TransformNonVectorized.h:1120
TQuat< T > GetRotation() const
Definition TransformNonVectorized.h:1109
TVector< T > GetScale3D() const
Definition TransformNonVectorized.h:1131
UE_FORCEINLINE_HINT bool Equals(const TVector< T > &V, T Tolerance=UE_KINDA_SMALL_NUMBER) const
Definition Vector.h:1601