UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
ImplicitObjectBVH.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2#pragma once
3
4#include "Chaos/Core.h"
6#include "Chaos/ImplicitFwd.h"
8
9namespace Chaos
10{
11 class FChaosArchive;
12
13 namespace Private
14 {
15 class FImplicitBVH;
16 class FImplicitBVTree;
18
19 // A item in a ImplicitBVH holding the leaf geometry and transform. Each FImplicitBVHNode node holds a set of these.
21 {
22 public:
23 friend class FImplicitBVH;
24
26
29
30 const FImplicitObject* GetGeometry() const { return Geometry.GetReference(); }
31
32 const FVec3f& GetX() const { return X; }
33
34 FRotation3f GetR() const { return FRotation3f(FQuat4f(R[0], R[1], R[2], R[3])); }
35
36 const FAABB3f& GetBounds() const { return Bounds; }
37
38 const FVec3f GetBoundsCenter() const { return Bounds.Center(); }
39
41
43
44 // A unique index for this object in the hierarchy. E.g., if the same FImplicitObject is referenced
45 // multiple times in the hierarchy in a union of transformed objects, each will have a different ObjectIndex.
46 // This is the ObjectIndex assigned when visiting the hierarchy via FImplicitObject::VisitHierachy and other
47 // visit methods and can be used to index arrays initialized via those visitors.
48 // @todo(chaos): rename to GetObjectId()
49 int32 GetObjectIndex() const { return ObjectIndex; }
50
51 // The index of our most distant ancestor. I.e., the index in the root Union. This is used to map
52 // each object to a ShapeInstance.
53 int32 GetRootObjectIndex() const { return RootObjectIndex; }
54
55 private:
57
58 // Transform and bounds in the space of the BVH owner (Union Implicit)
59 float R[4]; //an alias for FRotation3f to avoid 16b alignment requirement that creates 12b of padding
60
61 FVec3f X;
62 FAABB3f Bounds;
63
64 // The index of our ancestor in the array of RootObjects that was provided when creating the BVH
65 int32 RootObjectIndex;
66
67 // The leaf geometry stripped of decorators (but not Instanced or Scaled)
69
70 // Our index in the hierarchy. This could be used to uniquely identity copies of the same implicit in the hierarchy
71 int32 ObjectIndex;
72 };
73
74 // A node in an FImplicitBVH
76 {
77 public:
79 : Bounds(InBounds)
80 , ObjectBeginIndex(InObjectBeginIndex)
81 , ObjectEndIndex(InObjectEndIndex)
82 , ChildNodeIndices{ INDEX_NONE, INDEX_NONE }
83 {
84 }
85
86 bool IsLeaf() const
87 {
88 // NOTE: We either have zero or two children
89 return ChildNodeIndices[0] == INDEX_NONE;
90 }
91
92 private:
93 friend class FImplicitBVH;
94
95 FAABB3f Bounds;
96 int32 ObjectBeginIndex;
97 int32 ObjectEndIndex;
98 int32 ChildNodeIndices[2];
99 };
100
101
102 // A Bounding Volume Hierarchy of a set of Implicit Objects
104 {
105 public:
107
109
110 // Make an empty BVH (for serialization only)
112
113 // Utility for processing the hierarchy
116 static void CollectLeafObject(const FImplicitObject* Object, const FRigidTransform3& ParentTransform, const int32 RootObjectIndex,
118
119 // Create a BVH around a set of ImplicitObjects. Usually these are the immediate child elements of an FImplcitObjectUnion
120 // TryMake will then recurse into the geometry hierachy and add all descendents to the BVH. Will return null if the
121 // number of descendents is less that MinObjects.
123
124 // Create a BVH around given a list of leaves. Will return null if the number of descendents is less that MinObjects.
126
128
129 int32 GetNumObjects() const { return Objects.Num(); }
130 int32 GetDepth() const { return TreeDepth; }
131
132 const TArray<FImplicitBVHObject>& GetObjects() const { return Objects; }
133
134 const FImplicitBVHObject& GetObject(const int32 ObjectIndex) const { return Objects[ObjectIndex]; }
135
136 const FImplicitObject* GetGeometry(const int32 ObjectIndex) const { return Objects[ObjectIndex].GetGeometry(); }
137
138 const FVec3f& GetX(const int32 ObjectIndex) const { return Objects[ObjectIndex].GetX(); }
139
140 FRotation3f GetR(const int32 ObjectIndex) const { return Objects[ObjectIndex].GetR(); }
141
142 const FAABB3f& GetBounds(const int32 ObjectIndex) const { return Objects[ObjectIndex].GetBounds(); }
143
144 FRigidTransform3f GetTransformf(const int32 ObjectIndex) const { return Objects[ObjectIndex].GetTransformf(); }
145
146 FRigidTransform3 GetTransform(const int32 ObjectIndex) const { return Objects[ObjectIndex].GetTransform(); }
147
148 int32 GetRootObjectIndex(const int32 ObjectIndex) const { return Objects[ObjectIndex].GetRootObjectIndex(); }
149 int32 GetObjectIndex(const int32 ObjectIndex) const { return Objects[ObjectIndex].GetObjectIndex(); }
150
151 // Visit all the leaf objects that overlap the specified bounds.
152 // @param ObjectVisitor [](const FImplicitObject* Implicit, const FRigidTransform3f& RelativeTransformf, const FAABB3f& RelativeBoundsf, const int32 RootObjectIndex, const int32 LeafObjectIndex) -> void {}
153 template<typename TVisitor>
154 void VisitAllIntersections(const FAABB3& LocalBounds, const TVisitor& ObjectVisitor) const
155 {
156 const auto& NodeVisitor = [this, &ObjectVisitor](const int32 NodeIndex)
157 {
158 if (NodeIsLeaf(NodeIndex))
159 {
161 }
162 };
163
164 VisitOverlappingNodesStack(FAABB3f(LocalBounds), NodeVisitor);
165 }
166
167 // Calls the visitor for every overlapping leaf.
168 // @tparam TVisitor void(const int32 NodeIndex)
169 template<typename TVisitor>
170 void VisitOverlappingNodes(const FAABB3& LocalBounds, const TVisitor& NodeVisitor) const
171 {
172 VisitOverlappingNodesStack(FAABB3f(LocalBounds), NodeVisitor);
173 }
174
175 // Recursively visit all nodes in the hierarchy. Will stop visiting children
176 // if the visitor returns false. Leaf will be null when visiting an internal node.
177 // @param NodeVisitor (const FAABB3f& NodeBounds, const int32 NodeDepth, const int32 NodeIndex) -> void
178 template<typename TVisitor>
179 void VisitNodes(const TVisitor& NodeVisitor) const
180 {
181 VisitNodesStack(NodeVisitor);
182 }
183
184
185 // Visit all the items in the specified leaf node (which is probably obtained from VisitHierarchy)
186 // @param ObjectVisitor (const FImplicitObject* Implicit, const FRigidTransform3f& RelativeTransformf, const FAABB3f& RelativeBoundsf, const int32 RootObjectIndex, const int32 LeafObjectIndex) -> void
187 template<typename TVisitor>
188 void VisitNodeObjects(const int32 NodeIndex, const TVisitor& ObjectVisitor) const
189 {
190 const FImplicitBVHNode& Node = Nodes[NodeIndex];
191
192 for (int32 NodeObjectIndex = Node.ObjectBeginIndex; NodeObjectIndex < Node.ObjectEndIndex; ++NodeObjectIndex)
193 {
194 const FImplicitBVHObject& Object = Objects[NodeObjectIndices[NodeObjectIndex]];
195
196 ObjectVisitor(Object.GetGeometry(), Object.GetTransformf(), Object.GetBounds(), Object.GetRootObjectIndex(), Object.GetObjectIndex());
197 }
198 }
199
200 // Does the bounding box overlap any leaf nodes with items in it
201 bool IsOverlappingBounds(const FAABB3& LocalBounds) const
202 {
203 return IsOverlappingBoundsStack(FAABB3f(LocalBounds));
204 }
205
206 template<typename TVisitor>
208 {
209 VisitOverlappingLeafNodesStack(BVHA, BVHB, TransformBToA, LeafPairVisitor);
210 }
211
213 {
214 return Nodes.Num();
215 }
216
217 const FAABB3f& GetNodeBounds(const int32 NodeIndex) const
218 {
219 return Nodes[NodeIndex].Bounds;
220 }
221
222 bool NodeIsLeaf(const int32 NodeIndex) const
223 {
224 return Nodes[NodeIndex].IsLeaf();
225 }
226
227 private:
228 FImplicitBVH();
231
232 // Initialize the BVH from the specified set of children.
233 // NOTE: InChildren should be immediate children of the BVH owner, not further-removed descendents
234 void Init(FObjects&& InObjects, const int32 MaxDepth, const int32 MaxLeafObjects);
235
236 template<typename TVisitor>
237 void VisitOverlappingNodesStack(const FAABB3f& LocalBounds, const TVisitor& NodeVisitor) const
238 {
239 if (Nodes.IsEmpty())
240 {
241 return;
242 }
243
244 FMemMark Mark(FMemStack::Get());
246 NodeStack.Reserve(GetDepth());
247
248 int32 NodeIndex = 0;
249 while (true)
250 {
251 const FImplicitBVHNode& Node = Nodes[NodeIndex];
252
253 if (Node.Bounds.Intersects(LocalBounds))
254 {
255 if (Node.IsLeaf())
256 {
257 NodeVisitor(NodeIndex);
258 }
259 else
260 {
261 check(NodeStack.Num() < GetDepth());
262
263 NodeIndex = Node.ChildNodeIndices[0];
264 NodeStack.Push(Node.ChildNodeIndices[1]);
265 continue;
266 }
267 }
268
269 if (NodeStack.IsEmpty())
270 {
271 break;
272 }
273
274 NodeIndex = NodeStack.Pop(EAllowShrinking::No);
275 }
276 }
277
278 template<typename TVisitor>
279 void VisitNodesStack(const TVisitor& NodeVisitor) const
280 {
281 if (Nodes.IsEmpty())
282 {
283 return;
284 }
285
286 FMemMark Mark(FMemStack::Get());
287 TArray<TPair<int32, int32>, TMemStackAllocator<>> NodeStack; // NodeIndex, NodeDepth
288 NodeStack.Reserve(GetDepth());
289
290 int32 NodeIndex = 0;
291 int32 NodeDepth = 0;
292 while (true)
293 {
294 const FImplicitBVHNode& Node = Nodes[NodeIndex];
295
296 const bool bVisitChildren = NodeVisitor(Node.Bounds, NodeDepth, NodeIndex);
297
298 if (bVisitChildren && !Node.IsLeaf())
299 {
300 check(NodeStack.Num() < GetDepth());
301
302 const int32 ChildNodeIndexL = Node.ChildNodeIndices[0];
303 const int32 ChildNodeIndexR = Node.ChildNodeIndices[1];
304 NodeDepth = NodeDepth + 1;
305 NodeIndex = ChildNodeIndexL;
306 NodeStack.Push({ ChildNodeIndexR, NodeDepth });
307 continue;
308 }
309
310 if (NodeStack.IsEmpty())
311 {
312 break;
313 }
314
315 NodeIndex = NodeStack.Top().Key;
316 NodeDepth = NodeStack.Top().Value;
317 NodeStack.Pop(EAllowShrinking::No);
318 }
319 }
320
321 template<typename TVisitor>
322 static void VisitOverlappingLeafNodesStack(const FImplicitBVH& BVHA, const FImplicitBVH& BVHB, const FRigidTransform3& TransformBToA, const TVisitor& LeafPairVisitor)
323 {
324 if (BVHA.Nodes.IsEmpty() || BVHB.Nodes.IsEmpty())
325 {
326 return;
327 }
328
329 // The node pair stack
330 FMemMark Mark(FMemStack::Get());
332 const int32 NodeStackMax = BVHA.GetDepth() + BVHB.GetDepth();
334
335 int32 NodeIndexA = 0;
336 int32 NodeIndexB = 0;
337
338 while (true)
339 {
340 const FImplicitBVHNode& NodeA = BVHA.Nodes[NodeIndexA];
341 const FImplicitBVHNode& NodeB = BVHB.Nodes[NodeIndexB];
342
343 const FAABB3f& BoundsA = NodeA.Bounds;
345
346 if (BoundsA.Intersects(BoundsBInA))
347 {
348 if (NodeA.IsLeaf() && NodeB.IsLeaf())
349 {
351 }
352 else
353 {
355
356 // @todo(chaos): rule to choose whether to descend into A or B first
357 // Descend into B first, until we reach its leaf nodes
358 const bool bDescendA = NodeB.IsLeaf();
359 if (bDescendA)
360 {
361 // Descend A
362 NodeIndexA = NodeA.ChildNodeIndices[0];
363 NodePairStack.Push({ NodeA.ChildNodeIndices[1], NodeIndexB });
364 continue;
365 }
366 else
367 {
368 // Descend B
369 NodeIndexB = NodeB.ChildNodeIndices[0];
370 NodePairStack.Push({ NodeIndexA, NodeB.ChildNodeIndices[1] });
371 continue;
372 }
373 }
374 }
375
376 // If we get here we just processed a leaf or did not overlap,
377 // and need to pop an item off the stack to continue
378 if (NodePairStack.IsEmpty())
379 {
380 break;
381 }
382 NodeIndexA = NodePairStack.Top()[0];
383 NodeIndexB = NodePairStack.Top()[1];
385 }
386 }
387
388 bool IsOverlappingBoundsStack(const FAABB3f& LocalBounds) const
389 {
390 if (Nodes.IsEmpty())
391 {
392 return false;
393 }
394
395 FMemMark Mark(FMemStack::Get());
397 NodeStack.Reserve(GetDepth());
398
399 int32 NodeIndex = 0;
400 while (true)
401 {
402 const FImplicitBVHNode& Node = Nodes[NodeIndex];
403
404 if (Node.Bounds.Intersects(LocalBounds))
405 {
406 if (Node.IsLeaf())
407 {
408 return true;
409 }
410 check(NodeStack.Num() < GetDepth());
411
412 NodeIndex = Node.ChildNodeIndices[0];
413 NodeStack.Push(Node.ChildNodeIndices[1]);
414 continue;
415 }
416
417 if (NodeStack.IsEmpty())
418 {
419 break;
420 }
421 NodeIndex = NodeStack.Pop(EAllowShrinking::No);
422 }
423
424 return false;
425 }
426
427 struct FWorkData
428 {
429 TArray<FVec3f> ObjectCenters;
430 TArray<int32> NodeObjectIndices0;
431 TArray<int32> NodeObjectIndices1;
432 };
433
434 void BuildTree(const int32 MaxDepth, const int32 MaxLeafObjects);
435 int32 AddNodeRecursive(const FAABB3f& NodeBounds, const int32 ObjectBeginIndex, const int32 ObjectEndIndex, const int32 NodeDepth, const int32 MaxDepth, const int32 MaxLeafObjects, FWorkData& WorkData);
436 bool Partition(const FAABB3f& NodeBounds, const int32 ObjectBeginIndex, const int32 ObjectEndIndex, FAABB3f& OutChildNodeBounds0, FAABB3f& OutChildNodeBounds1, int32& OutPartitionIndex, FWorkData& WorkData);
437
438 // A BVH leaf holds an array of indices into the Objects array
439 FObjects Objects;
440 TArray<int32> NodeObjectIndices;
442 int32 TreeDepth;
443 };
444 }
445
446 // DO NOT USE: This is only required for serializing the old BVH data
447 template<> inline bool HasBoundingBox(const TArray<Private::FImplicitBVHObject>& Objects, const int32 ObjectIndex) { return Objects[ObjectIndex].GetGeometry()->HasBoundingBox(); }
448 template<class T, int d> inline const TAABB<T, d> GetWorldSpaceBoundingBox(const TArray<Private::FImplicitBVHObject>& Objects, const int32 ObjectIndex, const TMap<int32, TAABB<T, d>>& WorldSpaceBoxes) { return TAABB<T, d>(Objects[ObjectIndex].GetBounds()); }
449 template<class T, int d> void ComputeAllWorldSpaceBoundingBoxes(const TArray<Private::FImplicitBVHObject>& Objects, const TArray<int32>& AllObjects, const bool bUseVelocity, const T Dt, TMap<int32, TAABB<T, d>>& WorldSpaceBoxes) { }
450}
#define check(expr)
Definition AssertionMacros.h:314
@ INDEX_NONE
Definition CoreMiscDefines.h:150
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 X(Name, Desc)
Definition FormatStringSan.h:47
void Init()
Definition LockFreeList.h:4
UE::Math::TQuat< float > FQuat4f
Definition MathFwd.h:76
Definition ChaosArchive.h:167
Definition ImplicitObject.h:111
Definition ImplicitObjectBVH.h:76
FImplicitBVHNode(const FAABB3f &InBounds, const int32 InObjectBeginIndex, const int32 InObjectEndIndex)
Definition ImplicitObjectBVH.h:78
bool IsLeaf() const
Definition ImplicitObjectBVH.h:86
Definition ImplicitObjectBVH.h:21
const FVec3f & GetX() const
Definition ImplicitObjectBVH.h:32
int32 GetRootObjectIndex() const
Definition ImplicitObjectBVH.h:53
FImplicitBVHObject()
Definition ImplicitObjectBVH.cpp:19
const FImplicitObject * GetGeometry() const
Definition ImplicitObjectBVH.h:30
int32 GetObjectIndex() const
Definition ImplicitObjectBVH.h:49
FRotation3f GetR() const
Definition ImplicitObjectBVH.h:34
const FAABB3f & GetBounds() const
Definition ImplicitObjectBVH.h:36
FRigidTransform3f GetTransformf() const
Definition ImplicitObjectBVH.h:40
FRigidTransform3 GetTransform() const
Definition ImplicitObjectBVH.h:42
const FVec3f GetBoundsCenter() const
Definition ImplicitObjectBVH.h:38
friend FChaosArchive & operator<<(FChaosArchive &Ar, FImplicitBVHObject &BVHObjecty)
Definition ImplicitObjectBVH.cpp:324
Definition ImplicitObjectBVH.h:104
int32 GetNumNodes() const
Definition ImplicitObjectBVH.h:212
static void VisitOverlappingLeafNodes(const FImplicitBVH &BVHA, const FImplicitBVH &BVHB, const FRigidTransform3 &TransformBToA, const TVisitor &LeafPairVisitor)
Definition ImplicitObjectBVH.h:207
static TUniquePtr< FImplicitBVH > TryMakeFromLeaves(TArray< FImplicitBVHObject > &&LeafObjects, const int32 InMinObjects, const int32 InMaxBVHDepth)
Definition ImplicitObjectBVH.cpp:117
static TUniquePtr< FImplicitBVH > TryMake(const TArrayView< const Chaos::FImplicitObjectPtr > &InRootObjects, const int32 MinObjects, const int32 InMaxBVHDepth)
Definition ImplicitObjectBVH.cpp:111
void VisitAllIntersections(const FAABB3 &LocalBounds, const TVisitor &ObjectVisitor) const
Definition ImplicitObjectBVH.h:154
FRigidTransform3 GetTransform(const int32 ObjectIndex) const
Definition ImplicitObjectBVH.h:146
int32 GetDepth() const
Definition ImplicitObjectBVH.h:130
const TArray< FImplicitBVHObject > & GetObjects() const
Definition ImplicitObjectBVH.h:132
static void CollectLeafObject(const FImplicitObject *Object, const FRigidTransform3 &ParentTransform, const int32 RootObjectIndex, TArray< FImplicitBVHObject > &LeafObjects, const int32 LeafObjectIndex)
Definition ImplicitObjectBVH.cpp:70
bool NodeIsLeaf(const int32 NodeIndex) const
Definition ImplicitObjectBVH.h:222
const FImplicitObject * GetGeometry(const int32 ObjectIndex) const
Definition ImplicitObjectBVH.h:136
TArray< FImplicitBVHObject > FObjects
Definition ImplicitObjectBVH.h:106
FRigidTransform3f GetTransformf(const int32 ObjectIndex) const
Definition ImplicitObjectBVH.h:144
const FAABB3f & GetNodeBounds(const int32 NodeIndex) const
Definition ImplicitObjectBVH.h:217
const FVec3f & GetX(const int32 ObjectIndex) const
Definition ImplicitObjectBVH.h:138
void VisitNodeObjects(const int32 NodeIndex, const TVisitor &ObjectVisitor) const
Definition ImplicitObjectBVH.h:188
int32 GetObjectIndex(const int32 ObjectIndex) const
Definition ImplicitObjectBVH.h:149
~FImplicitBVH()
Definition ImplicitObjectBVH.cpp:144
bool IsOverlappingBounds(const FAABB3 &LocalBounds) const
Definition ImplicitObjectBVH.h:201
const FImplicitBVHObject & GetObject(const int32 ObjectIndex) const
Definition ImplicitObjectBVH.h:134
int32 GetNumObjects() const
Definition ImplicitObjectBVH.h:129
static TUniquePtr< FImplicitBVH > MakeEmpty()
Definition ImplicitObjectBVH.cpp:106
int32 GetRootObjectIndex(const int32 ObjectIndex) const
Definition ImplicitObjectBVH.h:148
void VisitNodes(const TVisitor &NodeVisitor) const
Definition ImplicitObjectBVH.h:179
FRotation3f GetR(const int32 ObjectIndex) const
Definition ImplicitObjectBVH.h:140
static FObjects CollectLeafObjects(const TArrayView< const Chaos::FImplicitObjectPtr > &InRootObjects)
Definition ImplicitObjectBVH.cpp:86
friend FChaosArchive & operator<<(FChaosArchive &Ar, FImplicitBVH &BVH)
Definition ImplicitObjectBVH.cpp:319
static int32 CountLeafObjects(const TArrayView< const Chaos::FImplicitObjectPtr > &InRootObjects)
Definition ImplicitObjectBVH.cpp:44
void VisitOverlappingNodes(const FAABB3 &LocalBounds, const TVisitor &NodeVisitor) const
Definition ImplicitObjectBVH.h:170
const FAABB3f & GetBounds(const int32 ObjectIndex) const
Definition ImplicitObjectBVH.h:142
FORCEINLINE TVector< T, d > Center() const
Definition AABB.h:450
CHAOSCORE_API TAABB< T, d > TransformedAABB(const FTransform &) const
Definition AABB.cpp:385
Definition Rotation.h:49
Definition Serializable.h:10
Definition Vector.h:407
Definition MemStack.h:506
Definition ArrayView.h:139
UE_REWRITE SizeType Num() const
Definition Array.h:1144
UE_NODEBUG UE_FORCEINLINE_HINT void Push(ElementType &&Item)
Definition Array.h:1224
UE_REWRITE bool IsEmpty() const
Definition Array.h:1133
ElementType Pop(EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:1196
UE_NODEBUG UE_FORCEINLINE_HINT ElementType & Top() UE_LIFETIMEBOUND
Definition Array.h:1248
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition UnrealString.h.inl:34
Definition MemStack.h:391
static FORCEINLINE FMemStack & Get()
Definition ThreadSingleton.h:101
Definition UniquePtr.h:107
Definition SkeletalMeshComponent.h:307
const TAABB< T, d > GetWorldSpaceBoundingBox(const OBJECT_ARRAY &Objects, const int32 i, const TMap< int32, TAABB< T, d > > &WorldSpaceBoxes)
Definition BoundingVolumeUtilities.h:143
TRigidTransform< FReal, 3 > FRigidTransform3
Definition Core.h:22
TRotation< FReal, 3 > FRotation3
Definition Core.h:19
bool HasBoundingBox(const OBJECT_ARRAY &Objects, const int32 i)
Definition BoundingVolumeUtilities.h:97
TVector< FReal, 3 > FVec3
Definition Core.h:17
TAABB< FRealSingle, 3 > FAABB3f
Definition Core.h:32
TRigidTransform< FRealSingle, 3 > FRigidTransform3f
Definition Core.h:29
void ComputeAllWorldSpaceBoundingBoxes(const OBJECT_ARRAY &Objects, const TArray< int32 > &AllObjects, const bool bUseVelocity, const T Dt, TMap< int32, TAABB< T, d > > &WorldSpaceBoxes)
Definition BoundingVolumeUtilities.h:329
TRotation< FRealSingle, 3 > FRotation3f
Definition Core.h:28
Definition OverriddenPropertySet.cpp:45