UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
ReferenceSkeleton.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreMinimal.h"
6#include "BoneIndices.h"
7
8class USkeleton;
10
11// This contains Reference-skeleton related info
12// Bone transform is saved as FTransform array
14{
15 // Bone's name.
17
18 // INDEX_NONE if this is the root bone.
20
21#if WITH_EDITORONLY_DATA
22 // Name used for export (this should be exact as FName may mess with case)
23 FString ExportName;
24#endif
25
27
28 FMeshBoneInfo(const FName& InName, const FString& InExportName, int32 InParentIndex)
29 : Name(InName)
32 , ExportName(InExportName)
33#endif
34 {}
35
36 bool operator==(const FMeshBoneInfo& B) const
37 {
38 return(Name == B.Name);
39 }
40
42};
43
44// Cached Virtual Bone data from USkeleton
58
59class USkeleton;
60
62
63// Allow modifications to a reference skeleton while guaranteeing that virtual bones remain valid.
65{
66private:
67 FReferenceSkeleton& RefSkeleton;
68 const USkeleton* Skeleton;
69public:
73
74 // Update the reference pose transform of the specified bone
75 ENGINE_API void UpdateRefPoseTransform(const int32 BoneIndex, const FTransform& BonePose);
76
77 // Add a new bone. BoneName must not already exist! ParentIndex must be valid.
78 ENGINE_API void Add(const FMeshBoneInfo& BoneInfo, const FTransform& BonePose, const bool bAllowMultipleRoots = false);
79
80 // Remove a bone. BoneName must be valid.
81 ENGINE_API void Remove(const FName& BoneName, const bool bRemoveChildren);
82
83 // Rename a bone. InOldName must be valid and InNewName not already a bone name.
84 ENGINE_API void Rename(const FName& InOldName, const FName& InNewName);
85
86 // Change bone's parent. InBoneName must be valid and InParentName can be Name_NONE to unparent.
88
90 ENGINE_API int32 FindBoneIndex(const FName& BoneName) const;
91
94
96};
97
100{
102 :bOnlyOneRootAllowed(bInOnlyOneRootAllowed)
103 {}
104
105private:
106 //RAW BONES: Bones that exist in the original asset
108 TArray<FMeshBoneInfo> RawRefBoneInfo;
110 TArray<FTransform> RawRefBonePose;
111
112 //FINAL BONES: Bones for this skeleton including user added virtual bones
114 TArray<FMeshBoneInfo> FinalRefBoneInfo;
116 TArray<FTransform> FinalRefBonePose;
117
119 TMap<FName, int32> RawNameToIndexMap;
120 TMap<FName, int32> FinalNameToIndexMap;
121
122 // cached data to allow virtual bones to be built into poses
123 TArray<FBoneIndexType> RequiredVirtualBones;
124 TArray<FVirtualBoneRefData> UsedVirtualBoneData;
125
132 bool bOnlyOneRootAllowed;
133
136 mutable TArray<int32> CachedEndOfBranchIndicesRaw;
137 static constexpr int32 BRANCH_CACHE_INVALID_INDEX = -2;
138 void InvalidateEndOfBranchCache() const
139 {
140 CachedEndOfBranchIndicesRaw.Init(BRANCH_CACHE_INVALID_INDEX, RawRefBonePose.Num());
141 }
142
144 bool RemoveIndividualBone(int32 BoneIndex, TArray<int32>& OutBonesRemoved)
145 {
146 bool bRemoveThisBone = true;
147
148 // Make sure we have no children
150 {
151 if( RawRefBoneInfo[CurrBoneIndex].ParentIndex == BoneIndex )
152 {
153 bRemoveThisBone = false;
154 break;
155 }
156 }
157
159 {
160 // Update parent indices of bones further through the array
162 {
163 FMeshBoneInfo& Bone = RawRefBoneInfo[CurrBoneIndex];
164 if( Bone.ParentIndex > BoneIndex )
165 {
166 Bone.ParentIndex -= 1;
167 }
168 }
169
170 OutBonesRemoved.Add(BoneIndex);
171 RawRefBonePose.RemoveAt(BoneIndex, 1);
172 RawRefBoneInfo.RemoveAt(BoneIndex, 1);
173
174 InvalidateEndOfBranchCache();
175 }
176 return bRemoveThisBone;
177 }
178
179 int32 GetParentIndexInternal(const int32 BoneIndex, const TArray<FMeshBoneInfo>& BoneInfo) const
180 {
181 const int32 ParentIndex = BoneInfo[BoneIndex].ParentIndex;
182
183 // Parent must be valid. Either INDEX_NONE for Root, or before children for non root bones.
184 checkSlow(!bOnlyOneRootAllowed ||
185 (((BoneIndex == 0) && (ParentIndex == INDEX_NONE))
186 || ((BoneIndex > 0) && BoneInfo.IsValidIndex(ParentIndex) && (ParentIndex < BoneIndex))));
187
188 return ParentIndex;
189 }
190
191 void UpdateRefPoseTransform(const int32 BoneIndex, const FTransform& BonePose)
192 {
193 RawRefBonePose[BoneIndex] = BonePose;
194 }
195
198 void Add(const FMeshBoneInfo& BoneInfo, const FTransform& BonePose)
199 {
200 // Adding a bone that already exists is illegal
202
203 // Make sure our arrays are in sync.
204 checkSlow((RawRefBoneInfo.Num() == RawRefBonePose.Num()) && (RawRefBoneInfo.Num() == RawNameToIndexMap.Num()));
205
206 const int32 BoneIndex = RawRefBoneInfo.Add(BoneInfo);
207 RawRefBonePose.Add(BonePose);
208 RawNameToIndexMap.Add(BoneInfo.Name, BoneIndex);
209
210 // Normalize Quaternion to be safe.
211 RawRefBonePose[BoneIndex].NormalizeRotation();
212
213 // Parent must be valid. Either INDEX_NONE for Root, or before children for non root bones.
214 check(!bOnlyOneRootAllowed ||
215 (((BoneIndex == 0) && (BoneInfo.ParentIndex == INDEX_NONE))
216 || ((BoneIndex > 0) && RawRefBoneInfo.IsValidIndex(BoneInfo.ParentIndex) && (BoneInfo.ParentIndex < BoneIndex))));
217 }
218
220 void Remove(const FName InBoneName, const bool bRemoveChildren);
221
223 void Rename(const FName InBoneName, const FName InNewName);
224
226 int32 SetParent(const FName InBoneName, const FName InParentName);
227
228 // Help us translate a virtual bone source into a raw bone source (for evaluating virtual bone transform)
229 int32 GetRawSourceBoneIndex(const USkeleton* Skeleton, const FName& SourceBoneName) const;
230
231 // very slow search function for all children (raw or final)
232 int32 GetChildrenInternal(int32 InParentBoneIndex, TArray<int32>& OutChildren, const bool bRaw) const;
233
234 // returns the index of the bone at the end of the branch belonging to the given one (uses cached result if there is one)
235 int32 GetCachedEndOfBranchIndex(const int32 InBoneIndex) const;
236
237public:
239
241 int32 GetNum() const
242 {
243 return FinalRefBoneInfo.Num();
244 }
245
248 {
249 return RawRefBoneInfo.Num();
250 }
251
252 const TArray<FBoneIndexType>& GetRequiredVirtualBones() const { return RequiredVirtualBones; }
253
254 const TArray<FVirtualBoneRefData>& GetVirtualBoneRefData() const { return UsedVirtualBoneData; }
255
258 {
259 return FinalRefBoneInfo;
260 }
261
264 {
265 return FinalRefBonePose;
266 }
267
270 {
271 return RawRefBoneInfo;
272 }
273
276 {
277 return RawRefBonePose;
278 }
279
280 const TMap<FName, int32>& GetRawNameToIndexMap() const { return RawNameToIndexMap; }
281
284 {
285 TArray<FName> BoneNames;
286 BoneNames.Reserve(RawRefBoneInfo.Num());
287
288 for (const FMeshBoneInfo& BoneInfo : RawRefBoneInfo)
289 {
290 BoneNames.Add(BoneInfo.Name);
291 }
292
293 return BoneNames;
294 }
295
297 {
298 RawRefBoneInfo.Empty(Size);
299 RawRefBonePose.Empty(Size);
300
301 FinalRefBoneInfo.Empty(Size);
302 FinalRefBonePose.Empty(Size);
303
304 RawNameToIndexMap.Empty(Size);
305 FinalNameToIndexMap.Empty(Size);
306
307 CachedEndOfBranchIndicesRaw.Empty(Size);
308 }
309
311 int32 FindBoneIndex(const FName& BoneName) const
312 {
313 checkSlow(FinalRefBoneInfo.Num() == FinalNameToIndexMap.Num());
314 int32 BoneIndex = INDEX_NONE;
315 if( BoneName != NAME_None )
316 {
317 const int32* IndexPtr = FinalNameToIndexMap.Find(BoneName);
318 if( IndexPtr )
319 {
320 BoneIndex = *IndexPtr;
321 }
322 }
323 return BoneIndex;
324 }
325
327 int32 FindRawBoneIndex(const FName& BoneName) const
328 {
329 checkSlow(RawRefBoneInfo.Num() == RawNameToIndexMap.Num());
330 int32 BoneIndex = INDEX_NONE;
331 if (BoneName != NAME_None)
332 {
333 const int32* IndexPtr = RawNameToIndexMap.Find(BoneName);
334 if (IndexPtr)
335 {
336 BoneIndex = *IndexPtr;
337 }
338 }
339 return BoneIndex;
340 }
341
342 FName GetBoneName(const int32 BoneIndex) const
343 {
344 return FinalRefBoneInfo[BoneIndex].Name;
345 }
346
347 int32 GetParentIndex(const int32 BoneIndex) const
348 {
349 return GetParentIndexInternal(BoneIndex, FinalRefBoneInfo);
350 }
351
352 int32 GetRawParentIndex(const int32 BoneIndex) const
353 {
354 return GetParentIndexInternal(BoneIndex, RawRefBoneInfo);
355 }
356
358 {
359 return (FinalRefBoneInfo.IsValidIndex(Index));
360 }
361
363 {
364 return (RawRefBoneInfo.IsValidIndex(Index));
365 }
366
372 int32 GetDepthBetweenBones(const int32 BoneIndex, const int32 ParentBoneIndex) const
373 {
374 if (BoneIndex >= ParentBoneIndex)
375 {
376 int32 CurBoneIndex = BoneIndex;
377 int32 Depth = 0;
378
379 do
380 {
381 // if same return;
382 if (CurBoneIndex == ParentBoneIndex)
383 {
384 return Depth;
385 }
386
387 CurBoneIndex = FinalRefBoneInfo[CurBoneIndex].ParentIndex;
388 ++Depth;
389
390 } while (CurBoneIndex!=INDEX_NONE);
391 }
392
393 return INDEX_NONE;
394 }
395
396 bool BoneIsChildOf(const int32 ChildBoneIndex, const int32 ParentBoneIndex) const
397 {
398 if (ParentBoneIndex != INDEX_NONE)
399 {
400 // Bones are in strictly increasing order.
401 // So child must have an index greater than its parent.
402 if (ChildBoneIndex > ParentBoneIndex)
403 {
404 int32 BoneIndex = GetParentIndex(ChildBoneIndex);
405 do
406 {
407 if (BoneIndex == ParentBoneIndex)
408 {
409 return true;
410 }
411 BoneIndex = GetParentIndex(BoneIndex);
412
413 } while (BoneIndex != INDEX_NONE);
414 }
415 }
416
417 return false;
418 }
419
421
424 {
426
427 const int32 NumBones = GetRawBoneNum();
428 for(int32 BoneIndex=NumBones-1; BoneIndex>=0; BoneIndex--)
429 {
430 FMeshBoneInfo& Bone = RawRefBoneInfo[BoneIndex];
431
432 if(BonesToRemove.Contains(Bone.Name))
433 {
434 RemoveIndividualBone(BoneIndex, BonesRemoved);
435 }
436 }
437
438 const bool bRebuildNameMap = true;
440 return BonesRemoved;
441 }
442
444
447
450
451 SIZE_T GetDataSize() const;
452
456
460
463
466
467 // lazy-cached for fast access to children and recursive children
468 void GetRawChildrenIndicesCached(const int32 BoneIndex, TArray<int32>& OutChildren) const;
470
471 // very slow search function for all children
472 ENGINE_API int32 GetDirectChildBones(int32 ParentBoneIndex, TArray<int32> & Children) const;
473 ENGINE_API int32 GetRawDirectChildBones(int32 ParentBoneIndex, TArray<int32> & Children) const;
476};
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define check(expr)
Definition AssertionMacros.h:314
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#define WITH_EDITORONLY_DATA
Definition CoreMiscDefines.h:24
FPlatformTypes::SIZE_T SIZE_T
An unsigned integer the same size as a pointer, the same as UPTRINT.
Definition Platform.h:1150
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
uint32 Size
Definition VulkanMemory.cpp:4034
if(Failed) console_printf("Failed.\n")
Definition Archive.h:1208
Definition NameTypes.h:617
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
void RemoveAt(SizeType Index, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2083
bool Contains(const ComparisonType &Item) const
Definition Array.h:1518
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
UE_NODEBUG UE_FORCEINLINE_HINT bool IsValidIndex(SizeType Index) const
Definition Array.h:1122
void Init(const ElementType &Element, SizeType Number)
Definition Array.h:3043
void Empty(SizeType Slack=0)
Definition Array.h:2273
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition UnrealString.h.inl:34
Definition Object.h:95
Definition Skeleton.h:295
const FName ParentIndex("ParentIndex")
Definition SkeletalMeshAttributes.h:33
U16 Index
Definition radfft.cpp:71
Definition ReferenceSkeleton.h:14
FName Name
Definition ReferenceSkeleton.h:16
friend FArchive & operator<<(FArchive &Ar, FMeshBoneInfo &F)
Definition ReferenceSkeleton.cpp:57
FMeshBoneInfo()
Definition ReferenceSkeleton.h:26
bool operator==(const FMeshBoneInfo &B) const
Definition ReferenceSkeleton.h:36
FMeshBoneInfo(const FName &InName, const FString &InExportName, int32 InParentIndex)
Definition ReferenceSkeleton.h:28
int32 ParentIndex
Definition ReferenceSkeleton.h:19
Definition ReferenceSkeleton.h:65
FReferenceSkeletonModifier(FReferenceSkeleton &InRefSkel, const USkeleton *InSkeleton)
Definition ReferenceSkeleton.h:70
ENGINE_API int32 FindBoneIndex(const FName &BoneName) const
Definition ReferenceSkeleton.cpp:47
ENGINE_API void UpdateRefPoseTransform(const int32 BoneIndex, const FTransform &BonePose)
Definition ReferenceSkeleton.cpp:20
ENGINE_API ~FReferenceSkeletonModifier()
Definition ReferenceSkeleton.cpp:15
ENGINE_API void Rename(const FName &InOldName, const FName &InNewName)
Definition ReferenceSkeleton.cpp:36
ENGINE_API int32 SetParent(const FName &InBoneName, const FName &InParentName, const bool bAllowMultipleRoots=false)
Definition ReferenceSkeleton.cpp:41
const FReferenceSkeleton & GetReferenceSkeleton() const
Definition ReferenceSkeleton.h:95
ENGINE_API const TArray< FMeshBoneInfo > & GetRefBoneInfo() const
Definition ReferenceSkeleton.cpp:52
Definition ReferenceSkeleton.h:100
int32 FindRawBoneIndex(const FName &BoneName) const
Definition ReferenceSkeleton.h:327
void Empty(int32 Size=0)
Definition ReferenceSkeleton.h:296
bool IsValidIndex(int32 Index) const
Definition ReferenceSkeleton.h:357
ENGINE_API FTransform GetBoneAbsoluteTransform(const int32 InBoneIndex) const
Definition ReferenceSkeleton.cpp:724
int32 GetNum() const
Definition ReferenceSkeleton.h:241
const TArray< FBoneIndexType > & GetRequiredVirtualBones() const
Definition ReferenceSkeleton.h:252
ENGINE_API int32 GetRawDirectChildBones(int32 ParentBoneIndex, TArray< int32 > &Children) const
Definition ReferenceSkeleton.cpp:904
TArray< FName > GetRawRefBoneNames() const
Definition ReferenceSkeleton.h:283
void GetRawChildrenIndicesRecursiveCached(const int32 BoneIndex, TArray< int32 > &OutChildren) const
Definition ReferenceSkeleton.cpp:752
ENGINE_API void GetRawBoneAbsoluteTransforms(TArray< FTransform > &OutAbsoluteTransforms) const
Definition ReferenceSkeleton.cpp:704
ENGINE_API void EnsureParentsExist(TArray< FBoneIndexType > &InOutBoneSortedArray) const
Definition ReferenceSkeleton.cpp:772
ENGINE_API void GetBoneAbsoluteTransforms(TArray< FTransform > &OutAbsoluteTransforms) const
Definition ReferenceSkeleton.cpp:709
ENGINE_API friend FArchive & operator<<(FArchive &Ar, FReferenceSkeleton &F)
Definition ReferenceSkeleton.cpp:909
ENGINE_API int32 GetDirectChildBones(int32 ParentBoneIndex, TArray< int32 > &Children) const
Definition ReferenceSkeleton.cpp:899
ENGINE_API void RebuildRefSkeleton(const USkeleton *Skeleton, bool bRebuildNameMap)
Definition ReferenceSkeleton.cpp:484
SIZE_T GetDataSize() const
Definition ReferenceSkeleton.cpp:641
FName GetBoneName(const int32 BoneIndex) const
Definition ReferenceSkeleton.h:342
void RemoveDuplicateBones(const UObject *Requester, TArray< FBoneIndexType > &DuplicateBones)
Definition ReferenceSkeleton.cpp:539
const TArray< FTransform > & GetRawRefBonePose() const
Definition ReferenceSkeleton.h:275
int32 GetRawParentIndex(const int32 BoneIndex) const
Definition ReferenceSkeleton.h:352
void GetRawChildrenIndicesCached(const int32 BoneIndex, TArray< int32 > &OutChildren) const
Definition ReferenceSkeleton.cpp:734
int32 FindBoneIndex(const FName &BoneName) const
Definition ReferenceSkeleton.h:311
const TMap< FName, int32 > & GetRawNameToIndexMap() const
Definition ReferenceSkeleton.h:280
int32 GetDepthBetweenBones(const int32 BoneIndex, const int32 ParentBoneIndex) const
Definition ReferenceSkeleton.h:372
bool IsValidRawIndex(int32 Index) const
Definition ReferenceSkeleton.h:362
int32 GetRawBoneNum() const
Definition ReferenceSkeleton.h:247
friend FReferenceSkeletonModifier
Definition ReferenceSkeleton.h:475
const TArray< FMeshBoneInfo > & GetRefBoneInfo() const
Definition ReferenceSkeleton.h:257
TArray< int32 > RemoveBonesByName(USkeleton *Skeleton, const TArray< FName > &BonesToRemove)
Definition ReferenceSkeleton.h:423
ENGINE_API void EnsureParentsExistAndSort(TArray< FBoneIndexType > &InOutBoneUnsortedArray) const
Definition ReferenceSkeleton.cpp:827
const TArray< FTransform > & GetRefBonePose() const
Definition ReferenceSkeleton.h:263
ENGINE_API FTransform GetRawBoneAbsoluteTransform(const int32 InRawBoneIndex) const
Definition ReferenceSkeleton.cpp:714
bool BoneIsChildOf(const int32 ChildBoneIndex, const int32 ParentBoneIndex) const
Definition ReferenceSkeleton.h:396
const TArray< FVirtualBoneRefData > & GetVirtualBoneRefData() const
Definition ReferenceSkeleton.h:254
int32 GetParentIndex(const int32 BoneIndex) const
Definition ReferenceSkeleton.h:347
FReferenceSkeleton(bool bInOnlyOneRootAllowed=true)
Definition ReferenceSkeleton.h:101
void RebuildNameToIndexMap()
Definition ReferenceSkeleton.cpp:617
const TArray< FMeshBoneInfo > & GetRawRefBoneInfo() const
Definition ReferenceSkeleton.h:269
Definition ReferenceSkeleton.h:46
int32 TargetRefSkelIndex
Definition ReferenceSkeleton.h:49
FVirtualBoneRefData(int32 InVBRefSkelIndex, int32 InSourceRefSkelIndex, int32 InTargetRefSkelIndex)
Definition ReferenceSkeleton.h:51
int32 VBRefSkelIndex
Definition ReferenceSkeleton.h:47
int32 SourceRefSkelIndex
Definition ReferenceSkeleton.h:48