UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
SparseDynamicOctree3.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5
6#include "Async/ParallelFor.h"
7#include "BoxTypes.h"
8#include "Containers/Array.h"
10#include "Containers/Map.h"
11#include "Containers/Set.h"
14#include "CoreMinimal.h"
15#include "GeometryTypes.h"
16#include "HAL/PlatformCrt.h"
17#include "IntVectorTypes.h"
19#include "Math/NumericLimits.h"
20#include "Math/Ray.h"
21#include "Math/UnrealMathSSE.h"
22#include "Math/Vector.h"
23#include "MathUtil.h"
25#include "Spatial/SparseGrid3.h"
26#include "Util/DynamicVector.h"
27#include "Util/RefCountVector.h"
28#include "Util/SmallListSet.h"
29
30template <typename FuncType> class TFunctionRef;
31
32namespace UE
33{
34namespace Geometry
35{
36
42{
43public:
46 static constexpr int32 GrowChunkSize = 0xFFF;
47
53
54 void Set(uint32 BitIndex, bool bValue)
55 {
56 if (bValue && BitIndex >= MaxIndex)
57 {
58 int32 ToAdd = (BitIndex - MaxIndex + 1) | GrowChunkSize;
59 BitArray.Add(false, ToAdd);
61 BitArray[BitIndex] = bValue;
62 }
63 else if (BitIndex < MaxIndex)
64 {
65 BitArray[BitIndex] = bValue;
66 }
67 // don't need to set anything for false bValue with BitIndex beyond bounds; Get() will return false beyond bounds
68 }
69
70 inline bool Get(uint32 BitIndex) const
71 {
72 return (BitIndex < MaxIndex) ? BitArray[BitIndex] : false;
73 }
74};
75
76
77
78
83{
86
89
92
95
98
101 {
102 Children[0] = Children[1] = Children[2] = Children[3] = InvalidID;
103 Children[4] = Children[5] = Children[6] = Children[7] = InvalidID;
104 }
105
112
113 inline bool IsExistingCell() const
114 {
115 return CellID != InvalidID;
116 }
117
118 inline bool HasChild(int ChildIndex) const
119 {
120 return Children[ChildIndex] != InvalidID;
121 }
122
123 inline uint32 GetChildCellID(int ChildIndex) const
124 {
125 return Children[ChildIndex];
126 }
127
128 inline FSparseOctreeCell MakeChildCell(int ChildIndex)
129 {
130 FVector3i IndexOffset(
131 ((ChildIndex & 1) != 0) ? 1 : 0,
132 ((ChildIndex & 2) != 0) ? 1 : 0,
133 ((ChildIndex & 4) != 0) ? 1 : 0);
134 return FSparseOctreeCell(Level + 1, 2*Index + IndexOffset);
135 }
136
137 inline void SetChild(uint32 ChildIndex, const FSparseOctreeCell& ChildCell)
138 {
139 Children[ChildIndex] = ChildCell.CellID;
140 }
141
142};
143
144
165{
166 // potential optimizations/improvements
167 // - Cell sizes are known at each level...can keep a lookup table? will this improve performance?
168 // - Store cells for each level in separate TDynamicVectors. CellID is then [Level:8 | Index:24].
169 // This would allow level-grids to be processed separately / in-parallel (for example a cut at given level would be much faster)
170 // - Currently insertion is max-depth but we do not dynamically expand more than once. So early
171 // insertions end up in very large buckets. When a child expands we should check if any of its parents would fit.
172 // - Currently insertion is max-depth so we end up with a huge number of single-object cells. Should only go down a level
173 // if enough objects exist in current cell. Can do this in a greedy fashion, less optimal but still acceptable...
174 // - Store an expand-factor for each cell? or an actual AABB for each cell? this would allow for tighter bounds but
175 // requires accumulating expansion "up" the tree...
176 // - get rid of ValidObjectIDs, I don't think we need it?
177
178public:
179
180 //
181 // Tree configuration parameters. It is not safe to change these after tree initialization!
182 //
183
187 double RootDimension = 1000.0;
188
192 double MaxExpandFactor = 0.25;
193
194 static constexpr uint32 MaxSupportedTreeDepth = 0x1F;
195
197 {
198 return MaxTreeDepth;
199 }
212protected:
216 int MaxTreeDepth = 10;
217
218
219public:
220
226 GEOMETRYCORE_API bool ContainsObject(int32 ObjectID) const;
227
234 GEOMETRYCORE_API void InsertObject(int32 ObjectID, const FAxisAlignedBox3d& Bounds);
235
241 GEOMETRYCORE_API bool RemoveObject(int32 ObjectID);
242
250
259
260
271 TFunctionRef<double(int, const FRay3d&)> HitObjectDistFunc,
273
280 TFunctionRef<void(int)> ObjectIDFunc) const;
281
289 TFunctionRef<bool(int)> ObjectIDFunc) const;
290
297 TFunctionRef<void(int)> ObjectIDFunc) const;
298
305 TArray<int>& ObjectIDsOut ) const;
306
315
327 TFunctionRef<bool (const FAxisAlignedBox3d& CellBounds)> BoundsOverlapFn,
328 TArray<int32>& ObjectIDs) const;
329
341
350 {
352 // Note a bounds vs ShapeBounds test will always be performed automatically;
353 // the optional bounds overlap fn here can just return true to indicate no additional filtering is performed
354 [](const FAxisAlignedBox3d&) {return true;}
355 );
356 }
357
358
371 bool bVerbose = false,
372 bool bFailOnMissingObjects = false) const;
373
385
390
391protected:
392 // this identifier is used for unknown cells
394
395 // if an object is in the spill cell, that means it didn't fit in the tree
396 static constexpr uint32 SpillCellID = InvalidCellID - 1;
397
398 // reference counts for Cells list. We don't actually need reference counts here, but we need a free
399 // list and iterators, and FRefCountVector provides this
401
402 // list of cells. Note that some cells may be unused, depending on CellRefCounts
404
405 // TODO: Consider switching FSmallListSet to TSparseListSet<int32> for more reliable performance in cases where we have more than 8 objects in many cells
406 FSmallListSet CellObjectLists; // per-cell object ID lists
407 TSet<int32> SpillObjectSet; // list of object IDs for objects that didn't fit in a root cell
408
409 TDynamicVector<uint32> ObjectIDToCellMap; // map from external Object IDs to which cell the object is in (or spill cell, or invalid)
410 FDynamicFlagArray ValidObjectIDs; // set of ObjectIDs in the tree. This is perhaps not necessary...couldn't we rely on ObjectIDToCellMap?
411
412 // RootCells are the top-level cells of the octree, of size RootDimension.
413 // So the elements of this sparse grid are CellIDs
415
416
417 // calculate the base width of a cell at a given level
418 inline double GetCellWidth(uint32 Level) const
419 {
421 double Divisor = (double)( (uint64)1 << (Level & MaxSupportedTreeDepth) );
422 double CellWidth = RootDimension / Divisor;
423 return CellWidth;
424 }
425
426
428 {
429 double CellWidth = GetCellWidth(Level);
431 double MinX = (CellWidth * (double)Index.X) - ExpandDelta;
432 double MinY = (CellWidth * (double)Index.Y) - ExpandDelta;
433 double MinZ = (CellWidth * (double)Index.Z) - ExpandDelta;
434 CellWidth += 2.0 * ExpandDelta;
435 return FAxisAlignedBox3d(
436 FVector3d(MinX, MinY, MinZ),
437 FVector3d(MinX + CellWidth, MinY + CellWidth, MinZ + CellWidth));
438 }
440 {
441 return GetBox(Cell.Level, Cell.Index, ExpandFactor);
442 }
444 {
445 double CellWidth = GetCellWidth(Cell.Level);
446 double MinX = CellWidth * (double)Cell.Index.X;
447 double MinY = CellWidth * (double)Cell.Index.Y;
448 double MinZ = CellWidth * (double)Cell.Index.Z;
449 CellWidth *= 0.5;
450 return FVector3d(MinX + CellWidth, MinY + CellWidth, MinZ + CellWidth);
451 }
452
453
454 // warning: result here appears to be unstable (due to optimization?) if any of the position values are on the border of a cell
455 // (in testing, pragma-optimization-disabled code produced off-by-one result from calling this function)
457 {
458 double CellWidth = GetCellWidth(Level);
459
460 // Converts to an int32 in such a way that values outside of the representable
461 // range ends up at the min and max values of the representable range.
462 auto SafeDoubleToInt32Floor = [](double Value)
463 {
464 return static_cast<int32>(FMath::Clamp(
466 static_cast<double>(TNumericLimits<int32>::Lowest()),
467 static_cast<double>(TNumericLimits<int32>::Max())));
468 };
469
473 return FVector3i(i, j, k);
474 }
475
476
478 {
480 int ChildIndex =
481 ((Position.X < Center.X) ? 0 : 1) +
482 ((Position.Y < Center.Y) ? 0 : 2) +
483 ((Position.Z < Center.Z) ? 0 : 4);
484 return ChildIndex;
485 }
486
487 bool CanFit(const FSparseOctreeCell& Cell, const FAxisAlignedBox3d& Bounds) const
488 {
490 return CellBox.Contains(Bounds);
491 }
492
494 {
495 if (ObjectID >= 0 && static_cast<size_t>(ObjectID) < ObjectIDToCellMap.Num())
496 {
497 return ObjectIDToCellMap[ObjectID];
498 }
499 return InvalidCellID;
500 }
501
503
504
505 GEOMETRYCORE_API void Insert_Spill(int32 ObjectID, const FAxisAlignedBox3d& Bounds);
509
511
512
514 const FAxisAlignedBox3d& Bounds,
515 TArray<int>& ObjectIDs) const;
516
517private:
518 // Gather objects in all cells that pass BoundsOverlapFn, starting from a set of root cells
519 void ParallelCollectObjectsInOverlappingCells(
521 TFunctionRef<bool(const FAxisAlignedBox3d& CellBounds)> BoundsOverlapFn,
522 TArray<int32>& ObjectIDs) const;
523 // Go down subtree of ParentCell and collect all objects in cells that pass BoundsOverlapFn.
526 TArray<int>& ObjectIDs) const;
527
528 int BranchCustomOverlapAnyQuery(const FSparseOctreeCell* ParentCell,
529 const FAxisAlignedBox3d& Bounds,
531
545 TFunctionRef<bool(const FAxisAlignedBox3d& CellBounds)> BoundsOverlapFn,
546 bool bCheckOverlapFnInRangeBounds = true) const;
547
548 // helper to find the root-level cells that could intersect with a given query object, specialized for point containment queries
550 {
551 return InitializeQueryQueue(FAxisAlignedBox3d(Point, Point),
552 [&Point](const FAxisAlignedBox3d& CellBounds) { return CellBounds.Contains(Point); },
553 false);
554 }
555
556 // helper to find the root-level cells that could intersect with a given query object, specialized for box queries
557 TArray<const FSparseOctreeCell*, TInlineAllocator<32>> InitializeQueryQueue(const FAxisAlignedBox3d& Bounds) const
558 {
559 return InitializeQueryQueue(Bounds,
560 [&Bounds](const FAxisAlignedBox3d& CellBounds) { return CellBounds.Intersects(Bounds); },
561 false);
562 }
563};
564
565
566} // end namespace UE::Geometry
567} // end namespace UE
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define ensure( InExpression)
Definition AssertionMacros.h:464
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
FPlatformTypes::uint64 uint64
A 64-bit unsigned integer.
Definition Platform.h:1117
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
UE::Math::TVector< double > FVector3d
Definition MathFwd.h:60
uint8_t uint8
Definition binka_ue_file_header.h:8
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition Array.h:670
UE_FORCEINLINE_HINT int32 Num() const
Definition BitArray.h:1466
int32 Add(const bool Value)
Definition BitArray.h:615
UE_FORCEINLINE_HINT void Init(bool bValue, int32 InNumBits)
Definition BitArray.h:828
Definition AssetRegistryState.h:50
static RealType Floor(const RealType Value)
Definition MathUtil.h:384
Definition ContainerAllocationPolicies.h:894
Definition StaticArray.h:26
Definition SparseDynamicOctree3.h:42
TBitArray BitArray
Definition SparseDynamicOctree3.h:44
void Set(uint32 BitIndex, bool bValue)
Definition SparseDynamicOctree3.h:54
FDynamicFlagArray()
Definition SparseDynamicOctree3.h:48
bool Get(uint32 BitIndex) const
Definition SparseDynamicOctree3.h:70
static constexpr int32 GrowChunkSize
Definition SparseDynamicOctree3.h:46
uint32 MaxIndex
Definition SparseDynamicOctree3.h:45
Definition RefCountVector.h:25
Definition SmallListSet.h:36
Definition SparseDynamicOctree3.h:165
int GetMaxTreeDepth()
Definition SparseDynamicOctree3.h:196
GEOMETRYCORE_API void Insert_Spill(int32 ObjectID, const FAxisAlignedBox3d &Bounds)
Definition SparseDynamicOctree3.cpp:124
GEOMETRYCORE_API void CheckValidity(TFunctionRef< bool(int)> IsValidObjectIDFunc, TFunctionRef< FAxisAlignedBox3d(int)> GetObjectBoundsFunc, EValidityCheckFailMode FailMode=EValidityCheckFailMode::Check, bool bVerbose=false, bool bFailOnMissingObjects=false) const
Definition SparseDynamicOctree3.cpp:806
void SetMaxTreeDepth(int MaxTreeDepthIn)
Definition SparseDynamicOctree3.h:203
GEOMETRYCORE_API FSparseOctreeCell FindCurrentContainingCell(const FAxisAlignedBox3d &Bounds) const
Definition SparseDynamicOctree3.cpp:752
GEOMETRYCORE_API int ParallelOverlapAnyQuery(const FAxisAlignedBox3d &ShapeBounds, TFunctionRef< bool(int32)> ObjectOverlapFn, TFunctionRef< bool(const FAxisAlignedBox3d &)> BoundsOverlapFn) const
Definition SparseDynamicOctree3.cpp:628
double RootDimension
Definition SparseDynamicOctree3.h:187
GEOMETRYCORE_API void Insert_NewRoot(int32 ObjectID, const FAxisAlignedBox3d &Bounds, FSparseOctreeCell NewRootCell)
Definition SparseDynamicOctree3.cpp:105
GEOMETRYCORE_API bool CheckIfObjectNeedsReinsert(int32 ObjectID, const FAxisAlignedBox3d &NewBounds, uint32 &CellIDOut) const
Definition SparseDynamicOctree3.cpp:165
TDynamicVector< uint32 > ObjectIDToCellMap
Definition SparseDynamicOctree3.h:409
FDynamicFlagArray ValidObjectIDs
Definition SparseDynamicOctree3.h:410
static constexpr uint32 MaxSupportedTreeDepth
Definition SparseDynamicOctree3.h:194
GEOMETRYCORE_API bool ContainsObject(int32 ObjectID) const
Definition SparseDynamicOctree3.cpp:19
int ParallelOverlapAnyQuery(const FAxisAlignedBox3d &ShapeBounds, TFunctionRef< bool(int32)> ObjectOverlapFn) const
Definition SparseDynamicOctree3.h:348
FVector3i PointToIndex(uint32 Level, const FVector3d &Position) const
Definition SparseDynamicOctree3.h:456
GEOMETRYCORE_API void ComputeStatistics(FStatistics &StatsOut) const
Definition SparseDynamicOctree3.cpp:911
GEOMETRYCORE_API double FindNearestRayCellIntersection(const FSparseOctreeCell &Cell, const FRay3d &Ray) const
Definition SparseDynamicOctree3.cpp:228
GEOMETRYCORE_API void Insert_ToCell(int32 ObjectID, const FAxisAlignedBox3d &Bounds, const FSparseOctreeCell &ExistingCell)
Definition SparseDynamicOctree3.cpp:90
double GetCellWidth(uint32 Level) const
Definition SparseDynamicOctree3.h:418
GEOMETRYCORE_API bool ContainmentQueryCancellable(const FVector3d &Point, TFunctionRef< bool(int)> ObjectIDFunc) const
Definition SparseDynamicOctree3.cpp:428
int ToChildCellIndex(const FSparseOctreeCell &Cell, const FVector3d &Position) const
Definition SparseDynamicOctree3.h:477
static constexpr uint32 SpillCellID
Definition SparseDynamicOctree3.h:396
GEOMETRYCORE_API void ParallelRangeQuery(const FAxisAlignedBox3d &Bounds, TArray< int > &ObjectIDsOut) const
Definition SparseDynamicOctree3.cpp:562
FVector3d GetCellCenter(const FSparseOctreeCell &Cell) const
Definition SparseDynamicOctree3.h:443
int MaxTreeDepth
Definition SparseDynamicOctree3.h:216
TSet< int32 > SpillObjectSet
Definition SparseDynamicOctree3.h:407
FSmallListSet CellObjectLists
Definition SparseDynamicOctree3.h:406
double MaxExpandFactor
Definition SparseDynamicOctree3.h:192
GEOMETRYCORE_API void RangeQuery(const FAxisAlignedBox3d &Bounds, TFunctionRef< void(int)> ObjectIDFunc) const
Definition SparseDynamicOctree3.cpp:481
TDynamicVector< FSparseOctreeCell > Cells
Definition SparseDynamicOctree3.h:403
GEOMETRYCORE_API void InsertObject(int32 ObjectID, const FAxisAlignedBox3d &Bounds)
Definition SparseDynamicOctree3.cpp:24
GEOMETRYCORE_API int32 FindNearestHitObject(const FRay3d &Ray, TFunctionRef< FAxisAlignedBox3d(int)> GetObjectBoundsFunc, TFunctionRef< double(int, const FRay3d &)> HitObjectDistFunc, double MaxDistance=TNumericLimits< double >::Max()) const
Definition SparseDynamicOctree3.cpp:244
uint32 GetCellForObject(int32 ObjectID) const
Definition SparseDynamicOctree3.h:493
bool CanFit(const FSparseOctreeCell &Cell, const FAxisAlignedBox3d &Bounds) const
Definition SparseDynamicOctree3.h:487
FAxisAlignedBox3d GetBox(uint32 Level, const FVector3i &Index, double ExpandFactor) const
Definition SparseDynamicOctree3.h:427
FAxisAlignedBox3d GetCellBox(const FSparseOctreeCell &Cell, double ExpandFactor=0) const
Definition SparseDynamicOctree3.h:439
GEOMETRYCORE_API void ContainmentQuery(const FVector3d &Point, TFunctionRef< void(int)> ObjectIDFunc) const
Definition SparseDynamicOctree3.cpp:389
TSparseGrid3< uint32 > RootCells
Definition SparseDynamicOctree3.h:414
FRefCountVector CellRefCounts
Definition SparseDynamicOctree3.h:400
static constexpr uint32 InvalidCellID
Definition SparseDynamicOctree3.h:393
GEOMETRYCORE_API void Insert_NewChildCell(int32 ObjectID, const FAxisAlignedBox3d &Bounds, int ParentCellID, FSparseOctreeCell NewChildCell, int ChildIdx)
Definition SparseDynamicOctree3.cpp:67
GEOMETRYCORE_API bool ReinsertObject(int32 ObjectID, const FAxisAlignedBox3d &NewBounds, uint32 CellIDHint=InvalidCellID)
Definition SparseDynamicOctree3.cpp:185
GEOMETRYCORE_API void BranchRangeQuery(const FSparseOctreeCell *ParentCell, const FAxisAlignedBox3d &Bounds, TArray< int > &ObjectIDs) const
Definition SparseDynamicOctree3.cpp:662
Definition DynamicVector.h:27
size_t Num() const
Definition DynamicVector.h:147
Definition SparseGrid3.h:27
TAxisAlignedBox3< double > FAxisAlignedBox3d
Definition BoxTypes.h:885
EValidityCheckFailMode
Definition GeometryTypes.h:72
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
Definition NumericLimits.h:41
Definition SparseDynamicOctree3.h:378
int32 SpillObjCount
Definition SparseDynamicOctree3.h:382
TArray< int32 > LevelObjCounts
Definition SparseDynamicOctree3.h:381
FString ToString() const
Definition SparseDynamicOctree3.cpp:932
int32 Levels
Definition SparseDynamicOctree3.h:379
TArray< int32 > LevelBoxCounts
Definition SparseDynamicOctree3.h:380
Definition SparseDynamicOctree3.h:83
FSparseOctreeCell()
Definition SparseDynamicOctree3.h:99
void SetChild(uint32 ChildIndex, const FSparseOctreeCell &ChildCell)
Definition SparseDynamicOctree3.h:137
static constexpr uint8 InvalidLevel
Definition SparseDynamicOctree3.h:85
bool IsExistingCell() const
Definition SparseDynamicOctree3.h:113
uint32 GetChildCellID(int ChildIndex) const
Definition SparseDynamicOctree3.h:123
TStaticArray< uint32, 8 > Children
Definition SparseDynamicOctree3.h:97
static constexpr uint32 InvalidID
Definition SparseDynamicOctree3.h:84
bool HasChild(int ChildIndex) const
Definition SparseDynamicOctree3.h:118
FSparseOctreeCell(uint8 LevelIn, const FVector3i &IndexIn)
Definition SparseDynamicOctree3.h:106
uint8 Level
Definition SparseDynamicOctree3.h:91
FSparseOctreeCell MakeChildCell(int ChildIndex)
Definition SparseDynamicOctree3.h:128
FVector3i Index
Definition SparseDynamicOctree3.h:94
uint32 CellID
Definition SparseDynamicOctree3.h:88
Definition IntVectorTypes.h:252
bool Contains(const TVector< RealType > &V) const
Definition BoxTypes.h:492