UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
AABBTree.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2#pragma once
3
4#include "Chaos/AABB.h"
8#include "Chaos/Defines.h"
10#include "Chaos/Transform.h"
11#include "ChaosLog.h"
13#include "Templates/Models.h"
16#include "ChaosStats.h"
17#include "Math/VectorRegister.h"
18#include <type_traits>
19
20#ifndef CHAOS_DEBUG_NAME
21#define CHAOS_DEBUG_NAME 0
22#endif
23
25
47
62
77
79
80namespace Chaos
81{
82
84{
85 Raycast,
86 Sweep,
88};
89
91{
99
101 {
102 StatNumNonEmptyCellsInGrid += Rhs.StatNumNonEmptyCellsInGrid;
103 StatNumElementsTooLargeForGrid += Rhs.StatNumElementsTooLargeForGrid;
104 StatNumDirtyElements += Rhs.StatNumDirtyElements;
105 StatNumGridOverflowElements += Rhs.StatNumGridOverflowElements;
106 return *this;
107 }
108
113};
114
116{
117 void Reset()
118 {
121 StatMaxLeafSize = 0;
124 }
125
127 {
128 StatMaxNumLeaves = FMath::Max(StatMaxNumLeaves, Rhs.StatMaxNumLeaves);
129 StatMaxDirtyElements = FMath::Max(StatMaxDirtyElements, Rhs.StatMaxDirtyElements);
130 StatMaxLeafSize = FMath::Max(StatMaxLeafSize, Rhs.StatMaxLeafSize);
131 StatMaxTreeDepth = FMath::Max(StatMaxTreeDepth, Rhs.StatMaxTreeDepth);
132 StatGlobalPayloadsSize += Rhs.StatGlobalPayloadsSize;
133 return *this;
134 }
140};
141
146//DECLARE_CYCLE_STAT(TEXT("AABBTreeGrowPhase"), STAT_AABBTreeGrowPhase, STATGROUP_Chaos);
147//DECLARE_CYCLE_STAT(TEXT("AABBTreeChildrenPhase"), STAT_AABBTreeChildrenPhase, STATGROUP_Chaos);
148
150{
151 template<typename ElementT>
152 auto Requires(ElementT& InElem, const ElementT& InOtherElem) -> decltype(InElem.UpdateFrom(InOtherElem));
153};
154
155template<typename T, typename TEnableIf<!TModels_V<CIsUpdatableElement, T>>::Type* = nullptr>
156static void UpdateElementHelper(T& InElem, const T& InFrom)
157{
158
159}
160
161template<typename T, typename TEnableIf<TModels_V<CIsUpdatableElement, T>>::Type* = nullptr>
162static void UpdateElementHelper(T& InElem, const T& InFrom)
163{
165 {
166 InElem.UpdateFrom(InFrom);
167 }
168}
169
170template <typename TQueryFastData, EAABBQueryType Query>
172{
173 static bool Intersects(const FVec3& Start, TQueryFastData& QueryFastData, FReal& TOI,
174 const FAABB3& Bounds, const FAABB3& QueryBounds, const FVec3& QueryHalfExtents, const FVec3& Dir, const FVec3 InvDir, const bool bParallel[3])
175 {
176 check(false);
177 return true;
178 }
179};
180
181template<>
183{
184 FORCEINLINE_DEBUGGABLE static bool Intersects(const FVec3& Start, FQueryFastData& QueryFastData, FReal& TOI,
185 const FAABB3& Bounds, const FAABB3& QueryBounds, const FVec3& QueryHalfExtents, const FVec3& Dir, const FVec3 InvDir, const bool bParallel[3])
186 {
188 return Bounds.RaycastFast(Start, Dir, InvDir, bParallel, QueryFastData.CurrentLength, QueryFastData.InvCurrentLength, TOI, TmpExitTime);
189 }
190
191};
192
193template <>
195{
196 FORCEINLINE_DEBUGGABLE static bool Intersects(const FVec3& Start, FQueryFastData& QueryFastData, FReal& TOI,
197 const FAABB3& Bounds, const FAABB3& QueryBounds, const FVec3& QueryHalfExtents, const FVec3& Dir, const FVec3 InvDir, const bool bParallel[3])
198 {
201 return SweepBounds.RaycastFast(Start, Dir, InvDir, bParallel, QueryFastData.CurrentLength, QueryFastData.InvCurrentLength, TOI, TmpExitTime);
202 }
203
204};
205
206template <>
208{
209 FORCEINLINE_DEBUGGABLE static bool Intersects(const FVec3& Start, FQueryFastDataVoid& QueryFastData, FReal& TOI,
210 const FAABB3& Bounds, const FAABB3& QueryBounds, const FVec3& QueryHalfExtents, const FVec3& Dir, const FVec3 InvDir, const bool bParallel[3])
211 {
212 return QueryBounds.Intersects(Bounds);
213 }
214};
215
216template <typename TPayloadType, typename T, bool bComputeBounds>
218{
219};
220
221template <typename TPayloadType, typename T>
222struct TBoundsWrapperHelper<TPayloadType, T, true>
223{
224 void ComputeBounds(const TArray<TPayloadBoundsElement<TPayloadType, T>>& Elems, bool bDynamicTree)
225 {
226 Bounds = TAABB<T, 3>::EmptyAABB();
227
228 for (const auto& Elem : Elems)
229 {
230 Bounds.GrowToInclude(Elem.Bounds);
231 }
232 if (!Elems.IsEmpty() && bDynamicTree)
233 {
234 // Grow the aabb leaf of percentage of its size
235 Bounds.ThickenSymmetrically(Bounds.Extents() * FAABBTreeCVars::DynamicTreeLeafEnlargePercent*0.5);
236 }
237 }
238
239 const TAABB<T, 3>& GetBounds() const { return Bounds; }
240
241private:
242 TAABB<T, 3> Bounds;
243};
244
245template <typename TPayloadType, typename T>
246struct TBoundsWrapperHelper<TPayloadType, T, false>
247{
249 {
250 }
251
252 const TAABB<T, 3> GetBounds() const
253 {
254 return TAABB<T, 3>::EmptyAABB();
255 }
256};
257
258template <typename TPayloadType, bool bComputeBounds = true, typename T = FReal>
259struct TAABBTreeLeafArray : public TBoundsWrapperHelper<TPayloadType, T, bComputeBounds>
260{
262 //todo: avoid copy?
264 : Elems(InElems)
265 {
266 this->ComputeBounds(Elems, false);
267 }
268
273
275 {
276 // Optimize for fewer memory allocations.
277 return Elems.Num();
278 }
279
281 {
282 return Elems.Num();
283 }
284
285 void RecomputeBounds(bool bDynamicTree)
286 {
287 this->ComputeBounds(Elems, bDynamicTree);
288 }
289
293 bool IsLeafDirty() const
294 {
295 return bDirtyLeaf;
296 }
297
301 void SetDirtyState(const bool bDirtyState)
302 {
304 }
305
306 template <typename TSQVisitor, typename TQueryFastData>
307 FORCEINLINE_DEBUGGABLE bool RaycastFast(const TVec3<T>& Start, TQueryFastData& QueryFastData, TSQVisitor& Visitor, const TVec3<T>& Dir, const TVec3<T>& InvDir, const bool bParallel[3]) const
308 {
309 return RaycastSweepImp</*bSweep=*/false>(Start, QueryFastData, TVec3<T>((T)0), Visitor, Dir, InvDir, bParallel);
310 }
311
312 template <typename TSQVisitor, typename TQueryFastData>
314 {
315 return RaycastImpSimd(Start, QueryFastData, Visitor, InvDir, Parallel, Length);
316 }
317
318 template <typename TSQVisitor, typename TQueryFastData>
320 const TVec3<T>& Dir, const TVec3<T> InvDir, const bool bParallel[3]) const
321 {
322 return RaycastSweepImp</*bSweep=*/true>(Start, QueryFastData, QueryHalfExtents, Visitor, Dir, InvDir, bParallel);
323 }
324
325 template <typename TSQVisitor>
326 bool OverlapFast(const FAABB3& QueryBounds, TSQVisitor& Visitor) const
327 {
329
330 const int32 NumElems = Elems.Num();
331 const int32 SimdIters = NumElems / 4;
332
334 {
335 const int32 StartIndex = SimdIter * 4;
336 VectorRegister4Double MinX = MakeVectorRegisterDouble(Elems[StartIndex].Bounds.Min()[0], Elems[StartIndex + 1].Bounds.Min()[0], Elems[StartIndex + 2].Bounds.Min()[0], Elems[StartIndex + 3].Bounds.Min()[0]);
337 VectorRegister4Double MaxX = MakeVectorRegisterDouble(Elems[StartIndex].Bounds.Max()[0], Elems[StartIndex + 1].Bounds.Max()[0], Elems[StartIndex + 2].Bounds.Max()[0], Elems[StartIndex + 3].Bounds.Max()[0]);
338 VectorRegister4Double MinY = MakeVectorRegisterDouble(Elems[StartIndex].Bounds.Min()[1], Elems[StartIndex + 1].Bounds.Min()[1], Elems[StartIndex + 2].Bounds.Min()[1], Elems[StartIndex + 3].Bounds.Min()[1]);
339 VectorRegister4Double MaxY = MakeVectorRegisterDouble(Elems[StartIndex].Bounds.Max()[1], Elems[StartIndex + 1].Bounds.Max()[1], Elems[StartIndex + 2].Bounds.Max()[1], Elems[StartIndex + 3].Bounds.Max()[1]);
340 VectorRegister4Double MinZ = MakeVectorRegisterDouble(Elems[StartIndex].Bounds.Min()[2], Elems[StartIndex + 1].Bounds.Min()[2], Elems[StartIndex + 2].Bounds.Min()[2], Elems[StartIndex + 3].Bounds.Min()[2]);
341 VectorRegister4Double MaxZ = MakeVectorRegisterDouble(Elems[StartIndex].Bounds.Max()[2], Elems[StartIndex + 1].Bounds.Max()[2], Elems[StartIndex + 2].Bounds.Max()[2], Elems[StartIndex + 3].Bounds.Max()[2]);
342
346
350
354
356
358
359 for (int32 MaskIndex = 0; MaskIndex < 4; ++MaskIndex)
360 {
361 if ((MaskBitFalse & (1 << MaskIndex)) == 0)
362 {
364 if (PrePreFilterHelper(Elem.Payload, Visitor))
365 {
366 continue;
367 }
368 const FAABB3 InstanceBounds(Elem.Bounds.Min(), Elem.Bounds.Max());
370 if (Visitor.VisitOverlap(VisitData) == false)
371 {
372 return false;
373 }
374 }
375 }
376 }
377
378 for (int32 SlowIter = SimdIters * 4; SlowIter < NumElems; ++SlowIter)
379 {
381 if (PrePreFilterHelper(Elem.Payload, Visitor))
382 {
383 continue;
384 }
385
386 if (Elem.Bounds.Intersects(QueryBounds))
387 {
388 TSpatialVisitorData<TPayloadType> VisitData(Elem.Payload, true, FAABB3(Elem.Bounds.Min(), Elem.Bounds.Max()));
389 if (Visitor.VisitOverlap(VisitData) == false)
390 {
391 return false;
392 }
393 }
394 }
395
396 return true;
397 }
398
399 template <bool bSweep, typename TQueryFastData, typename TSQVisitor>
400 FORCEINLINE_DEBUGGABLE bool RaycastSweepImp(const TVec3<T>& Start, TQueryFastData& QueryFastData, const TVec3<T>& QueryHalfExtents, TSQVisitor& Visitor, const TVec3<T>& Dir, const TVec3<T> InvDir, const bool bParallel[3]) const
401 {
403 FReal TOI;
404 for (const auto& Elem : Elems)
405 {
406 if (PrePreFilterHelper(Elem.Payload, Visitor))
407 {
408 continue;
409 }
410
411 const FAABB3 InstanceBounds(Elem.Bounds.Min(), Elem.Bounds.Max());
413 EAABBQueryType::Raycast>::Intersects(Start, QueryFastData, TOI, InstanceBounds, FAABB3(), QueryHalfExtents, Dir, InvDir, bParallel))
414 {
416 const bool bContinue = (bSweep && Visitor.VisitSweep(VisitData, QueryFastData)) || (!bSweep && Visitor.VisitRaycast(VisitData, QueryFastData));
417 if (!bContinue)
418 {
419 return false;
420 }
421 }
422 }
423 return true;
424 }
425
426 template <typename TQueryFastData, typename TSQVisitor>
428 {
431 for (const auto& Elem : Elems)
432 {
434 if (InstanceBounds.RaycastFast(Start, InvDir, Parallel, Length, TOI))
435 {
436 if (PrePreFilterHelper(Elem.Payload, Visitor))
437 {
438 continue;
439 }
440 TSpatialVisitorData<TPayloadType> VisitData(Elem.Payload, true, FAABB3(Elem.Bounds.Min(), Elem.Bounds.Max()));
441 const bool bContinue = Visitor.VisitRaycast(VisitData, QueryFastData);
442 if (!bContinue)
443 {
444 return false;
445 }
446 }
447 }
448 return true;
449 }
450
451
452 void RemoveElement(TPayloadType Payload)
453 {
454 for (int32 Idx = 0; Idx < Elems.Num(); ++Idx)
455 {
456 if (Elems[Idx].Payload == Payload)
457 {
458 Elems.RemoveAtSwap(Idx);
459 break;
460 }
461 if (UNLIKELY(!ensure(Idx != Elems.Num() - 1))) // Make sure the payload was actually in here
462 {
463 UE_LOG(LogChaos, Warning, TEXT("AABBTree: Element not removed"));
464 }
465 }
466 bDirtyLeaf = true;
467 }
468
469 void UpdateElement(const TPayloadType& Payload, const TAABB<T, 3>& NewBounds, bool bHasBounds)
470 {
471 if (!bHasBounds)
472 return;
473
474 for (int32 Idx = 0; Idx < Elems.Num(); ++Idx)
475 {
476 if (Elems[Idx].Payload == Payload)
477 {
478 Elems[Idx].Bounds = NewBounds;
479 UpdateElementHelper(Elems[Idx].Payload, Payload);
480 break;
481 }
482 }
483 bDirtyLeaf = true;
484 }
485
486 void UpdateElementWithoutDirty(const TPayloadType& Payload, const TAABB<T, 3>& NewBounds)
487 {
488 for (int32 Idx = 0; Idx < Elems.Num(); ++Idx)
489 {
490 if (Elems[Idx].Payload == Payload)
491 {
492 Elems[Idx].Bounds = NewBounds;
493 UpdateElementHelper(Elems[Idx].Payload, Payload);
494 break;
495 }
496 }
497 }
498
500 {
501 Elems.Add(Element);
502 bDirtyLeaf = true;
503 }
504
505 void Reset()
506 {
507 Elems.Reset();
508 bDirtyLeaf = false;
509 }
510
511#if !UE_BUILD_SHIPPING
513 {
515
516 const float Alpha = (float)Elems.Num() / 10.f;
519
521 for (const auto& Elem : Elems)
522 {
523 InInterface.Line(LeafBounds.Center(), Elem.Bounds.Center(), ColorAsVec, InThickness);
524 InInterface.Box(Elem.Bounds, { (T)1.0, (T)0.2, (T)0.2 }, 1.0f);
525 }
526 }
527#endif
528
530 void PrintLeaf() const
531 {
532 int32 ElemIndex = 0;
533 for (const auto& Elem : Elems)
534 {
535 UE_LOG(LogChaos, Log, TEXT("Elem[%d] with bounds = %f %f %f | %f %f %f"), ElemIndex,
536 Elem.Bounds.Min()[0], Elem.Bounds.Min()[1], Elem.Bounds.Min()[2],
537 Elem.Bounds.Max()[0], Elem.Bounds.Max()[1], Elem.Bounds.Max()[2]);
538 ++ElemIndex;
539 }
540 }
541
543 {
544 Ar << Elems;
545 }
546
548
550 bool bDirtyLeaf = false;
551
552 friend ::FChaosVDDataWrapperUtils;
553};
554
555template <typename TPayloadType, bool bComputeBounds, typename T>
561
562
563// Default container behaviour is a that of a TArray
564template<typename LeafType>
565class TLeafContainer : public TArray<LeafType>
566{
567public:
572};
573
574
575// Here we are specializing the behaviour of our leaf container to minimize memory allocations/deallocations when the container is reset.
576// This is accomplished by only resetting the leafArrays and not the whole container
577// This is only specialized for one specific type, but can expanded to more types if necessary
578template<>
579class TLeafContainer<TAABBTreeLeafArray<FAccelerationStructureHandle, true, FReal >> : private TArray<TAABBTreeLeafArray<FAccelerationStructureHandle, true, FReal>>
580{
581
582private:
585public:
586
588
589
591 {
592 return FParent::operator[](Index);
593 }
595 {
596 return FParent::operator[](Index);
597 }
598 SizeType Num() const
599 {
600 return NumOfValidElements;
601 }
603 {
604 FParent::Reserve(Number);
605 }
606 void Reset()
607 {
608 for (int32 ElementIndex = 0; ElementIndex < NumOfValidElements; ElementIndex++)
609 {
610 (*this)[ElementIndex].Reset();
611 }
612 NumOfValidElements = 0;
613 }
614 SizeType Add(const FLeafType& Item)
615 {
616 NumOfValidElements++;
617 if (NumOfValidElements > FParent::Num())
618 {
619 FParent::Add(Item);
620 }
621 else
622 {
623 (*this)[NumOfValidElements - 1] = Item;
624 }
625 return NumOfValidElements - 1;
626 }
627
629 {
630 ensure(false); // This type does not get serialized
631 }
632private:
633 int32 NumOfValidElements = 0;
634
635 friend ::FChaosVDDataWrapperUtils;
636};
637
638template <typename LeafType>
645
646template <typename T>
648{
650 : ChildrenBounds{TAABB<T, 3>() , TAABB<T, 3>()}
651 , ChildrenNodes{INDEX_NONE, INDEX_NONE}
652 , ParentNode(INDEX_NONE)
653 , bLeaf(false)
654 , bDirtyNode(false)
655 {}
656
657 TAABB<T, 3> ChildrenBounds[2];
658 int32 ChildrenNodes[2];
660 bool bLeaf : 1;
661 bool bDirtyNode : 1;
662
663#if !UE_BUILD_SHIPPING
665 {
666 constexpr float ColorRatio = 0.75f;
667 constexpr float LineThicknessRatio = 0.75f;
668 if (!bLeaf)
669 {
671 for (int ChildIndex = 0; ChildIndex < 2; ++ChildIndex)
672 {
673 int32 NodeIndex = ChildrenNodes[ChildIndex];
674 if (NodeIndex > 0 && NodeIndex < Nodes.Num())
675 {
676 Nodes[NodeIndex].DebugDraw(InInterface, Nodes, { ChildColor.R, ChildColor.G, ChildColor.B }, InThickness * LineThicknessRatio);
677 }
678 }
679 for (int ChildIndex = 0; ChildIndex < 2; ++ChildIndex)
680 {
681 InInterface.Box(ChildrenBounds[ChildIndex], InLinearColor, InThickness);
682 }
683 }
684 }
685#endif
686
688 {
689 for (auto& Bounds : ChildrenBounds)
690 {
691 TBox<T, 3>::SerializeAsAABB(Ar, Bounds);
692 }
693
694 for (auto& Node : ChildrenNodes)
695 {
696 Ar << Node;
697 }
698
699 // Making a copy here because bLeaf is a bitfield and the archive can't handle it
700 bool bLeafCopy = bLeaf;
701 Ar << bLeafCopy;
702
703 // Dynamic trees are not serialized
704 if (Ar.IsLoading())
705 {
706 ParentNode = INDEX_NONE;
707 bLeaf = bLeafCopy;
708 }
709 }
710};
711
712template <typename T>
714{
715 Node.Serialize(Ar);
716 return Ar;
717}
718
720{
726
734
736 {
737 Ar << GlobalPayloadIdx;
738 Ar << DirtyPayloadIdx;
739 Ar << LeafIdx;
740 Ar << DirtyGridOverflowIdx;
741 // Dynamic trees are not serialized
742 if (Ar.IsLoading())
743 {
744 NodeIdx = INDEX_NONE;
745 }
746 }
747};
748
754
755extern CHAOS_API int32 MaxDirtyElements;
756
758{
760 {
761 Index = 0;
762 Count = 0;
763 }
764
766 {
767 Index = Other.Index;
768 Count = Other.Count;
769 }
770
771 int32 Index; // Index into FlattenedCellArrayOfDirtyIndices
772 int32 Count; // Number of valid entries from Index in FlattenedCellArrayOfDirtyIndices
773};
774
775template<typename PayloadType>
783
784template <typename TPayloadType, typename TLeafType, bool bMutable = true, typename T = FReal, typename StorageTraits = TDefaultAABBTreeStorageTraits<TPayloadType>>
785class TAABBTree final : public ISpatialAcceleration<TPayloadType, T, 3>
786{
787private:
789 using FNode = TAABBTreeNode<T>;
790public:
791 using PayloadType = TPayloadType;
792 static constexpr int D = 3;
793 using TType = T;
794 static constexpr T DefaultMaxPayloadBounds = 100000;
795 static constexpr int32 DefaultMaxChildrenInLeaf = 12;
796 static constexpr int32 DefaultMaxTreeDepth = 16;
797 static constexpr int32 DefaultMaxNumToProcess = 0; // 0 special value for processing all without timeslicing
798 static constexpr ESpatialAcceleration StaticType = std::is_same_v<TAABBTreeLeafArray<TPayloadType>, TLeafType> ? ESpatialAcceleration::AABBTree :
799 (std::is_same_v<TBoundingVolume<TPayloadType>, TLeafType> ? ESpatialAcceleration::AABBTreeBV : ESpatialAcceleration::Unknown);
801 : ISpatialAcceleration<TPayloadType, T, 3>(StaticType)
802 , bDynamicTree(false)
803 , RootNode(INDEX_NONE)
804 , FirstFreeInternalNode(INDEX_NONE)
805 , FirstFreeLeafNode(INDEX_NONE)
806 , MaxChildrenInLeaf(DefaultMaxChildrenInLeaf)
807 , MaxTreeDepth(DefaultMaxTreeDepth)
808 , MaxPayloadBounds(DefaultMaxPayloadBounds)
809 , MaxNumToProcess(DefaultMaxNumToProcess)
810 , bModifyingTreeMultiThreadingFastCheck(false)
811 , bShouldRebuild(true)
812 , bBuildOverlapCache(true)
813 {
814 GetCVars();
815
816 StorageTraits::InitPayloadToInfo(PayloadToInfo);
817 }
818
819 virtual void Reset() override
820 {
821 Nodes.Reset();
822 Leaves.Reset();
823 DirtyElements.Reset();
824 CellHashToFlatArray.Reset();
825 FlattenedCellArrayOfDirtyIndices.Reset();
826 DirtyElementsGridOverflow.Reset();
827 TreeStats.Reset();
828 TreeExpensiveStats.Reset();
829 GlobalPayloads.Reset();
830 PayloadToInfo.Reset();
831
832 OverlappingLeaves.Reset();
833 OverlappingOffsets.Reset();
834 OverlappingPairs.Reset();
835 OverlappingCounts.Reset();
836
837 NumProcessedThisSlice = 0;
838
839 StartSliceTimeStamp = 0.0;
840 CurrentDataElementsCopiedSinceLastCheck = 0;
841 CurrentProcessedNodesSinceChecked = 0;
842
843 WorkStack.Reset();
844 WorkPoolFreeList.Reset();
845 WorkPool.Reset();
846
847 bShouldRebuild = true;
848
849 RootNode = INDEX_NONE;
850 FirstFreeInternalNode = INDEX_NONE;
851 FirstFreeLeafNode = INDEX_NONE;
852
853 this->SetAsyncTimeSlicingComplete(true);
854
855 if (DirtyElementTree != nullptr)
856 {
857 DirtyElementTree->Reset();
858 }
859 }
860
862 {
864
865 if (bDynamicTree)
866 {
867 // Nothing to do
868 this->SetAsyncTimeSlicingComplete(true);
869 return;
870 }
871
872 // force is to stop time slicing and complete the rest of the build now
874 {
875 MaxNumToProcess = 0;
876 }
877
878 // still has work to complete
879 if (WorkStack.Num())
880 {
881 NumProcessedThisSlice = 0;
882 StartSliceTimeStamp = FPlatformTime::Seconds();
883 SplitNode();
884 }
885 }
886
887 template <typename TParticles>
888 TAABBTree(const TParticles& Particles, int32 InMaxChildrenInLeaf = DefaultMaxChildrenInLeaf, int32 InMaxTreeDepth = DefaultMaxTreeDepth, T InMaxPayloadBounds = DefaultMaxPayloadBounds, int32 InMaxNumToProcess = DefaultMaxNumToProcess, bool bInDynamicTree = false, bool bInUseDirtyTree = false, bool bInBuildOverlapCache = true)
889 : ISpatialAcceleration<TPayloadType, T, 3>(StaticType)
890 , bDynamicTree(bInDynamicTree)
891 , MaxChildrenInLeaf(InMaxChildrenInLeaf)
892 , MaxTreeDepth(InMaxTreeDepth)
893 , MaxPayloadBounds(InMaxPayloadBounds)
894 , MaxNumToProcess(InMaxNumToProcess)
895 , bModifyingTreeMultiThreadingFastCheck(false)
896 , bShouldRebuild(true)
897 , bBuildOverlapCache(bInBuildOverlapCache)
898 {
899 if (bInUseDirtyTree)
900 {
901 DirtyElementTree = TUniquePtr<TAABBTree>(new TAABBTree());
902 DirtyElementTree->SetTreeToDynamic();
903 }
904
905 StorageTraits::InitPayloadToInfo(PayloadToInfo);
906
907 GenerateTree(Particles);
908 }
909
910 // Tag dispatch enable for the below constructor to allow setting up the defaults without an initial set of particles
911 struct EmptyInit {};
912
913 TAABBTree(EmptyInit, int32 InMaxChildrenInLeaf = DefaultMaxChildrenInLeaf, int32 InMaxTreeDepth = DefaultMaxTreeDepth, T InMaxPayloadBounds = DefaultMaxPayloadBounds, int32 InMaxNumToProcess = DefaultMaxNumToProcess, bool bInDynamicTree = false, bool bInUseDirtyTree = false, bool bInBuildOverlapCache = true)
914 : ISpatialAcceleration<TPayloadType, T, 3>(StaticType)
915 , bDynamicTree(bInDynamicTree)
916 , MaxChildrenInLeaf(InMaxChildrenInLeaf)
917 , MaxTreeDepth(InMaxTreeDepth)
918 , MaxPayloadBounds(InMaxPayloadBounds)
919 , MaxNumToProcess(InMaxNumToProcess)
920 , bModifyingTreeMultiThreadingFastCheck(false)
921 , bShouldRebuild(true)
922 , bBuildOverlapCache(bInBuildOverlapCache)
923 {
925 {
926 DirtyElementTree = TUniquePtr<TAABBTree>(new TAABBTree());
927 DirtyElementTree->SetTreeToDynamic();
928 }
929
930 StorageTraits::InitPayloadToInfo(PayloadToInfo);
931 }
932
933 template <typename ParticleView>
934 void Reinitialize(const ParticleView& Particles, int32 InMaxChildrenInLeaf = DefaultMaxChildrenInLeaf, int32 InMaxTreeDepth = DefaultMaxTreeDepth, T InMaxPayloadBounds = DefaultMaxPayloadBounds, int32 InMaxNumToProcess = DefaultMaxNumToProcess, bool bInDynamicTree = false, bool bInbBuildOverlapCache = true)
935 {
936 bDynamicTree = bInDynamicTree;
937 MaxChildrenInLeaf = InMaxChildrenInLeaf;
938 MaxTreeDepth = InMaxTreeDepth;
939 MaxPayloadBounds = InMaxPayloadBounds;
940 MaxNumToProcess = InMaxNumToProcess;
941 bModifyingTreeMultiThreadingFastCheck = false;
942 bShouldRebuild = true;
943 bBuildOverlapCache = bInbBuildOverlapCache;
944 GenerateTree(Particles);
945 }
946
947 virtual TArray<TPayloadType> FindAllIntersections(const FAABB3& Box) const override { return FindAllIntersectionsImp(Box); }
948
949 bool GetAsBoundsArray(TArray<TAABB<T, 3>>& AllBounds, int32 NodeIdx, int32 ParentNode, TAABB<T, 3>& Bounds)
950 {
951 if (Nodes[NodeIdx].bLeaf)
952 {
953 AllBounds.Add(Bounds);
954 return false;
955 }
956 else
957 {
958 GetAsBoundsArray(AllBounds, Nodes[NodeIdx].ChildrenNodes[0], NodeIdx, Nodes[NodeIdx].ChildrenBounds[0]);
959 GetAsBoundsArray(AllBounds, Nodes[NodeIdx].ChildrenNodes[1], NodeIdx, Nodes[NodeIdx].ChildrenBounds[0]);
960 }
961 return true;
962 }
963
964 virtual ~TAABBTree() {}
965
967 {
968 (*this) = Other;
969 }
970
975
976 virtual void Raycast(const FVec3& Start, const FVec3& Dir, const FReal Length, ISpatialVisitor<TPayloadType, FReal>& Visitor) const override
977 {
979 Raycast(Start, Dir, Length, ProxyVisitor);
980 }
981
982 template <typename SQVisitor>
983 void Raycast(const FVec3& Start, const FVec3& Dir, const FReal Length, SQVisitor& Visitor) const
984 {
985 FQueryFastData QueryFastData(Dir, Length);
986 QueryImp<EAABBQueryType::Raycast>(Start, QueryFastData, FVec3(), FAABB3(), Visitor, QueryFastData.Dir, QueryFastData.InvDir, QueryFastData.bParallel);
987 }
988
989 template <typename SQVisitor>
990 bool RaycastFast(const FVec3& Start, FQueryFastData& CurData, SQVisitor& Visitor, const FVec3& Dir, const FVec3 InvDir, const bool bParallel[3]) const
991 {
992 return QueryImp<EAABBQueryType::Raycast>(Start, CurData, TVec3<T>(), TAABB<T, 3>(), Visitor, Dir, InvDir, bParallel);
993 }
994
995 void Sweep(const FVec3& Start, const FVec3& Dir, const FReal Length, const FVec3 QueryHalfExtents, ISpatialVisitor<TPayloadType, FReal>& Visitor) const override
996 {
999 }
1000
1001 template <typename SQVisitor>
1002 void Sweep(const FVec3& Start, const FVec3& Dir, const FReal Length, const FVec3 QueryHalfExtents, SQVisitor& Visitor) const
1003 {
1004 FQueryFastData QueryFastData(Dir, Length);
1005 QueryImp<EAABBQueryType::Sweep>(Start, QueryFastData, QueryHalfExtents, FAABB3(), Visitor, QueryFastData.Dir, QueryFastData.InvDir, QueryFastData.bParallel);
1006 }
1007
1008 template <typename SQVisitor>
1009 bool SweepFast(const FVec3& Start, FQueryFastData& CurData, const FVec3 QueryHalfExtents, SQVisitor& Visitor, const FVec3& Dir, const FVec3 InvDir, const bool bParallel[3]) const
1010 {
1011 return QueryImp<EAABBQueryType::Sweep>(Start,CurData, QueryHalfExtents, FAABB3(), Visitor, Dir, InvDir, bParallel);
1012 }
1013
1019
1020 template <typename SQVisitor>
1021 void Overlap(const FAABB3& QueryBounds, SQVisitor& Visitor) const
1022 {
1023 OverlapFast(QueryBounds, Visitor);
1024 }
1025
1026 template <typename SQVisitor>
1027 bool OverlapFast(const FAABB3& QueryBounds, SQVisitor& Visitor) const
1028 {
1029 //dummy variables to reuse templated path
1031 return QueryImp<EAABBQueryType::Overlap>(FVec3(), VoidData, FVec3(), QueryBounds, Visitor, VoidData.Dir, VoidData.InvDir, VoidData.bParallel);
1032 }
1033
1034 // This is to make sure important parameters are not changed during inopportune times
1036 {
1037 DirtyElementGridCellSize = (T) FAABBTreeDirtyGridCVars::DirtyElementGridCellSize;
1038 if (DirtyElementGridCellSize > UE_SMALL_NUMBER)
1039 {
1040 DirtyElementGridCellSizeInv = 1.0f / DirtyElementGridCellSize;
1041 }
1042 else
1043 {
1044 DirtyElementGridCellSizeInv = 1.0f;
1045 }
1046
1047 DirtyElementMaxGridCellQueryCount = FAABBTreeDirtyGridCVars::DirtyElementMaxGridCellQueryCount;
1048 DirtyElementMaxPhysicalSizeInCells = FAABBTreeDirtyGridCVars::DirtyElementMaxPhysicalSizeInCells;
1049 DirtyElementMaxCellCapacity = FAABBTreeDirtyGridCVars::DirtyElementMaxCellCapacity;
1050 }
1051
1053 {
1054 return DirtyElementTree == nullptr && DirtyElementGridCellSize > 0.0f &&
1055 DirtyElementMaxGridCellQueryCount > 0 &&
1056 DirtyElementMaxPhysicalSizeInCells > 0 &&
1057 DirtyElementMaxCellCapacity > 0;
1058 }
1059
1061 {
1062 DirtyGridHashEntry* HashEntry = CellHashToFlatArray.Find(Hash);
1063 if (HashEntry)
1064 {
1065 if (HashEntry->Count >= DirtyElementMaxCellCapacity) // Checking if we are at capacity
1066 {
1067 return false;
1068 }
1069 }
1070
1071 return true;
1072 }
1073
1074 // Returns true if there was enough space in the cell to add the new dirty element index or if the element was already added (This second condition should not happen)
1075 //(The second condition should never be true for the current implementation)
1077 {
1078 DirtyGridHashEntry* HashEntry = CellHashToFlatArray.Find(Hash);
1079 if (HashEntry)
1080 {
1081 if (HashEntry->Count < DirtyElementMaxCellCapacity)
1082 {
1083 if (ensure(InsertValueIntoSortedSubArray(FlattenedCellArrayOfDirtyIndices, NewDirtyIndex, HashEntry->Index, HashEntry->Count)))
1084 {
1085 ++(HashEntry->Count);
1086 }
1087 return true;
1088 }
1089 }
1090 else
1091 {
1092 DirtyGridHashEntry& NewHashEntry = CellHashToFlatArray.Add(Hash);
1093 NewHashEntry.Index = FlattenedCellArrayOfDirtyIndices.Num(); // End of flat array
1094 NewHashEntry.Count = 1;
1095 FlattenedCellArrayOfDirtyIndices.AddUninitialized(DirtyElementMaxCellCapacity);
1096 FlattenedCellArrayOfDirtyIndices[NewHashEntry.Index] = NewDirtyIndex;
1097 TreeStats.StatNumNonEmptyCellsInGrid++;
1098 return true;
1099 }
1100 return false;
1101 }
1102
1103 // Returns true if the dirty particle was in the grid and successfully deleted
1105 {
1106 DirtyGridHashEntry* HashEntry = CellHashToFlatArray.Find(Hash);
1107 if (HashEntry && HashEntry->Count >= 1)
1108 {
1109 if (DeleteValueFromSortedSubArray(FlattenedCellArrayOfDirtyIndices, DirtyIndex, HashEntry->Index, HashEntry->Count))
1110 {
1111 --(HashEntry->Count);
1112 // Not deleting cell when it gets empty, it may get reused or will be deleted when the AABBTree is rebuilt
1113 return true;
1114 }
1115 }
1116 return false;
1117 }
1118
1120 {
1122 {
1123 // Remove this element from the Grid
1124 DoForOverlappedCells(DirtyElements[DeleteDirtyParticleIdx].Bounds, DirtyElementGridCellSize, DirtyElementGridCellSizeInv, [&](int32 Hash) {
1125 ensure(DeleteDirtyParticleIndexFromGridCell(Hash, DeleteDirtyParticleIdx));
1126 return true;
1127 });
1128 }
1129 else
1130 {
1131 // remove element from the grid overflow
1132 ensure(DirtyElementsGridOverflow[DeleteDirtyGridOverflowIdx] == DeleteDirtyParticleIdx);
1133
1134 if (DeleteDirtyGridOverflowIdx + 1 < DirtyElementsGridOverflow.Num())
1135 {
1136 auto LastOverflowPayload = DirtyElements[DirtyElementsGridOverflow.Last()].Payload;
1137 PayloadToInfo.FindChecked(LastOverflowPayload).DirtyGridOverflowIdx = DeleteDirtyGridOverflowIdx;
1138 }
1139 DirtyElementsGridOverflow.RemoveAtSwap(DeleteDirtyGridOverflowIdx);
1140 }
1141
1142 if (DeleteDirtyParticleIdx + 1 < DirtyElements.Num())
1143 {
1144 // Now rename the last element in DirtyElements in both the grid and the overflow
1145 // So that it is correct after swapping Dirty elements in next step
1146 int32 LastDirtyElementIndex = DirtyElements.Num() - 1;
1147 auto LastDirtyPayload = DirtyElements[LastDirtyElementIndex].Payload;
1148 int32 LastDirtyGridOverflowIdx = PayloadToInfo.FindChecked(LastDirtyPayload).DirtyGridOverflowIdx;
1150 {
1151 // Rename this element in the Grid
1152 DoForOverlappedCells(DirtyElements.Last().Bounds, DirtyElementGridCellSize, DirtyElementGridCellSizeInv, [&](int32 Hash) {
1153 ensure(DeleteDirtyParticleIndexFromGridCell(Hash, LastDirtyElementIndex));
1154 ensure(AddNewDirtyParticleIndexToGridCell(Hash, DeleteDirtyParticleIdx));
1155 return true;
1156 });
1157 }
1158 else
1159 {
1160 // Rename element in overflow instead
1161 DirtyElementsGridOverflow[LastDirtyGridOverflowIdx] = DeleteDirtyParticleIdx;
1162 }
1163
1164 // Copy the Payload to the new index
1165
1166 PayloadToInfo.FindChecked(LastDirtyPayload).DirtyPayloadIdx = DeleteDirtyParticleIdx;
1167 }
1168 DirtyElements.RemoveAtSwap(DeleteDirtyParticleIdx);
1169 }
1170
1172 {
1173 bool bAddToGrid = !TooManyOverlapQueryCells(NewBounds, DirtyElementGridCellSizeInv, DirtyElementMaxPhysicalSizeInCells);
1174 if (!bAddToGrid)
1175 {
1176 TreeStats.StatNumElementsTooLargeForGrid++;
1177 }
1178
1179 if (bAddToGrid)
1180 {
1181 DoForOverlappedCells(NewBounds, DirtyElementGridCellSize, DirtyElementGridCellSizeInv, [&](int32 Hash) {
1182 if (!EnoughSpaceInGridCell(Hash))
1183 {
1184 bAddToGrid = false;
1185 return false; // early exit to avoid going through all the cells
1186 }
1187 return true;
1188 });
1189 }
1190
1191 if (bAddToGrid)
1192 {
1193 DoForOverlappedCells(NewBounds, DirtyElementGridCellSize, DirtyElementGridCellSizeInv, [&](int32 Hash) {
1194 ensure(AddNewDirtyParticleIndexToGridCell(Hash, NewDirtyElement));
1195 return true;
1196 });
1197 }
1198 else
1199 {
1200 int32 NewOverflowIndex = DirtyElementsGridOverflow.Add(NewDirtyElement);
1201 return NewOverflowIndex;
1202 }
1203
1204 return INDEX_NONE;
1205 }
1206
1208 {
1209 if (DirtyGridOverflowIdx == INDEX_NONE)
1210 {
1211 const TAABB<T, 3>& OldBounds = DirtyElements[DirtyElementIndex].Bounds;
1212
1213 // Delete element in cells that are no longer overlapping
1214 DoForOverlappedCellsExclude(OldBounds, NewBounds, DirtyElementGridCellSize, DirtyElementGridCellSizeInv, [&](int32 Hash) -> bool {
1215 ensure(DeleteDirtyParticleIndexFromGridCell(Hash, DirtyElementIndex));
1216 return true;
1217 });
1218
1219 // Add to new overlapped cells
1220 if (!DoForOverlappedCellsExclude(NewBounds, OldBounds, DirtyElementGridCellSize, DirtyElementGridCellSizeInv, [&](int32 Hash) -> bool {
1221 return AddNewDirtyParticleIndexToGridCell(Hash, DirtyElementIndex);
1222 }))
1223 {
1224 // Was not able to add it to the grid , so delete element from grid
1225 DoForOverlappedCells(NewBounds, DirtyElementGridCellSize, DirtyElementGridCellSizeInv, [&](int32 Hash) {
1226 DeleteDirtyParticleIndexFromGridCell(Hash, DirtyElementIndex);
1227 return true;
1228 });
1229 // Add to overflow
1230 int32 NewOverflowIndex = DirtyElementsGridOverflow.Add(DirtyElementIndex);
1231 return NewOverflowIndex;
1232 }
1233 }
1234 return DirtyGridOverflowIdx;
1235 }
1236
1244
1246 {
1247 TArray<FElement> AllElements;
1248 for(int LeafIndex = 0; LeafIndex < Leaves.Num(); LeafIndex++)
1249 {
1250 const TLeafType& Leaf = Leaves[LeafIndex];
1251 Leaf.GatherElements(AllElements);
1252 }
1253
1254 int32 MaxDepth = 0;
1255 int32 DepthTotal = 0;
1256 for(const FElement& Element : AllElements)
1257 {
1258 const FAABBTreePayloadInfo* PayloadInfo = PayloadToInfo.Find(Element.Payload);
1259
1260 if(!PayloadInfo)
1261 {
1262 continue;
1263 }
1264
1265 int32 Depth = 0;
1266 int32 Node = PayloadInfo->NodeIdx;
1267
1268 while(Node != INDEX_NONE)
1269 {
1270 Node = Nodes[Node].ParentNode;
1271 if(Node != INDEX_NONE)
1272 {
1273 Depth++;
1274 }
1275 }
1276 if(Depth > MaxDepth)
1277 {
1278 MaxDepth = Depth;
1279 }
1280 DepthTotal += Depth;
1281 }
1282
1283 return { AllElements, DepthTotal, MaxDepth, DirtyElements.Num() };
1284 }
1285
1286 // Expensive function: Don't call unless debugging
1288 {
1289 const FElementsCollection ElemData = DebugGetElementsCollection();
1290
1291#if !WITH_EDITOR
1292 CSV_CUSTOM_STAT(ChaosPhysicsTimers, MaximumTreeDepth, ElemData.MaxDepth, ECsvCustomStatOp::Max);
1293 CSV_CUSTOM_STAT(ChaosPhysicsTimers, AvgTreeDepth, ElemData.DepthTotal / ElemData.AllElements.Num(), ECsvCustomStatOp::Max);
1294 CSV_CUSTOM_STAT(ChaosPhysicsTimers, Dirty, ElemData.DirtyElementCount, ECsvCustomStatOp::Max);
1295#endif
1296
1297 }
1298
1299#if !UE_BUILD_SHIPPING
1300 void DumpStats() const override
1301 {
1302 if(GLog)
1303 {
1304 DumpStatsTo(*GLog);
1305 }
1306 }
1307
1308 void DumpStatsTo(FOutputDevice& Ar) const override
1309 {
1310 const FElementsCollection ElemData = DebugGetElementsCollection();
1311
1312 const int32 ElementsNum = ElemData.AllElements.Num();
1313 const int32 PayloadMapNum = PayloadToInfo.Num();
1314 const int32 PayloadMapCapacity = PayloadToInfo.Capacity();
1315 const uint64 PayloadAllocsize = (uint64)PayloadToInfo.GetAllocatedSize();
1316 const float AvgDepth = ElementsNum > 0 ? (float)ElemData.DepthTotal / (float)ElementsNum : 0.0f;
1317
1318 Ar.Logf(ELogVerbosity::Log, TEXT("\t\tContains %d elements"), ElementsNum);
1319 Ar.Logf(ELogVerbosity::Log, TEXT("\t\tMax depth is %d"), ElemData.MaxDepth);
1320 Ar.Logf(ELogVerbosity::Log, TEXT("\t\tAvg depth is %.3f"), AvgDepth);
1321 Ar.Logf(ELogVerbosity::Log, TEXT("\t\tDirty element count is %d"), ElemData.DirtyElementCount);
1322 Ar.Logf(ELogVerbosity::Log, TEXT("\t\tPayload container size is %d elements"), PayloadMapNum);
1323 Ar.Logf(ELogVerbosity::Log, TEXT("\t\tPayload container capacity is %d elements"), PayloadMapCapacity);
1324 Ar.Logf(ELogVerbosity::Log, TEXT("\t\tAllocated size of payload container is %u bytes (%u per tree element)"), PayloadAllocsize, ElementsNum > 0 ? PayloadAllocsize / (uint32)ElementsNum : 0);
1325
1326 if(DirtyElementTree)
1327 {
1328 Ar.Logf(ELogVerbosity::Log, TEXT(""));
1329 Ar.Logf(ELogVerbosity::Log, TEXT("\t\tDirty Tree:"));
1330 DirtyElementTree->DumpStatsTo(Ar);
1331 }
1332 }
1333#endif
1334
1336 {
1337 int32 AllocatedNodeIdx = FirstFreeInternalNode;
1338 if (FirstFreeInternalNode != INDEX_NONE)
1339 {
1340 // Unlink from free list
1341 FirstFreeInternalNode = Nodes[FirstFreeInternalNode].ChildrenNodes[1];
1342 }
1343 else
1344 {
1345 // create the actual node space
1346 AllocatedNodeIdx = Nodes.AddUninitialized(1);;
1347 Nodes[AllocatedNodeIdx].bLeaf = false;
1348 }
1349 if (UNLIKELY(!ensure(Nodes[AllocatedNodeIdx].bLeaf == false)))
1350 {
1351 UE_LOG(LogChaos, Warning, TEXT("AABBTree: Allocated internal node is a leaf"));
1352 }
1353 return AllocatedNodeIdx;
1354 }
1355
1356
1362
1364 {
1365 int32 AllocatedNodeIdx = FirstFreeLeafNode;
1366 int32 LeafIndex;
1367 if (FirstFreeLeafNode != INDEX_NONE)
1368 {
1369 FirstFreeLeafNode = Nodes[FirstFreeLeafNode].ChildrenNodes[1];
1370 LeafIndex = Nodes[AllocatedNodeIdx].ChildrenNodes[0]; // This is already set when it was allocated for the first time
1371 FElement NewElement{ Payload, NewBounds };
1372 Leaves[LeafIndex].AddElement(NewElement);
1373 Leaves[LeafIndex].RecomputeBounds(bDynamicTree);
1374 }
1375 else
1376 {
1377 LeafIndex = Leaves.Num();
1378
1379 // create the actual node space
1380 AllocatedNodeIdx = Nodes.AddUninitialized(1);
1381 Nodes[AllocatedNodeIdx].ChildrenNodes[0] = LeafIndex;
1382 Nodes[AllocatedNodeIdx].bLeaf = true;
1383
1384 FElement NewElement{ Payload, NewBounds };
1387 Leaves.Add(TLeafType{ SingleElementArray }); // Extra copy
1388 }
1389
1390 // Expand the leaf node bounding box to reduce the number of updates
1393 Nodes[AllocatedNodeIdx].ChildrenBounds[0] = ExpandedBounds;
1394
1395 Nodes[AllocatedNodeIdx].ParentNode = INDEX_NONE;
1396 if (UNLIKELY(!ensure(Nodes[AllocatedNodeIdx].bLeaf == true)))
1397 {
1398 UE_LOG(LogChaos, Warning, TEXT("AABBTree: Allocated leaf node is not a leaf"));
1399 }
1400
1401 return NodeAndLeafIndices{ AllocatedNodeIdx , LeafIndex };
1402 }
1403
1405 {
1406 Nodes[NodeIdx].ChildrenNodes[1] = FirstFreeInternalNode;
1407 FirstFreeInternalNode = NodeIdx;
1408 if (UNLIKELY(!ensure(Nodes[NodeIdx].bLeaf == false)))
1409 {
1410 UE_LOG(LogChaos, Warning, TEXT("AABBTree: Deallocated Internal node is a leaf"));
1411 }
1412 }
1413
1415 {
1416
1417 Leaves[Nodes[NodeIdx].ChildrenNodes[0]].Reset();
1418
1419 Nodes[NodeIdx].ChildrenNodes[1] = FirstFreeLeafNode;
1420 FirstFreeLeafNode = NodeIdx;
1421 if (UNLIKELY(!ensure(Nodes[NodeIdx].bLeaf == true)))
1422 {
1423 UE_LOG(LogChaos, Warning, TEXT("AABBTree: Deallocated Leaf node is not a leaf"));
1424 }
1425 }
1426
1427 // Is the input node Child 0 or Child 1?
1429 {
1430 check(NodeIdx != INDEX_NONE);
1431 int32 ParentIdx = Nodes[NodeIdx].ParentNode;
1433 if (Nodes[ParentIdx].ChildrenNodes[0] == NodeIdx)
1434 {
1435 return 0;
1436 }
1437 else
1438 {
1439 if (UNLIKELY(!ensure(Nodes[ParentIdx].ChildrenNodes[1] == NodeIdx)))
1440 {
1441 UE_LOG(LogChaos, Warning, TEXT("AABBTree: Child node not found"));
1442 }
1443 return 1;
1444 }
1445 }
1446
1447 // Is the input node Child 0 or Child 1?
1449 {
1450 return(WhichChildAmI(NodeIdx) ^ 1);
1451 }
1452
1453public:
1455 {
1456 bOutAddToLeaf = false;
1457
1460 const FReal NewBoundsArea = NewBounds.GetArea();
1461
1462 //Priority Q of indices to explore
1463 PriorityQ.Reset();
1464
1465 int32 QIndex = 0;
1466
1467 // Initializing
1468 FNode& RNode = Nodes[RootNode];
1470 WorkingAABB.GrowToInclude(RNode.ChildrenBounds[0]);
1471 if(!RNode.bLeaf)
1472 {
1473 WorkingAABB.GrowToInclude(RNode.ChildrenBounds[1]);
1474 }
1475
1476 int32 BestSiblingIdx = RootNode;
1477 FReal BestCost = WorkingAABB.GetArea();
1478 PriorityQ.Emplace(RNode, RootNode, 0.0f);
1479
1480 while (PriorityQ.Num() - QIndex)
1481 {
1482 // Pop from queue
1483 const FNodeIndexAndCost NodeAndCost = PriorityQ[QIndex];
1484 FNode& TestNode = NodeAndCost.template Get<0>();
1485 const FReal SumDeltaCost = NodeAndCost.template Get<2>();
1486 QIndex++;
1487
1488 // TestSibling bounds union with new bounds
1489 bool bAddToLeaf = false;
1490 WorkingAABB = TestNode.ChildrenBounds[0];
1491 if (!TestNode.bLeaf)
1492 {
1493 WorkingAABB.GrowToInclude(TestNode.ChildrenBounds[1]);
1494 }
1495 else
1496 {
1497 int32 LeafIdx = TestNode.ChildrenNodes[0];
1498 bAddToLeaf = Leaves[LeafIdx].GetElementCount() < FAABBTreeCVars::DynamicTreeLeafCapacity;
1499 }
1500
1501 const FReal TestSiblingArea = WorkingAABB.GetArea();
1502
1503 WorkingAABB.GrowToInclude(NewBounds);
1504
1505 const FReal NewPotentialNodeArea = WorkingAABB.GetArea();
1507
1508 if (bAddToLeaf)
1509 {
1510 // No new node is added (we can experiment with this cost function
1511 // It is faster overall if we don't subtract here
1512 //CostForChoosingNode -= TestSiblingArea;
1513 }
1514
1516 // Did we get a better cost?
1518 {
1520 BestSiblingIdx = NodeAndCost.template Get<1>();
1522 }
1523
1524 // Lower bound of Children costs
1527
1528 if (!TestNode.bLeaf && ChildCostLowerBound < BestCost)
1529 {
1530 // Now we will push the children
1531 PriorityQ.Reserve(PriorityQ.Num() + 2);
1532 PriorityQ.Emplace(Nodes[TestNode.ChildrenNodes[0]], TestNode.ChildrenNodes[0], NewCost);
1533 PriorityQ.Emplace(Nodes[TestNode.ChildrenNodes[1]], TestNode.ChildrenNodes[1], NewCost);
1534 }
1535
1536 }
1537
1538 return BestSiblingIdx;
1539 }
1540
1541 // Rotate nodes to decrease tree cost
1542 // Grandchildren can swap with their aunts
1543 void RotateNode(uint32 NodeIdx, bool debugAssert = false)
1544 {
1545 int32 BestGrandChildToSwap = INDEX_NONE; // GrandChild of NodeIdx
1546 int32 BestAuntToSwap = INDEX_NONE; // Aunt of BestGrandChildToSwap
1547 FReal BestDeltaCost = 0.0f; // Negative values are cost reductions, doing nothing changes the cost with 0
1548
1549 check(!Nodes[NodeIdx].bLeaf);
1550 // Check both children of NodeIdx
1552 {
1553 int32 Aunt = Nodes[NodeIdx].ChildrenNodes[AuntLocalIdx];
1554 int32 Mother = Nodes[NodeIdx].ChildrenNodes[AuntLocalIdx ^ 1];
1555 if (Nodes[Mother].bLeaf)
1556 {
1557 continue;
1558 }
1559 for (int32 GrandChild : Nodes[Mother].ChildrenNodes)
1560 {
1561 // Only the Mother's cost will change
1562 TAABB<T, 3> NewMotherAABB{ Nodes[NodeIdx].ChildrenBounds[AuntLocalIdx]}; // Aunt will be under mother now
1563 NewMotherAABB.GrowToInclude(Nodes[Mother].ChildrenBounds[GetSiblingIndex(GrandChild)]); // Add the Grandchild's sibling cost
1564 FReal MotherCostWithoutRotation = Nodes[NodeIdx].ChildrenBounds[AuntLocalIdx ^ 1].GetArea();
1567
1569 {
1573 }
1574
1575 }
1576 }
1577
1578 // Now do the rotation if required
1580 {
1582 if (debugAssert)
1583 {
1584 check(false);
1585 }
1586
1587 int32 AuntLocalChildIdx = WhichChildAmI(BestAuntToSwap);
1589
1591
1592 if (UNLIKELY(!ensure(BestGrandChildToSwap != NodeIdx)))
1593 {
1594 UE_LOG(LogChaos, Warning, TEXT("AABBTree: 1: Rotate Node loop detected"));
1595 return;
1596 }
1597
1599 {
1600 // This really should not happen, but was due to NaNs entering the bounds (FIXED)
1601 UE_LOG(LogChaos, Warning, TEXT("AABBTree: 2: Rotate Node loop detected"));
1602 return;
1603 }
1604
1605 // Modify NodeIdx
1606 Nodes[NodeIdx].ChildrenNodes[AuntLocalChildIdx] = BestGrandChildToSwap;
1607
1608 // Modify BestGrandChildToSwap
1609 Nodes[BestGrandChildToSwap].ParentNode = NodeIdx;
1610
1611 // Modify BestAuntToSwap
1612 Nodes[BestAuntToSwap].ParentNode = MotherOfBestGrandChild;
1613 // Modify MotherOfBestGrandChild
1615 // Swap the bounds
1616 TAABB<T, 3> AuntAABB = Nodes[NodeIdx].ChildrenBounds[AuntLocalChildIdx];
1617 Nodes[NodeIdx].ChildrenBounds[AuntLocalChildIdx] = Nodes[MotherOfBestGrandChild].ChildrenBounds[GrandChildLocalChildIdx];
1619 // Update the other child bound of NodeIdx
1620 Nodes[NodeIdx].ChildrenBounds[AuntLocalChildIdx ^ 1] = Nodes[MotherOfBestGrandChild].ChildrenBounds[0];
1621 Nodes[NodeIdx].ChildrenBounds[AuntLocalChildIdx ^ 1].GrowToInclude(Nodes[MotherOfBestGrandChild].ChildrenBounds[1]);
1622 }
1623 }
1624
1625 // Returns the inserted node and leaf
1626 NodeAndLeafIndices InsertLeaf(const TPayloadType& Payload, const TAABB<T, 3>& NewBounds)
1627 {
1628
1629 // Slow Debug Code
1630 //if (GetUniqueIdx(Payload).Idx == 5)
1631 //{
1632 // DynamicTreeDebugStats();
1633 //}
1634
1635 // Empty tree case
1636 if(RootNode == INDEX_NONE)
1637 {
1638 NodeAndLeafIndices NewIndices = AllocateLeafNodeAndLeaf(Payload, NewBounds);
1639 RootNode = NewIndices.NodeIdx;
1640 return NewIndices;
1641 }
1642
1643 // Find the best sibling
1644 bool bAddToLeaf;
1645 int32 BestSibling = FindBestSibling(NewBounds, bAddToLeaf);
1646
1647 if (bAddToLeaf)
1648 {
1649 const int32 LeafIdx = Nodes[BestSibling].ChildrenNodes[0];
1650 Leaves[LeafIdx].AddElement(TPayloadBoundsElement<TPayloadType, T>{Payload, NewBounds});
1651 Leaves[LeafIdx].RecomputeBounds(bDynamicTree);
1652 TAABB<T, 3> ExpandedBounds = Leaves[LeafIdx].GetBounds();
1654 Nodes[BestSibling].ChildrenBounds[0] = ExpandedBounds;
1655 UpdateAncestorBounds(BestSibling, true);
1656 return NodeAndLeafIndices{ BestSibling , LeafIdx };
1657 }
1658
1659 const NodeAndLeafIndices NewLeafIndices = AllocateLeafNodeAndLeaf(Payload, NewBounds);
1660
1661 // New internal parent node
1662 const int32 OldParent = Nodes[BestSibling].ParentNode;
1663 const int32 NewParent = AllocateInternalNode();
1664 FNode& NewParentNode = Nodes[NewParent];
1666 {
1667 UE_LOG(LogChaos, Warning, TEXT("AABBTree: 1: Insert leaf loop detected"));
1668 }
1669 NewParentNode.ParentNode = OldParent;
1670 NewParentNode.ChildrenNodes[0] = BestSibling;
1671 NewParentNode.ChildrenNodes[1] = NewLeafIndices.NodeIdx;
1672 NewParentNode.ChildrenBounds[0] = Nodes[BestSibling].ChildrenBounds[0];
1673 if (!Nodes[BestSibling].bLeaf)
1674 {
1675 NewParentNode.ChildrenBounds[0].GrowToInclude(Nodes[BestSibling].ChildrenBounds[1]);
1676 }
1677 NewParentNode.ChildrenBounds[1] = Nodes[NewLeafIndices.NodeIdx].ChildrenBounds[0];
1678
1679 if (OldParent != INDEX_NONE)
1680 {
1681 const int32 ChildIdx = WhichChildAmI(BestSibling);
1682 Nodes[OldParent].ChildrenNodes[ChildIdx] = NewParent;
1683 }
1684 else
1685 {
1686 RootNode = NewParent;
1687 }
1688
1690 {
1691 UE_LOG(LogChaos, Warning, TEXT("AABBTree: 2: Insert leaf loop detected"));
1692 }
1693 Nodes[BestSibling].ParentNode = NewParent;
1694 if (UNLIKELY(!ensure(NewLeafIndices.NodeIdx != NewParent)))
1695 {
1696 UE_LOG(LogChaos, Warning, TEXT("AABBTree: 3: Insert leaf loop detected"));
1697 }
1698 Nodes[NewLeafIndices.NodeIdx].ParentNode = NewParent;
1699
1700 UpdateAncestorBounds(NewParent, true);
1701
1702 return NewLeafIndices;
1703 }
1704
1705 void UpdateAncestorBounds(int32 NodeIdx, bool bDoRotation = false)
1706 {
1707 int32 CurrentNodeIdx = NodeIdx;
1708 int32 ParentNodeIdx = Nodes[NodeIdx].ParentNode;
1709
1710
1711 // This should not be required
1712 /*if (bDoRotation && NodeIdx != INDEX_NONE)
1713 {
1714 RotateNode(NodeIdx,true);
1715 }*/
1716 while (ParentNodeIdx != INDEX_NONE)
1717 {
1719 {
1720 UE_LOG(LogChaos, Warning, TEXT("AABBTree: 1: UpdateAncestorBounds loop detected"));
1721 check(false); // Crash here, this is not recoverable
1722 return;
1723 }
1724 int32 ChildIndex = WhichChildAmI(CurrentNodeIdx);
1725 Nodes[ParentNodeIdx].ChildrenBounds[ChildIndex] = Nodes[CurrentNodeIdx].ChildrenBounds[0];
1726 if (!Nodes[CurrentNodeIdx].bLeaf)
1727 {
1728 Nodes[ParentNodeIdx].ChildrenBounds[ChildIndex].GrowToInclude(Nodes[CurrentNodeIdx].ChildrenBounds[1]);
1729 }
1730
1731 if (bDoRotation)
1732 {
1733 RotateNode(ParentNodeIdx);
1734 }
1735
1737 ParentNodeIdx = Nodes[CurrentNodeIdx].ParentNode;
1738 }
1739 }
1740
1741 void RemoveLeafNode(int32 LeafNodeIdx, const TPayloadType& Payload)
1742 {
1743 if (UNLIKELY(!ensure(Nodes[LeafNodeIdx].bLeaf == true)))
1744 {
1745 UE_LOG(LogChaos, Warning, TEXT("AABBTree: RemoveLeafNode on node that is not a leaf"));
1746 }
1747
1748
1749 int32 LeafIdx = Nodes[LeafNodeIdx].ChildrenNodes[0];
1750
1751 if (Leaves[LeafIdx].GetElementCount() > 1)
1752 {
1753 Leaves[LeafIdx].RemoveElement(Payload);
1754 Leaves[LeafIdx].RecomputeBounds(bDynamicTree);
1755 TAABB<T, 3> ExpandedBounds = Leaves[LeafIdx].GetBounds();
1757 Nodes[LeafNodeIdx].ChildrenBounds[0] = ExpandedBounds;
1758 UpdateAncestorBounds(LeafNodeIdx);
1759 return;
1760 }
1761 else
1762 {
1763 Leaves[LeafIdx].RemoveElement(Payload); // Just to check if the element was in there to begin with
1764 }
1765
1766 int32 ParentNodeIdx = Nodes[LeafNodeIdx].ParentNode;
1767
1769 {
1770 int32 GrandParentNodeIdx = Nodes[ParentNodeIdx].ParentNode;
1771 int32 SiblingNodeLocalIdx = GetSiblingIndex(LeafNodeIdx);
1772 int32 SiblingNodeIdx = Nodes[ParentNodeIdx].ChildrenNodes[SiblingNodeLocalIdx];
1773
1775 {
1776 int32 ChildLocalIdx = WhichChildAmI(ParentNodeIdx);
1777 Nodes[GrandParentNodeIdx].ChildrenNodes[ChildLocalIdx] = SiblingNodeIdx;
1778 }
1779 else
1780 {
1781 RootNode = SiblingNodeIdx;
1782 }
1783
1785 {
1786 UE_LOG(LogChaos, Warning, TEXT("AABBTree: RemoveLeafNode loop detected"));
1787 }
1788
1789 Nodes[SiblingNodeIdx].ParentNode = GrandParentNodeIdx;
1790 UpdateAncestorBounds(SiblingNodeIdx);
1791 DeAllocateInternalNode(ParentNodeIdx);
1792 }
1793 else
1794 {
1795 RootNode = INDEX_NONE;
1796 }
1797 DeAllocateLeafNode(LeafNodeIdx);
1798 }
1799
1800 virtual bool RemoveElement(const TPayloadType& Payload) override
1801 {
1802 if (UNLIKELY(!ensure(bModifyingTreeMultiThreadingFastCheck == false)))
1803 {
1804 UE_LOG(LogChaos, Warning, TEXT("AABBTree: RemoveElement unsafe updated from multiple threads detected"));
1805 }
1806 bModifyingTreeMultiThreadingFastCheck = true;
1807 if (ensure(bMutable))
1808 {
1809 if (FAABBTreePayloadInfo* PayloadInfo = PayloadToInfo.Find(Payload))
1810 {
1811 if (PayloadInfo->GlobalPayloadIdx != INDEX_NONE)
1812 {
1813 ensure(PayloadInfo->DirtyPayloadIdx == INDEX_NONE);
1814 ensure(PayloadInfo->DirtyGridOverflowIdx == INDEX_NONE);
1815 ensure(PayloadInfo->LeafIdx == INDEX_NONE);
1816 if (PayloadInfo->GlobalPayloadIdx + 1 < GlobalPayloads.Num())
1817 {
1818 auto LastGlobalPayload = GlobalPayloads.Last().Payload;
1819 PayloadToInfo.FindChecked(LastGlobalPayload).GlobalPayloadIdx = PayloadInfo->GlobalPayloadIdx;
1820 }
1821 GlobalPayloads.RemoveAtSwap(PayloadInfo->GlobalPayloadIdx);
1822 }
1823 else if (PayloadInfo->DirtyPayloadIdx != INDEX_NONE)
1824 {
1825 if (DirtyElementTree != nullptr)
1826 {
1827 DirtyElementTree->RemoveLeafNode(PayloadInfo->DirtyPayloadIdx, Payload);
1828 }
1829 else if (DirtyElementGridEnabled())
1830 {
1831 DeleteDirtyParticleEverywhere(PayloadInfo->DirtyPayloadIdx, PayloadInfo->DirtyGridOverflowIdx);
1832 }
1833 else
1834 {
1835 if (PayloadInfo->DirtyPayloadIdx + 1 < DirtyElements.Num())
1836 {
1837 auto LastDirtyPayload = DirtyElements.Last().Payload;
1838 PayloadToInfo.FindChecked(LastDirtyPayload).DirtyPayloadIdx = PayloadInfo->DirtyPayloadIdx;
1839 }
1840 DirtyElements.RemoveAtSwap(PayloadInfo->DirtyPayloadIdx);
1841 }
1842 }
1843 else if (ensure(PayloadInfo->LeafIdx != INDEX_NONE))
1844 {
1845 if (bDynamicTree)
1846 {
1847 RemoveLeafNode(PayloadInfo->NodeIdx, Payload);
1848 }
1849 else
1850 {
1851 Leaves[PayloadInfo->LeafIdx].RemoveElement(Payload);
1852 }
1853 }
1854
1855 PayloadToInfo.Remove(Payload);
1856 bShouldRebuild = true;
1857 bModifyingTreeMultiThreadingFastCheck = false;
1858 return true;
1859 }
1860 }
1861 bModifyingTreeMultiThreadingFastCheck = false;
1862 return false;
1863 }
1864
1865 // Returns true if the payload's leaf need to be updated, false if the Payload can stay in the same leaf without changing the leaf size.
1866 // In the case this function returns false, the payload's bounding volume will be updated.
1867 virtual bool NeedUpdateElement(const TPayloadType& Payload, const TAABB<T, 3>& NewBounds) override
1868 {
1870 FAABBTreePayloadInfo* PayloadInfo = PayloadToInfo.Find(Payload);
1871 if (PayloadInfo && bDynamicTree)
1872 {
1873 if (PayloadInfo->LeafIdx != INDEX_NONE)
1874 {
1875 // The leaf node bounds can be larger than the actual leave bound
1876 const TAABB<T, 3>& LeafNodeBounds = Nodes[PayloadInfo->NodeIdx].ChildrenBounds[0];
1877 if (LeafNodeBounds.Contains(NewBounds.Min()) && LeafNodeBounds.Contains(NewBounds.Max()))
1878 {
1879 // We still need to update the constituent bounds, but the leaf won't change
1880 Leaves[PayloadInfo->LeafIdx].UpdateElementWithoutDirty(Payload, NewBounds);
1881 return false;
1882 }
1883 }
1884 }
1885 return true;
1886 }
1887
1888 // Returns true if element was updated, or false when it was added instead
1889 virtual bool UpdateElement(const TPayloadType& Payload, const TAABB<T, 3>& NewBounds, bool bInHasBounds) override
1890 {
1891 if (UNLIKELY(!ensure(bModifyingTreeMultiThreadingFastCheck == false)))
1892 {
1893 UE_LOG(LogChaos, Warning, TEXT("AABBTree: UpdateElement unsafe updated from multiple threads detected"));
1894 }
1895 bModifyingTreeMultiThreadingFastCheck = true;
1896#if !WITH_EDITOR
1897 //CSV_SCOPED_TIMING_STAT(ChaosPhysicsTimers, AABBTreeUpdateElement)
1898 //CSV_CUSTOM_STAT(ChaosPhysicsTimers, 1, 1, ECsvCustomStatOp::Accumulate);
1899 //CSV_CUSTOM_STAT(PhysicsCounters, NumIntoNP, 1, ECsvCustomStatOp::Accumulate);
1900#endif
1901
1902 bool bHasBounds = bInHasBounds;
1903 // If bounds are bad, use global
1904 if (bHasBounds && ValidateBounds(NewBounds) == false)
1905 {
1906 bHasBounds = false;
1908#if CHAOS_DEBUG_NAME
1909 if constexpr (std::is_same_v<TPayloadType, FAccelerationStructureHandle>)
1910 {
1912 {
1913 if (const FGeometryParticleHandle* Particle = Payload.GetGeometryParticleHandle_PhysicsThread())
1914 {
1915 DebugName = Particle->DebugName();
1916 }
1917 }
1918 else
1919 {
1920 if (const FGeometryParticle* Particle = Payload.GetExternalGeometryParticle_ExternalThread())
1921 {
1922 DebugName = Particle->DebugName();
1923 }
1924 }
1925 }
1926#endif
1927 UE_LOG(LogChaos, Warning, TEXT("AABBTree encountered invalid bounds. Min: %s Max: %s Name: %s")
1928 , *NewBounds.Min().ToString() , *NewBounds.Max().ToString()
1929 , DebugName.IsValid() ? **DebugName : TEXT("<Unknown>"));
1930 }
1931
1932 bool bElementExisted = true;
1933 if (ensure(bMutable))
1934 {
1935 FAABBTreePayloadInfo* PayloadInfo = PayloadToInfo.Find(Payload);
1936 if (PayloadInfo)
1937 {
1938 if (PayloadInfo->LeafIdx != INDEX_NONE)
1939 {
1940 //If we are still within the same leaf bounds, do nothing, don't detect a change either
1941 if (bHasBounds)
1942 {
1943 if (bDynamicTree)
1944 {
1945 // The leaf node bounds can be larger than the actual leave bound
1946 const TAABB<T, 3>& LeafNodeBounds = Nodes[PayloadInfo->NodeIdx].ChildrenBounds[0];
1947 if (LeafNodeBounds.Contains(NewBounds.Min()) && LeafNodeBounds.Contains(NewBounds.Max()))
1948 {
1949 // We still need to update the constituent bounds
1950 Leaves[PayloadInfo->LeafIdx].UpdateElement(Payload, NewBounds, bHasBounds);
1951 bModifyingTreeMultiThreadingFastCheck = false;
1952 return bElementExisted;
1953 }
1954 }
1955 else
1956 {
1957 const TAABB<T, 3>& LeafBounds = Leaves[PayloadInfo->LeafIdx].GetBounds();
1958 if (LeafBounds.Contains(NewBounds.Min()) && LeafBounds.Contains(NewBounds.Max()))
1959 {
1960 // We still need to update the constituent bounds
1961 Leaves[PayloadInfo->LeafIdx].UpdateElement(Payload, NewBounds, bHasBounds);
1962 bModifyingTreeMultiThreadingFastCheck = false;
1963 return bElementExisted;
1964 }
1965 }
1966 }
1967
1968 // DBVH remove from tree
1969
1970 if (bDynamicTree)
1971 {
1972 RemoveLeafNode(PayloadInfo->NodeIdx, Payload);
1973 PayloadInfo->LeafIdx = INDEX_NONE;
1974 PayloadInfo->NodeIdx = INDEX_NONE;
1975 }
1976 else
1977 {
1978 Leaves[PayloadInfo->LeafIdx].RemoveElement(Payload);
1979 PayloadInfo->LeafIdx = INDEX_NONE;
1980 }
1981 }
1982 }
1983 else
1984 {
1985 bElementExisted = false;
1986 PayloadInfo = &PayloadToInfo.Add(Payload);
1987 }
1988
1989 bShouldRebuild = true;
1990
1991 bool bTooBig = false;
1992 if (bHasBounds)
1993 {
1994 if (NewBounds.Extents().Max() > MaxPayloadBounds)
1995 {
1996 bTooBig = true;
1997 bHasBounds = false;
1998 }
1999 }
2000
2001 if (bHasBounds)
2002 {
2003 if (PayloadInfo->DirtyPayloadIdx == INDEX_NONE)
2004 {
2005 if (bDynamicTree)
2006 {
2007 NodeAndLeafIndices Indices = InsertLeaf(Payload, NewBounds);
2008 PayloadInfo->NodeIdx = Indices.NodeIdx;
2009 PayloadInfo->LeafIdx = Indices.LeafIdx;
2010 }
2011 else
2012 {
2013 if (DirtyElementTree)
2014 {
2015 // We save the node index for the dirty tree here:
2016 PayloadInfo->DirtyPayloadIdx = DirtyElementTree->InsertLeaf(Payload, NewBounds).NodeIdx;
2017 }
2018 else
2019 {
2020 PayloadInfo->DirtyPayloadIdx = DirtyElements.Add(FElement{ Payload, NewBounds });
2021
2022 if (DirtyElementGridEnabled())
2023 {
2024 PayloadInfo->DirtyGridOverflowIdx = AddDirtyElementToGrid(NewBounds, PayloadInfo->DirtyPayloadIdx);
2025 }
2026 }
2027 }
2028 }
2029 else
2030 {
2031 const int32 DirtyElementIndex = PayloadInfo->DirtyPayloadIdx;
2032 if (DirtyElementTree)
2033 {
2034 // The leaf node bounds can be larger than the actual leave bound
2035 const TAABB<T, 3>& LeafNodeBounds = DirtyElementTree->Nodes[DirtyElementIndex].ChildrenBounds[0];
2036 if (LeafNodeBounds.Contains(NewBounds.Min()) && LeafNodeBounds.Contains(NewBounds.Max()))
2037 {
2038 // We still need to update the constituent bounds
2039 const int32 DirtyLeafIndex = DirtyElementTree->Nodes[DirtyElementIndex].ChildrenNodes[0]; // A Leaf node's first child is the leaf index
2040 DirtyElementTree->Leaves[DirtyLeafIndex].UpdateElement(Payload, NewBounds, bHasBounds);
2041 DirtyElementTree->Leaves[DirtyLeafIndex].RecomputeBounds(bDynamicTree);
2042 }
2043 else
2044 {
2045 // Reinsert the leaf
2046 DirtyElementTree->RemoveLeafNode(DirtyElementIndex, Payload);
2047 PayloadInfo->DirtyPayloadIdx = DirtyElementTree->InsertLeaf(Payload, NewBounds).NodeIdx;
2048 }
2049 }
2050 else
2051 {
2052 if (DirtyElementGridEnabled())
2053 {
2054 //CSV_SCOPED_TIMING_STAT(ChaosPhysicsTimers, AABBUpElement)
2055 PayloadInfo->DirtyGridOverflowIdx = UpdateDirtyElementInGrid(NewBounds, DirtyElementIndex, PayloadInfo->DirtyGridOverflowIdx);
2056 }
2057 DirtyElements[DirtyElementIndex].Bounds = NewBounds;
2058 UpdateElementHelper(DirtyElements[DirtyElementIndex].Payload, Payload);
2059 }
2060 }
2061
2062 // Handle something that previously did not have bounds that may be in global elements.
2063 if (PayloadInfo->GlobalPayloadIdx != INDEX_NONE)
2064 {
2065 if (PayloadInfo->GlobalPayloadIdx + 1 < GlobalPayloads.Num())
2066 {
2067 auto LastGlobalPayload = GlobalPayloads.Last().Payload;
2068 PayloadToInfo.FindChecked(LastGlobalPayload).GlobalPayloadIdx = PayloadInfo->GlobalPayloadIdx;
2069 }
2070 GlobalPayloads.RemoveAtSwap(PayloadInfo->GlobalPayloadIdx);
2071
2072 PayloadInfo->GlobalPayloadIdx = INDEX_NONE;
2073 }
2074 }
2075 else
2076 {
2078 if (PayloadInfo->GlobalPayloadIdx == INDEX_NONE)
2079 {
2080 PayloadInfo->GlobalPayloadIdx = GlobalPayloads.Add(FElement{ Payload, GlobalBounds });
2081 }
2082 else
2083 {
2084 GlobalPayloads[PayloadInfo->GlobalPayloadIdx].Bounds = GlobalBounds;
2085 UpdateElementHelper(GlobalPayloads[PayloadInfo->GlobalPayloadIdx].Payload, Payload);
2086 }
2087
2088 // Handle something that previously had bounds that may be in dirty elements.
2089 if (PayloadInfo->DirtyPayloadIdx != INDEX_NONE)
2090 {
2091 if (DirtyElementTree)
2092 {
2093 DirtyElementTree->RemoveLeafNode(PayloadInfo->DirtyPayloadIdx, Payload);
2094 }
2095 else
2096 {
2097 if (DirtyElementGridEnabled())
2098 {
2099 DeleteDirtyParticleEverywhere(PayloadInfo->DirtyPayloadIdx, PayloadInfo->DirtyGridOverflowIdx);
2100 }
2101 else
2102 {
2103 if (PayloadInfo->DirtyPayloadIdx + 1 < DirtyElements.Num())
2104 {
2105 auto LastDirtyPayload = DirtyElements.Last().Payload;
2106 PayloadToInfo.FindChecked(LastDirtyPayload).DirtyPayloadIdx = PayloadInfo->DirtyPayloadIdx;
2107 }
2108 DirtyElements.RemoveAtSwap(PayloadInfo->DirtyPayloadIdx);
2109 }
2110 }
2111
2112 PayloadInfo->DirtyPayloadIdx = INDEX_NONE;
2113 PayloadInfo->DirtyGridOverflowIdx = INDEX_NONE;
2114 }
2115 }
2116 }
2117
2118 if(!DirtyElementTree && !bDynamicTree && DirtyElements.Num() > MaxDirtyElements)
2119 {
2120 UE_LOG(LogChaos, Verbose, TEXT("Bounding volume exceeded maximum dirty elements (%d dirty of max %d) and is forcing a tree rebuild."), DirtyElements.Num(), MaxDirtyElements);
2121 ReoptimizeTree();
2122 }
2123 bModifyingTreeMultiThreadingFastCheck = false;
2124 return bElementExisted;
2125 }
2126
2128 {
2129 return DirtyElements.Num();
2130 }
2131
2132 // Some useful statistics
2134 {
2135 // Update the stats that needs it first
2136 TreeStats.StatNumDirtyElements = DirtyElements.Num();
2137 TreeStats.StatNumGridOverflowElements = DirtyElementsGridOverflow.Num();
2138 return TreeStats;
2139 }
2140
2142 {
2143 TreeExpensiveStats.StatMaxDirtyElements = DirtyElements.Num();
2144 TreeExpensiveStats.StatMaxNumLeaves = Leaves.Num();
2145 int32 StatMaxLeafSize = 0;
2146 for (int LeafIndex = 0; LeafIndex < Leaves.Num(); LeafIndex++)
2147 {
2148 const TLeafType& Leaf = Leaves[LeafIndex];
2149
2150 StatMaxLeafSize = FMath::Max(StatMaxLeafSize, (int32)Leaf.GetElementCount());
2151 }
2152 TreeExpensiveStats.StatMaxLeafSize = StatMaxLeafSize;
2153 TreeExpensiveStats.StatMaxTreeDepth = (Nodes.Num() == 0) ? 0 : GetSubtreeDepth(0);
2154 TreeExpensiveStats.StatGlobalPayloadsSize = GlobalPayloads.Num();
2155
2156 return TreeExpensiveStats;
2157 }
2158
2159 const int32 GetSubtreeDepth(const int32 NodeIdx)
2160 {
2161 const FNode& Node = Nodes[NodeIdx];
2162 if (Node.bLeaf)
2163 {
2164 return 1;
2165 }
2166 else
2167 {
2168 return FMath::Max(GetSubtreeDepth(Node.ChildrenNodes[0]), GetSubtreeDepth(Node.ChildrenNodes[1])) + 1;
2169 }
2170 }
2171
2173 {
2174 return GlobalPayloads;
2175 }
2176
2177
2178 virtual bool ShouldRebuild() override { return bDynamicTree ? false : bShouldRebuild; } // Used to find out if something changed since last reset for optimizations
2179 // Contract: bShouldRebuild can only ever be cleared by calling the ClearShouldRebuild method, it can be set at will though
2180 virtual void ClearShouldRebuild() override { bShouldRebuild = false; }
2181
2182 virtual bool IsTreeDynamic() const override { return bDynamicTree; }
2183 void SetTreeToDynamic() { bDynamicTree = true; } // Tree cannot be changed back to static for now
2184
2186 {
2187 check(this != &InFrom);
2188 check(InFrom.GetType() == ESpatialAcceleration::AABBTree);
2189 const TAABBTree& From = static_cast<const TAABBTree&>(InFrom);
2190
2191 Reset();
2192
2193 // Copy all the small objects first
2194
2196
2197 DirtyElementGridCellSize = From.DirtyElementGridCellSize;
2198 DirtyElementGridCellSizeInv = From.DirtyElementGridCellSizeInv;
2199 DirtyElementMaxGridCellQueryCount = From.DirtyElementMaxGridCellQueryCount;
2200 DirtyElementMaxPhysicalSizeInCells = From.DirtyElementMaxPhysicalSizeInCells;
2201 DirtyElementMaxCellCapacity = From.DirtyElementMaxCellCapacity;
2202
2203 MaxChildrenInLeaf = From.MaxChildrenInLeaf;
2204 MaxTreeDepth = From.MaxTreeDepth;
2205 MaxPayloadBounds = From.MaxPayloadBounds;
2206 MaxNumToProcess = From.MaxNumToProcess;
2207 NumProcessedThisSlice = From.NumProcessedThisSlice;
2208
2209 StartSliceTimeStamp = From.StartSliceTimeStamp;
2210
2211 bShouldRebuild = From.bShouldRebuild;
2212
2213 RootNode = From.RootNode;
2214 FirstFreeInternalNode = From.FirstFreeInternalNode;
2215 FirstFreeLeafNode = From.FirstFreeLeafNode;
2216
2217 // Reserve sizes for arrays etc
2218
2219 Nodes.Reserve(From.Nodes.Num());
2220 Leaves.Reserve(From.Leaves.Num());
2221 DirtyElements.Reserve(From.DirtyElements.Num());
2222 CellHashToFlatArray.Reserve(From.CellHashToFlatArray.Num());
2223 FlattenedCellArrayOfDirtyIndices.Reserve(From.FlattenedCellArrayOfDirtyIndices.Num());
2224 DirtyElementsGridOverflow.Reserve(From.DirtyElementsGridOverflow.Num());
2225 GlobalPayloads.Reserve(From.GlobalPayloads.Num());
2226 PayloadToInfo.Reserve(From.PayloadToInfo.Num());
2227 OverlappingLeaves.Reserve(From.OverlappingLeaves.Num());
2228 OverlappingOffsets.Reserve(From.OverlappingOffsets.Num());
2229 OverlappingPairs.Reserve(From.OverlappingPairs.Num());
2230 OverlappingCounts.Reserve(From.OverlappingCounts.Num());
2231
2232 this->SetAsyncTimeSlicingComplete(false);
2233
2234 if (From.DirtyElementTree)
2235 {
2236 if (!DirtyElementTree)
2237 {
2238 DirtyElementTree = TUniquePtr<TAABBTree>(new TAABBTree());
2239 }
2240 DirtyElementTree->PrepareCopyTimeSliced(*(From.DirtyElementTree));
2241 }
2242 else
2243 {
2244 DirtyElementTree = nullptr;
2245 }
2246 }
2247
2249 {
2250 check(this != &InFrom);
2251 check(InFrom.GetType() == ESpatialAcceleration::AABBTree);
2252 const TAABBTree& From = static_cast<const TAABBTree&>(InFrom);
2253
2255 check(From.CellHashToFlatArray.Num() == 0); // Partial Copy of TMAPs not implemented, and this should be empty for our current use cases
2256
2258 {
2259 bool bCanContinueCopy = true;
2260 const bool bForceCopyAll = MaxSizeToCopy == -1;
2261 if (bForceCopyAll)
2262 {
2263 return bCanContinueCopy;
2264 }
2265
2267 {
2268 // Checking for platform time every time is expensive as its cost adds up fast, so only do it between a configured amount of elements.
2269 // This means we might overshoot the budget, but on the other hand we will keep most of the time doing actual work.
2270 CurrentDataElementsCopiedSinceLastCheck++;
2271 if (CurrentDataElementsCopiedSinceLastCheck > FAABBTimeSliceCVars::MinDataChunkToProcessBetweenTimeChecks)
2272 {
2273 CurrentDataElementsCopiedSinceLastCheck = 0;
2274 return bCanContinueCopy;
2275 }
2276
2277 const double ElapseTime = FPlatformTime::Seconds() - StartSliceTimeStamp;
2278
2280 {
2281 bCanContinueCopy = false;
2282 }
2283 }
2284 else
2285 {
2287 }
2288
2289 return bCanContinueCopy;
2290 };
2291
2292 constexpr int32 SizeCopiedSoFar = 0;
2294 {
2295 // For data copy, the time stamp is set from the setup phase, and it is copied from the tree we are copying from.
2296 // Therefore if we reach this point with no time available left, just reset it so the next frame can start counting from scratch
2297 StartSliceTimeStamp = 0.0;
2298 return;
2299 }
2300
2301 if (FMath::IsNearlyZero(StartSliceTimeStamp))
2302 {
2303 StartSliceTimeStamp = FPlatformTime::Seconds();
2304 }
2305
2306 if (!ContinueTimeSliceCopy(From.Nodes, Nodes, SizeToCopyLeft, CanContinueCopyingDataCallback))
2307 {
2308 return;
2309 }
2310 if (!ContinueTimeSliceCopy(From.Leaves, Leaves, SizeToCopyLeft, CanContinueCopyingDataCallback))
2311 {
2312 return;
2313 }
2314 if (!ContinueTimeSliceCopy(From.DirtyElements, DirtyElements, SizeToCopyLeft, CanContinueCopyingDataCallback))
2315 {
2316 return;
2317 }
2318
2319 if (!ContinueTimeSliceCopy(From.FlattenedCellArrayOfDirtyIndices, FlattenedCellArrayOfDirtyIndices, SizeToCopyLeft, CanContinueCopyingDataCallback))
2320 {
2321 return;
2322 }
2323 if (!ContinueTimeSliceCopy(From.DirtyElementsGridOverflow, DirtyElementsGridOverflow, SizeToCopyLeft, CanContinueCopyingDataCallback))
2324 {
2325 return;
2326 }
2327 if (!ContinueTimeSliceCopy(From.GlobalPayloads, GlobalPayloads, SizeToCopyLeft, CanContinueCopyingDataCallback))
2328 {
2329 return;
2330 }
2331 if (!ContinueTimeSliceCopy(From.PayloadToInfo, PayloadToInfo, SizeToCopyLeft, CanContinueCopyingDataCallback))
2332 {
2333 return;
2334 }
2335 if (!ContinueTimeSliceCopy(From.OverlappingLeaves, OverlappingLeaves, SizeToCopyLeft, CanContinueCopyingDataCallback))
2336 {
2337 return;
2338 }
2339 if (!ContinueTimeSliceCopy(From.OverlappingOffsets, OverlappingOffsets, SizeToCopyLeft, CanContinueCopyingDataCallback))
2340 {
2341 return;
2342 }
2343 if (!ContinueTimeSliceCopy(From.OverlappingCounts, OverlappingCounts, SizeToCopyLeft, CanContinueCopyingDataCallback))
2344 {
2345 return;
2346 }
2347 if (!ContinueTimeSliceCopy(From.OverlappingPairs, OverlappingPairs, SizeToCopyLeft, CanContinueCopyingDataCallback))
2348 {
2349 return;
2350 }
2351
2352 if (DirtyElementTree)
2353 {
2354 check(From.DirtyElementTree);
2355 DirtyElementTree->ProgressCopyTimeSliced(*(From.DirtyElementTree), MaximumBytesToCopy);
2356 if (!DirtyElementTree->IsAsyncTimeSlicingComplete())
2357 {
2358 return;
2359 }
2360 }
2361
2362 this->SetAsyncTimeSlicingComplete(true);
2363 }
2364
2365 // Returns true if bounds appear valid. Returns false if extremely large values, contains NaN, or is empty.
2367 {
2368 const TVec3<T>& Min = Bounds.Min();
2369 const TVec3<T>& Max = Bounds.Max();
2370
2371 for (int32 i = 0; i < 3; ++i)
2372 {
2373 const T& MinComponent = Min[i];
2374 const T& MaxComponent = Max[i];
2375
2376 // Are we an empty aabb?
2378 {
2379 return false;
2380 }
2381
2382 // Are we NaN/Inf?
2383 if (!FMath::IsFinite(MinComponent) || !FMath::IsFinite(MaxComponent))
2384 {
2385 return false;
2386 }
2387 }
2388
2389 return true;
2390 }
2391
2392 virtual void Serialize(FChaosArchive& Ar) override
2393 {
2395
2397 {
2398 // Serialize out unused aabb for earlier versions
2399 TAABB<T, 3> Dummy(TVec3<T>((T)0), TVec3<T>((T)0));
2401 }
2402 Ar << Nodes;
2403 Ar << Leaves;
2404 Ar << DirtyElements;
2405 Ar << GlobalPayloads;
2406
2409 {
2411 }
2412 else
2413 {
2415 }
2416
2418 {
2419 Ar << PayloadToInfo;
2420
2421 if (!bMutable) //if immutable empty this even if we had to serialize it in for backwards compat
2422 {
2423 PayloadToInfo.Empty();
2424 }
2425 }
2426
2427 Ar << MaxChildrenInLeaf;
2428 Ar << MaxTreeDepth;
2429 Ar << MaxPayloadBounds;
2430
2431 if (Ar.IsLoading())
2432 {
2433 // Disable the Grid until it is rebuilt
2434 DirtyElementGridCellSize = 0.0f;
2435 DirtyElementGridCellSizeInv = 1.0f;
2436 bShouldRebuild = true;
2437 }
2438
2439 // Dynamic trees are not serialized/deserialized for now
2440 if (Ar.IsLoading())
2441 {
2442 bModifyingTreeMultiThreadingFastCheck = false;
2443 bDynamicTree = false;
2444 bBuildOverlapCache = true;
2445 RootNode = INDEX_NONE;
2446 FirstFreeInternalNode = INDEX_NONE;
2447 FirstFreeLeafNode = INDEX_NONE;
2448 }
2449 else
2450 {
2451 ensure(bDynamicTree == false);
2452 }
2453 }
2454
2460 void FindOverlappingLeaf(const int32 FirstNode, const int32 LeafIndex, TArray<int32>& NodeStack)
2461 {
2462 const TAABB<T,3>& LeafBounds = Leaves[LeafIndex].GetBounds();
2463
2464 NodeStack.Reset();
2465 NodeStack.Add(FirstNode);
2466
2467 int32 NodeIndex = INDEX_NONE;
2468 while (NodeStack.Num())
2469 {
2470 NodeIndex = NodeStack.Pop(EAllowShrinking::No);
2471 const FNode& Node = Nodes[NodeIndex];
2472
2473 // If a leaf directly test the bounds
2474 if (Node.bLeaf)
2475 {
2476 if (LeafBounds.Intersects(Leaves[Node.ChildrenNodes[0]].GetBounds()))
2477 {
2478 OverlappingLeaves.Add(Node.ChildrenNodes[0]);
2479 }
2480 }
2481 else
2482 {
2483 // If not loop over all the children nodes to check if they intersect the bounds
2484 for(int32 ChildIndex = 0; ChildIndex < 2; ++ChildIndex)
2485 {
2486 if(LeafBounds.Intersects(Node.ChildrenBounds[ChildIndex]) && Node.ChildrenNodes[ChildIndex] != INDEX_NONE)
2487 {
2488 NodeStack.Add(Node.ChildrenNodes[ChildIndex]);
2489 }
2490 }
2491 }
2492 }
2493 }
2494
2497 const TAABBTreeNode<T>& RightNode, const TAABB<T, 3>& RightBounds, const bool bDirtyFilter)
2498 {
2499 // If dirty filter enabled only look for overlapping leaves if one of the 2 nodes are dirty
2500 if(!bDirtyFilter || (bDirtyFilter && (LeftNode.bDirtyNode || RightNode.bDirtyNode)))
2501 {
2502 if(LeftBounds.Intersects(RightBounds))
2503 {
2504 // If left and right are leaves check for intersection
2505 if(LeftNode.bLeaf && RightNode.bLeaf)
2506 {
2507 const int32 LeftLeaf = LeftNode.ChildrenNodes[0];
2508 const int32 RightLeaf = RightNode.ChildrenNodes[0];
2509
2510 // Same condition as for the nodes
2511 if(!bDirtyFilter || (bDirtyFilter && (Leaves[LeftLeaf].IsLeafDirty() || Leaves[RightLeaf].IsLeafDirty())))
2512 {
2513 if(Leaves[LeftLeaf].GetBounds().Intersects(Leaves[RightLeaf].GetBounds()))
2514 {
2515 OverlappingPairs.Add(FIntVector2(LeftLeaf, RightLeaf));
2516 ++OverlappingCounts[LeftLeaf];
2517 ++OverlappingCounts[RightLeaf];
2518 }
2519 }
2520 }
2521 // If only left is a leaf continue recursion with the right node children
2522 else if(LeftNode.bLeaf)
2523 {
2524 AddNodesOverlappingLeaves(LeftNode, LeftBounds, Nodes[RightNode.ChildrenNodes[0]], RightNode.ChildrenBounds[0], bDirtyFilter);
2525 AddNodesOverlappingLeaves(LeftNode, LeftBounds, Nodes[RightNode.ChildrenNodes[1]], RightNode.ChildrenBounds[1], bDirtyFilter);
2526 }
2527 // Otherwise continue recursion with the left node children
2528 else
2529 {
2530 AddNodesOverlappingLeaves(Nodes[LeftNode.ChildrenNodes[0]], LeftNode.ChildrenBounds[0], RightNode, RightBounds, bDirtyFilter);
2531 AddNodesOverlappingLeaves(Nodes[LeftNode.ChildrenNodes[1]], LeftNode.ChildrenBounds[1], RightNode, RightBounds, bDirtyFilter);
2532 }
2533 }
2534 }
2535 }
2536
2539 {
2540 if(!TreeNode.bLeaf)
2541 {
2542 // Find overlapping leaves within the left and right children
2543 AddRootOverlappingLeaves(Nodes[TreeNode.ChildrenNodes[0]], bDirtyFilter);
2544 AddRootOverlappingLeaves(Nodes[TreeNode.ChildrenNodes[1]], bDirtyFilter);
2545
2546 // Then try finding some overlaps in between the 2 children
2547 AddNodesOverlappingLeaves(Nodes[TreeNode.ChildrenNodes[0]], TreeNode.ChildrenBounds[0],
2548 Nodes[TreeNode.ChildrenNodes[1]], TreeNode.ChildrenBounds[1], bDirtyFilter);
2549 }
2550 }
2551
2554 {
2555 for(int32 LeafIndex = 0, NumLeaves = Leaves.Num(); LeafIndex < NumLeaves; ++LeafIndex)
2556 {
2557 int32 NumOverlaps = 0;
2558 if(!Leaves[LeafIndex].IsLeafDirty())
2559 {
2560 for(int32 OverlappingIndex = OverlappingOffsets[LeafIndex]; OverlappingIndex < OverlappingOffsets[LeafIndex+1]; ++OverlappingIndex)
2561 {
2562 if(!Leaves[OverlappingLeaves[OverlappingIndex]].IsLeafDirty())
2563 {
2564 if(LeafIndex < OverlappingLeaves[OverlappingIndex])
2565 {
2566 OverlappingPairs.Add(FIntVector2(LeafIndex, OverlappingLeaves[OverlappingIndex]));
2567 }
2568 if(LeafIndex != OverlappingLeaves[OverlappingIndex])
2569 {
2570 ++NumOverlaps;
2571 }
2572 }
2573 }
2574 }
2575 // Make sure the leaf is intersecting itself if several elements per leaf
2576 OverlappingPairs.Add(FIntVector2(LeafIndex, LeafIndex));
2577 ++NumOverlaps;
2578
2579 OverlappingCounts[LeafIndex] = NumOverlaps;
2580 }
2581 }
2584 {
2585 for(int32 NodeIndex = 0, NumNodes = Nodes.Num(); NodeIndex < NumNodes; ++NodeIndex)
2586 {
2587 if(Nodes[NodeIndex].bLeaf)
2588 {
2589 Nodes[NodeIndex].bDirtyNode = Leaves[Nodes[NodeIndex].ChildrenNodes[0]].IsLeafDirty();
2590 if(Nodes[NodeIndex].bDirtyNode)
2591 {
2592 int32 NodeParent = Nodes[NodeIndex].ParentNode;
2593 while(NodeParent != INDEX_NONE && !Nodes[NodeParent].bDirtyNode)
2594 {
2595 Nodes[NodeParent].bDirtyNode = true;
2596 NodeParent = Nodes[NodeParent].ParentNode;
2597 }
2598 }
2599 }
2600 }
2601 }
2602
2605 {
2606 if(!bDynamicTree || (bDynamicTree && RootNode == INDEX_NONE)) return;
2607
2608 OverlappingOffsets.SetNum(Leaves.Num()+1, EAllowShrinking::No);
2609 OverlappingCounts.SetNum(Leaves.Num(), EAllowShrinking::No);
2610 OverlappingPairs.Reset();
2611
2612 if(bDirtyFilter)
2613 {
2614 FillPersistentOverlappingPairs();
2615 PropagateLeavesDirtyFlag();
2616 }
2617 else
2618 {
2619 for(int32 LeafIndex = 0, NumLeaves = Leaves.Num(); LeafIndex < NumLeaves; ++LeafIndex)
2620 {
2621 // Make sure the leaf is intersecting itself if several elements per leaf
2622 OverlappingPairs.Add(FIntVector2(LeafIndex, LeafIndex));
2623 OverlappingCounts[LeafIndex] = 1;
2624 }
2625 }
2626 AddRootOverlappingLeaves(Nodes[RootNode], bDirtyFilter);
2627
2628 OverlappingOffsets[0] = 0;
2629 for(int32 LeafIndex = 0, NumLeaves = Leaves.Num(); LeafIndex < NumLeaves; ++LeafIndex)
2630 {
2631 OverlappingOffsets[LeafIndex+1] = OverlappingOffsets[LeafIndex] + OverlappingCounts[LeafIndex];
2632 OverlappingCounts[LeafIndex] = OverlappingOffsets[LeafIndex];
2633 }
2634 OverlappingLeaves.SetNum(OverlappingOffsets.Last(), EAllowShrinking::No);
2635 for(auto& OverlappingPair : OverlappingPairs)
2636 {
2637 if(OverlappingPair[0] != OverlappingPair[1])
2638 {
2639 OverlappingLeaves[OverlappingCounts[OverlappingPair[0]]++] = OverlappingPair[1];
2640 OverlappingLeaves[OverlappingCounts[OverlappingPair[1]]++] = OverlappingPair[0];
2641 }
2642 else
2643 {
2644 OverlappingLeaves[OverlappingCounts[OverlappingPair[0]]++] = OverlappingPair[0];
2645 }
2646 }
2647 if(bDirtyFilter)
2648 {
2649 for(int32 LeafIndex = 0, NumLeaves = Leaves.Num(); LeafIndex < NumLeaves; ++LeafIndex)
2650 {
2651 Leaves[LeafIndex].SetDirtyState(false);
2652 }
2653 for(int32 NodeIndex = 0, NumNodes = Nodes.Num(); NodeIndex < NumNodes; ++NodeIndex)
2654 {
2655 Nodes[NodeIndex].bDirtyNode = false;
2656 }
2657 }
2658 }
2659
2662 {
2663 OverlappingOffsets.SetNum(Leaves.Num()+1, EAllowShrinking::No);
2664 OverlappingLeaves.Reset();
2665
2666 if(!bDynamicTree || (bDynamicTree && RootNode == INDEX_NONE)) return;
2667
2668 TArray<int32> NodeStack;
2669 const int32 FirstNode = bDynamicTree ? RootNode : 0;
2670
2671 for(int32 LeafIndex = 0, NumLeaves = Leaves.Num(); LeafIndex < NumLeaves; ++LeafIndex)
2672 {
2673 OverlappingOffsets[LeafIndex] = OverlappingLeaves.Num();
2674 FindOverlappingLeaf(FirstNode, LeafIndex, NodeStack);
2675 }
2676 OverlappingOffsets.Last() = OverlappingLeaves.Num();
2677 }
2678
2680 virtual void CacheOverlappingLeaves() override
2681 {
2682 if (!bBuildOverlapCache)
2683 {
2684 return;
2685 }
2686
2687 // Dev settings to switch easily algorithms
2688 // Will switch to cvars if the leaf version could be faster
2689 const bool bCachingRoot = true;
2690 const bool bDirtyFilter = false;
2691
2692 if(bCachingRoot)
2693 {
2694 ComputeOverlappingCacheFromRoot(bDirtyFilter);
2695 }
2696 else
2697 {
2698 ComputeOverlappingCacheFromLeaf();
2699 }
2700 }
2701
2704 {
2705 UE_LOG(LogChaos, Log, TEXT("Num Leaves = %d"), Leaves.Num());
2706 for(int32 LeafIndex = 0, NumLeaves = Leaves.Num(); LeafIndex < NumLeaves; ++LeafIndex)
2707 {
2708 auto& Leaf = Leaves[LeafIndex];
2709 UE_LOG(LogChaos, Log, TEXT("Overlapping Count[%d] = %d with bounds = %f %f %f | %f %f %f"), LeafIndex,
2710 OverlappingOffsets[LeafIndex+1] - OverlappingOffsets[LeafIndex], Leaf.GetBounds().Min()[0],
2711 Leaf.GetBounds().Min()[1], Leaf.GetBounds().Min()[2], Leaf.GetBounds().Max()[0], Leaf.GetBounds().Max()[1], Leaf.GetBounds().Max()[2]);
2712
2713 for(int32 OverlappingIndex = OverlappingOffsets[LeafIndex]; OverlappingIndex < OverlappingOffsets[LeafIndex+1]; ++OverlappingIndex)
2714 {
2715 UE_LOG(LogChaos, Log, TEXT("Overlapping Leaf[%d] = %d"), LeafIndex, OverlappingLeaves[OverlappingIndex]);
2716 }
2717 }
2718 }
2719
2720 const TArray<TAABBTreeNode<T>>& GetNodes() const { return Nodes; }
2721 const TArray<TLeafType>& GetLeaves() const { return Leaves; }
2722
2723private:
2724
2725 void ReoptimizeTree()
2726 {
2727 check(!DirtyElementTree && !bDynamicTree);
2728 TRACE_CPUPROFILER_EVENT_SCOPE(TAABBTree::ReoptimizeTree);
2729 TArray<FElement> AllElements;
2730
2731 int32 ReserveCount = DirtyElements.Num() + GlobalPayloads.Num();
2732 for (int LeafIndex = 0; LeafIndex < Leaves.Num(); LeafIndex++)
2733 {
2734 const TLeafType& Leaf = Leaves[LeafIndex];
2735 ReserveCount += static_cast<int32>(Leaf.GetReserveCount());
2736 }
2737
2738 AllElements.Reserve(ReserveCount);
2739
2740 AllElements.Append(DirtyElements);
2741 AllElements.Append(GlobalPayloads);
2742
2743 for (int LeafIndex = 0; LeafIndex < Leaves.Num(); LeafIndex++)
2744 {
2745 TLeafType& Leaf = Leaves[LeafIndex];
2746 Leaf.GatherElements(AllElements);
2747 }
2748
2749 TAABBTree NewTree(AllElements);
2750 *this = NewTree;
2751 bShouldRebuild = true; // No changes since last time tree was built
2752 }
2753
2754 // Returns true if the query should continue
2755 // Execute a function for all cells found in a query as well as the overflow
2756 template <typename FunctionType>
2757 bool DoForHitGridCellsAndOverflow(TArray<DirtyGridHashEntry>& HashEntryForOverlappedCells, FunctionType Function) const
2758 {
2759
2760 // Now merge and iterate the lists of elements found in the overlapping cells
2761 bool DoneWithGridElements = false;
2762 bool DoneWithNonGridElements = false;
2763 int NonGridElementIter = 0;
2765 {
2766 // Get the next dirty element index
2767
2768 int32 SmallestDirtyParticleIndex = INT_MAX; // Best dirty particle index to find
2769
2771 {
2772 // Find the next smallest index
2773 // This will start slowing down if we are overlapping a lot of cells
2774 DoneWithGridElements = true;
2775 for (const DirtyGridHashEntry& HashEntry : HashEntryForOverlappedCells)
2776 {
2777 int32 Count = HashEntry.Count;
2778 if (Count > 0)
2779 {
2780 int32 DirtyParticleIndex = FlattenedCellArrayOfDirtyIndices[HashEntry.Index];
2782 {
2784 DoneWithGridElements = false;
2785 }
2786 }
2787 }
2788 }
2789
2790 // Now skip all elements with the same best index
2792 {
2793 for (DirtyGridHashEntry& HashEntry : HashEntryForOverlappedCells)
2794 {
2795 int32 Count = HashEntry.Count;
2796 if (Count > 0)
2797 {
2798 int32 DirtyParticleIndex = FlattenedCellArrayOfDirtyIndices[HashEntry.Index];
2800 {
2801 ++HashEntry.Index; // Increment Index
2802 --HashEntry.Count; // Decrement count
2803 }
2804 }
2805 }
2806 }
2807
2808 DoneWithNonGridElements = NonGridElementIter >= DirtyElementsGridOverflow.Num();
2810 {
2811 SmallestDirtyParticleIndex = DirtyElementsGridOverflow[NonGridElementIter];
2813 }
2814
2815 // Elements that are in the overflow should not also be in the grid
2816 ensure(DoneWithGridElements || PayloadToInfo.Find(DirtyElements[SmallestDirtyParticleIndex].Payload)->DirtyGridOverflowIdx == INDEX_NONE);
2817
2819 {
2821 const auto& Elem = DirtyElements[Index];
2822
2823 if (!Function(Elem))
2824 {
2825 return false;
2826 }
2827 }
2828 }
2829 return true;
2830 }
2831
2838 template <EAABBQueryType Query, typename SQVisitor>
2839 bool OverlapCached(const FAABB3& QueryBounds, SQVisitor& Visitor, bool& bOverlapResult) const
2840 {
2841 bool bCouldUseCache = false;
2842 bOverlapResult = true;
2843 // Only the overlap queries could use the caching
2844 if (Query == EAABBQueryType::Overlap && Visitor.GetQueryPayload())
2845 {
2846 // Grab the payload from the visitor (only available on physics thread for now) and retrieve the info
2847 const TPayloadType& QueryPayload = *static_cast<const TPayloadType*>(Visitor.GetQueryPayload());
2848 if( const FAABBTreePayloadInfo* QueryInfo = PayloadToInfo.Find(QueryPayload))
2849 {
2850 const int32 LeafIndex = QueryInfo->LeafIdx;
2851 if(LeafIndex != INDEX_NONE && LeafIndex < (OverlappingOffsets.Num()-1))
2852 {
2853 //Once we have the leaf index we can loop over the overlapping leaves
2854 for(int32 OverlappingIndex = OverlappingOffsets[LeafIndex];
2855 OverlappingIndex < OverlappingOffsets[LeafIndex+1]; ++OverlappingIndex)
2856 {
2857 const TLeafType& OverlappingLeaf = Leaves[OverlappingLeaves[OverlappingIndex]];
2858 if (OverlappingLeaf.OverlapFast(QueryBounds, Visitor) == false)
2859 {
2860 bOverlapResult = false;
2861 break;
2862 }
2863 }
2864 bCouldUseCache = true;
2865 }
2866 }
2867 }
2868 return bCouldUseCache;
2869 }
2870
2871 template <EAABBQueryType Query, typename TQueryFastData, typename SQVisitor>
2872 bool QueryImp(const FVec3& RESTRICT Start, TQueryFastData& CurData, const FVec3& QueryHalfExtents, const FAABB3& QueryBounds, SQVisitor& Visitor, const FVec3& Dir, const FVec3& InvDir, const bool bParallel[3]) const
2873 {
2874 PHYSICS_CSV_CUSTOM_VERY_EXPENSIVE(PhysicsCounters, MaxDirtyElements, DirtyElements.Num(), ECsvCustomStatOp::Max);
2877 //QUICK_SCOPE_CYCLE_COUNTER(AABBTreeQueryImp);
2878#if !WITH_EDITOR
2879 //CSV_SCOPED_TIMING_STAT(ChaosPhysicsTimers, AABBTreeQuery)
2880#endif
2881 FReal TOI = 0;
2882 {
2883 //QUICK_SCOPE_CYCLE_COUNTER(QueryGlobal);
2885 for(const auto& Elem : GlobalPayloads)
2886 {
2887 if (PrePreFilterHelper(Elem.Payload, Visitor))
2888 {
2889 continue;
2890 }
2891
2892 const FAABB3 InstanceBounds(Elem.Bounds.Min(), Elem.Bounds.Max());
2893 if(TAABBTreeIntersectionHelper<TQueryFastData,Query>::Intersects(Start, CurData, TOI, InstanceBounds, QueryBounds, QueryHalfExtents, Dir, InvDir, bParallel))
2894 {
2896 bool bContinue;
2897 if(Query == EAABBQueryType::Overlap)
2898 {
2899 bContinue = Visitor.VisitOverlap(VisitData);
2900 } else
2901 {
2902 bContinue = Query == EAABBQueryType::Sweep ? Visitor.VisitSweep(VisitData,CurData) : Visitor.VisitRaycast(VisitData,CurData);
2903 }
2904
2905 if(!bContinue)
2906 {
2907 return false;
2908 }
2909 }
2910 }
2911 }
2912
2913 if (DirtyElementTree)
2914 {
2915 if (!DirtyElementTree->template QueryImp<Query, TQueryFastData, SQVisitor>(Start, CurData, QueryHalfExtents, QueryBounds, Visitor, Dir, InvDir, bParallel))
2916 {
2917 return false;
2918 }
2919 }
2920 else if (bMutable)
2921 { // Returns true if we should continue
2922 auto IntersectAndVisit = [&](const FElement& Elem) -> bool
2923 {
2924 const FAABB3 InstanceBounds(Elem.Bounds.Min(), Elem.Bounds.Max());
2925 if (PrePreFilterHelper(Elem.Payload, Visitor))
2926 {
2927 return true;
2928 }
2929
2930 if (TAABBTreeIntersectionHelper<TQueryFastData, Query>::Intersects(Start, CurData, TOI, InstanceBounds, QueryBounds, QueryHalfExtents, Dir, InvDir, bParallel))
2931 {
2933 bool bContinue;
2934 if constexpr (Query == EAABBQueryType::Overlap)
2935 {
2936 bContinue = Visitor.VisitOverlap(VisitData);
2937 }
2938 else
2939 {
2940 bContinue = Query == EAABBQueryType::Sweep ? Visitor.VisitSweep(VisitData, CurData) : Visitor.VisitRaycast(VisitData, CurData);
2941 }
2942
2943 if (!bContinue)
2944 {
2945 return false;
2946 }
2947 }
2948 return true;
2949 };
2950
2951 //QUICK_SCOPE_CYCLE_COUNTER(QueryDirty);
2952 if (DirtyElements.Num() > 0)
2953 {
2954 bool bUseGrid = false;
2955
2956 if (DirtyElementGridEnabled() && CellHashToFlatArray.Num() > 0)
2957 {
2958 if constexpr (Query == EAABBQueryType::Overlap)
2959 {
2960 bUseGrid = !TooManyOverlapQueryCells(QueryBounds, DirtyElementGridCellSizeInv, DirtyElementMaxGridCellQueryCount);
2961 }
2962 else if constexpr (Query == EAABBQueryType::Raycast)
2963 {
2964 bUseGrid = !TooManyRaycastQueryCells(Start, CurData.Dir, CurData.CurrentLength, DirtyElementGridCellSizeInv, DirtyElementMaxGridCellQueryCount);
2965 }
2966 else if constexpr (Query == EAABBQueryType::Sweep)
2967 {
2968 bUseGrid = !TooManySweepQueryCells(QueryHalfExtents, Start, CurData.Dir, CurData.CurrentLength, DirtyElementGridCellSizeInv, DirtyElementMaxGridCellQueryCount);
2969 }
2970 }
2971
2972 if (bUseGrid)
2973 {
2976
2977 auto AddHashEntry = [&](int32 QueryCellHash)
2978 {
2979 const DirtyGridHashEntry* HashEntry = CellHashToFlatArray.Find(QueryCellHash);
2980 if (HashEntry)
2981 {
2983 }
2984 return true;
2985 };
2986
2987 if constexpr (Query == EAABBQueryType::Overlap)
2988 {
2989 DoForOverlappedCells(QueryBounds, DirtyElementGridCellSize, DirtyElementGridCellSizeInv, AddHashEntry);
2990 }
2991 else if constexpr (Query == EAABBQueryType::Raycast)
2992 {
2993 DoForRaycastIntersectCells(Start, CurData.Dir, CurData.CurrentLength, DirtyElementGridCellSize, DirtyElementGridCellSizeInv, AddHashEntry);
2994 }
2995 else if constexpr (Query == EAABBQueryType::Sweep)
2996 {
2997 DoForSweepIntersectCells(QueryHalfExtents, Start, CurData.Dir, CurData.CurrentLength, DirtyElementGridCellSize, DirtyElementGridCellSizeInv,
2998 [&](FReal X, FReal Y)
2999 {
3000 int32 QueryCellHash = HashCoordinates(X, Y, DirtyElementGridCellSizeInv);
3001 const DirtyGridHashEntry* HashEntry = CellHashToFlatArray.Find(QueryCellHash);
3002 if (HashEntry)
3003 {
3004 HashEntryForOverlappedCells.Add(*HashEntry);
3005 }
3006 });
3007 }
3008
3009 if (!DoForHitGridCellsAndOverflow(HashEntryForOverlappedCells, IntersectAndVisit))
3010 {
3011 return false;
3012 }
3013 } // end overlap
3014
3015 else
3016 {
3018 for (const auto& Elem : DirtyElements)
3019 {
3020 if (!IntersectAndVisit(Elem))
3021 {
3022 return false;
3023 }
3024 }
3025 }
3026 }
3027 }
3028
3029 struct FNodeQueueEntry
3030 {
3031 int32 NodeIdx;
3032 FReal TOI;
3033 };
3034
3035 // Caching is for now only available for dynanmic tree
3036 if (bDynamicTree && !OverlappingLeaves.IsEmpty())
3037 {
3038 // For overlap query and dynamic tree we are using the cached overlapping leaves
3039 bool bOverlapResult = true;
3041 {
3042 return bOverlapResult;
3043 }
3044 }
3045
3046 constexpr int32 MaxNodeStackNumOnSystemStack = 255;
3048
3049 if (bDynamicTree)
3050 {
3051
3052 if (RootNode != INDEX_NONE)
3053 {
3054 NodeStack.Emplace(FNodeQueueEntry{RootNode, 0});
3055 }
3056 }
3057 else if (Nodes.Num())
3058 {
3059 NodeStack.Emplace(FNodeQueueEntry{ 0, 0 });
3060 }
3061
3062// Slow debug code
3063//#if !WITH_EDITOR
3064// if (Query == EAABBQueryType::Overlap)
3065// {
3066// CSV_CUSTOM_STAT(ChaosPhysicsTimers, OverlapCount, 1, ECsvCustomStatOp::Accumulate);
3067// CSV_CUSTOM_STAT(ChaosPhysicsTimers, DirtyCount, DirtyElements.Num(), ECsvCustomStatOp::Max);
3068// }
3069//#endif
3070
3071
3073 VectorRegister4Double DirSimd;
3075 VectorRegister4Double InvDirSimd;
3077
3078 if constexpr (Query == EAABBQueryType::Raycast)
3079 {
3081 DirSimd = VectorLoadDouble3(&Dir.X);
3083 InvDirSimd = VectorBitwiseNotAnd(Parallel, VectorDivide(VectorOne(), DirSimd));
3084 LengthSimd = VectorSetDouble1(CurData.CurrentLength);
3085 }
3086
3087 while (NodeStack.Num())
3088 {
3090
3091//#if !WITH_EDITOR
3092// if (Query == EAABBQueryType::Overlap)
3093// {
3094// CSV_CUSTOM_STAT(ChaosPhysicsTimers, AABBCheckCount, 1, ECsvCustomStatOp::Accumulate);
3095// }
3096//#endif
3098 if constexpr (Query != EAABBQueryType::Overlap)
3099 {
3100 if (NodeEntry.TOI > CurData.CurrentLength)
3101 {
3102 continue;
3103 }
3104 }
3105
3106 const FNode& Node = Nodes[NodeEntry.NodeIdx];
3107 if (Node.bLeaf)
3108 {
3110 const auto& Leaf = Leaves[Node.ChildrenNodes[0]];
3111 if constexpr (Query == EAABBQueryType::Overlap)
3112 {
3113 if (Leaf.OverlapFast(QueryBounds, Visitor) == false)
3114 {
3115 return false;
3116 }
3117 }
3118 else if constexpr (Query == EAABBQueryType::Sweep)
3119 {
3120 if (Leaf.SweepFast(Start, CurData, QueryHalfExtents, Visitor, Dir, InvDir, bParallel) == false)
3121 {
3122 return false;
3123 }
3124 }
3125 else if (Leaf.RaycastFastSimd(StartSimd, CurData, Visitor, DirSimd, InvDirSimd, Parallel, LengthSimd) == false)
3126 {
3127 return false;
3128 }
3129 }
3130 else
3131 {
3133 int32 Idx = 0;
3134
3135 if constexpr (Query != EAABBQueryType::Overlap)
3136 {
3138 FReal TOI0, TOI1;
3139 if constexpr (Query == EAABBQueryType::Raycast)
3140 {
3141 FAABBVectorizedDouble AABBVectorized(Node.ChildrenBounds[0]);
3143 bIntersect0 = AABBVectorized.RaycastFast(StartSimd, InvDirSimd, Parallel, LengthSimd, TOISimd);
3145
3146 AABBVectorized = FAABBVectorizedDouble(Node.ChildrenBounds[1]);
3147 bIntersect1 = AABBVectorized.RaycastFast(StartSimd, InvDirSimd, Parallel, LengthSimd, TOISimd);
3149 }
3150 else
3151 {
3152 FAABB3 AABB0(Node.ChildrenBounds[0].Min(), Node.ChildrenBounds[0].Max());
3153 bIntersect0 = TAABBTreeIntersectionHelper<TQueryFastData, Query>::Intersects(Start, CurData, TOI0, AABB0, QueryBounds, QueryHalfExtents, Dir, InvDir, bParallel);
3154
3155 FAABB3 AABB1(Node.ChildrenBounds[1].Min(), Node.ChildrenBounds[1].Max());
3156 bIntersect1 = TAABBTreeIntersectionHelper<TQueryFastData, Query>::Intersects(Start, CurData, TOI1, AABB1, QueryBounds, QueryHalfExtents, Dir, InvDir, bParallel);
3157 }
3158 if (bIntersect0 && bIntersect1)
3159 {
3160 if (TOI1 > TOI0)
3161 {
3162 NodeStack.Emplace(FNodeQueueEntry{Node.ChildrenNodes[1], TOI1});
3163 NodeStack.Emplace(FNodeQueueEntry{Node.ChildrenNodes[0], TOI0});
3164 }
3165 else
3166 {
3167 NodeStack.Emplace(FNodeQueueEntry{Node.ChildrenNodes[0], TOI0});
3168 NodeStack.Emplace(FNodeQueueEntry{ Node.ChildrenNodes[1], TOI1 });
3169 }
3170 }
3171 else if (bIntersect0)
3172 {
3173 NodeStack.Emplace(FNodeQueueEntry{Node.ChildrenNodes[0], TOI0});
3174 }
3175 else if (bIntersect1)
3176 {
3177 NodeStack.Emplace(FNodeQueueEntry{ Node.ChildrenNodes[1], TOI1 });
3178 }
3179 }
3180 else
3181 {
3182 for (const TAABB<T, 3>&AABB : Node.ChildrenBounds)
3183 {
3184 if (TAABBTreeIntersectionHelper<TQueryFastData, Query>::Intersects(Start, CurData, TOI, FAABB3(AABB.Min(), AABB.Max()), QueryBounds, QueryHalfExtents, Dir, InvDir, bParallel))
3185 {
3186 NodeStack.Emplace(FNodeQueueEntry{ Node.ChildrenNodes[Idx], TOI });
3187 }
3188 ++Idx;
3189 }
3190 }
3191 }
3192 }
3193
3194 return true;
3195 }
3196
3197 int32 GetNewWorkSnapshot()
3198 {
3199 if(WorkPoolFreeList.Num())
3200 {
3201 return WorkPoolFreeList.Pop();
3202 }
3203 else
3204 {
3205 return WorkPool.AddDefaulted(1);
3206 }
3207 }
3208
3209 void FreeWorkSnapshot(int32 WorkSnapshotIdx)
3210 {
3211 //Reset for next time they want to use it
3212 WorkPool[WorkSnapshotIdx] = FWorkSnapshot();
3213 WorkPoolFreeList.Add(WorkSnapshotIdx);
3214
3215 }
3216
3217 template <typename TParticles>
3218 void GenerateTree(const TParticles& Particles)
3219 {
3221 this->SetAsyncTimeSlicingComplete(false);
3222
3223 ensure(WorkStack.Num() == 0);
3224
3225 int32 NumParticles = 0;
3226 constexpr bool bIsValidParticleArray = !std::is_same_v<TParticles, std::nullptr_t>;
3227 if constexpr (bIsValidParticleArray)
3228 {
3229 NumParticles = Particles.Num();
3230 }
3231
3232 const int32 ExpectedNumLeaves = NumParticles / MaxChildrenInLeaf;
3234
3235 WorkStack.Reserve(ExpectedNumNodes);
3236
3237 const int32 CurIdx = GetNewWorkSnapshot();
3238 FWorkSnapshot& WorkSnapshot = WorkPool[CurIdx];
3239 WorkSnapshot.Elems.Reserve(NumParticles);
3240
3241
3242 GlobalPayloads.Reset();
3243 Leaves.Reset();
3244 Nodes.Reset();
3245 RootNode = INDEX_NONE;
3246 FirstFreeInternalNode = INDEX_NONE;
3247 FirstFreeLeafNode = INDEX_NONE;
3248 DirtyElements.Reset();
3249 CellHashToFlatArray.Reset();
3250 FlattenedCellArrayOfDirtyIndices.Reset();
3251 DirtyElementsGridOverflow.Reset();
3252 if (DirtyElementTree)
3253 {
3254 DirtyElementTree->Reset();
3255 }
3256
3257 TreeStats.Reset();
3258 TreeExpensiveStats.Reset();
3259 PayloadToInfo.Reset();
3260 NumProcessedThisSlice = 0;
3261
3262 StartSliceTimeStamp = FPlatformTime::Seconds();
3263
3264 GetCVars(); // Safe to copy CVARS here
3265
3266 if (bDynamicTree)
3267 {
3268 int32 Idx = 0;
3269 if constexpr (bIsValidParticleArray)
3270 {
3271 for (auto& Particle : Particles)
3272 {
3273 bool bHasBoundingBox = HasBoundingBox(Particle);
3274 auto Payload = Particle.template GetPayload<TPayloadType>(Idx);
3275 TAABB<T, 3> ElemBounds = ComputeWorldSpaceBoundingBox(Particle, false, (T)0);
3276
3277 UpdateElement(Payload, ElemBounds, bHasBoundingBox);
3278 ++Idx;
3279 }
3280 }
3281 this->SetAsyncTimeSlicingComplete(true);
3282 return;
3283 }
3284
3286
3287 {
3289
3290 // Prepare to find the average and scaled variance of particle centers.
3291 WorkSnapshot.AverageCenter = FVec3(0);
3292 WorkSnapshot.ScaledCenterVariance = FVec3(0);
3293
3294 int32 Idx = 0;
3295
3296 //TODO: we need a better way to time-slice this case since there can be a huge number of Particles. Can't do it right now without making full copy
3298
3299 if constexpr (bIsValidParticleArray)
3300 {
3301 for (auto& Particle : Particles)
3302 {
3303 bool bHasBoundingBox = HasBoundingBox(Particle);
3304 auto Payload = Particle.template GetPayload<TPayloadType>(Idx);
3305 TAABB<T, 3> ElemBounds = ComputeWorldSpaceBoundingBox(Particle, false, (T)0);
3306
3307 // If bounds are bad, use global so we won't screw up splitting computations.
3308 if (bHasBoundingBox && ValidateBounds(ElemBounds) == false)
3309 {
3310 bHasBoundingBox = false;
3311 ensureMsgf(false, TEXT("AABBTree encountered invalid bounds input. Forcing element to global payload. Min: %s Max: %s."),
3312 *ElemBounds.Min().ToString(), *ElemBounds.Max().ToString());
3313 }
3314
3315
3316 if (bHasBoundingBox)
3317 {
3318 if (ElemBounds.Extents().Max() > MaxPayloadBounds)
3319 {
3320 bHasBoundingBox = false;
3321 }
3322 else
3323 {
3324 FReal NumElems = (FReal)(WorkSnapshot.Elems.Add(FElement{ Payload, ElemBounds }) + 1);
3325 WorkSnapshot.Bounds.GrowToInclude(ElemBounds);
3326
3327 // Include the current particle in the average and scaled variance of the particle centers using Welford's method.
3328 TVec3<T> CenterDelta = ElemBounds.Center() - WorkSnapshot.AverageCenter;
3329 WorkSnapshot.AverageCenter += CenterDelta / (T)NumElems;
3330 WorkSnapshot.ScaledCenterVariance += (ElemBounds.Center() - WorkSnapshot.AverageCenter) * CenterDelta;
3331 }
3332 }
3333 else
3334 {
3336 }
3337
3338 if (!bHasBoundingBox)
3339 {
3340 if (bMutable)
3341 {
3342 PayloadToInfo.Add(Payload, FAABBTreePayloadInfo{ GlobalPayloads.Num(), INDEX_NONE, INDEX_NONE, INDEX_NONE });
3343 }
3344 GlobalPayloads.Add(FElement{ Payload, ElemBounds });
3345 }
3346
3347 ++Idx;
3348 //todo: payload info
3349 }
3350 }
3351 }
3352
3353 NumProcessedThisSlice = NumParticles; //todo: give chance to time slice out of next phase
3354
3355 {
3357 WorkSnapshot.NewNodeIdx = 0;
3358 WorkSnapshot.NodeLevel = 0;
3359
3360 //push root onto stack
3361 WorkStack.Add(CurIdx);
3362
3363 SplitNode();
3364 }
3365
3366 /* Helper validation code
3367 int32 Count = 0;
3368 TSet<int32> Seen;
3369 if(WorkStack.Num() == 0)
3370 {
3371 int32 LeafIdx = 0;
3372 for (const auto& Leaf : Leaves)
3373 {
3374 Validate(Seen, Count, Leaf);
3375 bool bHasParent = false;
3376 for (const auto& Node : Nodes)
3377 {
3378 if (Node.bLeaf && Node.ChildrenNodes[0] == LeafIdx)
3379 {
3380 bHasParent = true;
3381
3382 break;
3383 }
3384 }
3385 ensure(bHasParent);
3386 ++LeafIdx;
3387 }
3388 ensure(Count == 0 || Seen.Num() == Count);
3389 ensure(Count == 0 || Count == Particles.Num());
3390 }
3391 */
3392
3393 }
3394
3395 enum eTimeSlicePhase
3396 {
3397 PreFindBestBounds,
3398 DuringFindBestBounds,
3399 ProcessingChildren
3400 };
3401
3402 struct FSplitInfo
3403 {
3404 TAABB<T, 3> RealBounds; //Actual bounds as children are added
3405 int32 WorkSnapshotIdx; //Idx into work snapshot pool
3406 };
3407
3408 struct FWorkSnapshot
3409 {
3410 FWorkSnapshot()
3411 : TimeslicePhase(eTimeSlicePhase::PreFindBestBounds)
3412 {
3413
3414 }
3415
3416 eTimeSlicePhase TimeslicePhase;
3417
3418 TAABB<T, 3> Bounds;
3419 TArray<FElement> Elems;
3420
3421 // The average of the element centers and their variance times the number of elements.
3422 TVec3<T> AverageCenter;
3423 TVec3<T> ScaledCenterVariance;
3424
3425 int32 NodeLevel;
3426 int32 NewNodeIdx;
3427
3428 int32 BestBoundsCurIdx;
3429
3430 FSplitInfo SplitInfos[2];
3431 };
3432
3433 int32 GetLastIndexToProcess(int32 CurrentIndex)
3434 {
3437 {
3438 // When TimeSlicing by a millisecond budget, try to process all nodes.
3439 // We will stop if we ran out of time and continue on the next frame
3440 LastNodeToProcessIndex = WorkPool[CurrentIndex].Elems.Num();
3441 }
3442 else
3443 {
3444 const bool WeAreTimeslicing = (MaxNumToProcess > 0);
3445 const int32 NumWeCanProcess = MaxNumToProcess - NumProcessedThisSlice;
3446 LastNodeToProcessIndex = WeAreTimeslicing ? FMath::Min(WorkPool[CurrentIndex].BestBoundsCurIdx + NumWeCanProcess, WorkPool[CurrentIndex].Elems.Num()) : WorkPool[CurrentIndex].Elems.Num();
3447 }
3448
3450 }
3451
3452 bool CanContinueProcessingNodes(bool bOnlyUseTimeStampCheck = true)
3453 {
3454 bool bCanDoWork = true;
3455
3457 {
3458 bool bCheckIfHasAvailableTime = false;
3459
3461 {
3463 }
3464 else
3465 {
3466 // Checking for platform time every time is expensive, so only do it between a configured amount of elements.
3467 // This means we might overshoot the budget, but on the other hand we will keep most of the time doing actual work.
3468 CurrentProcessedNodesSinceChecked++;
3469 if (CurrentProcessedNodesSinceChecked > FAABBTimeSliceCVars::MinNodesChunkToProcessBetweenTimeChecks)
3470 {
3471 CurrentProcessedNodesSinceChecked = 0;
3473 }
3474 }
3475
3477 {
3478 const double ElapsedTime = FPlatformTime::Seconds() - StartSliceTimeStamp;
3479 const bool WeAreTimeslicing = FAABBTimeSliceCVars::MaxProcessingTimePerSliceSeconds > 0 && MaxNumToProcess > 0;
3481 {
3482 // done enough
3483 bCanDoWork = false;
3484 }
3485 }
3486 }
3487 else
3488 {
3489 const bool WeAreTimeslicing = (MaxNumToProcess > 0);
3490 if (WeAreTimeslicing && (NumProcessedThisSlice >= MaxNumToProcess))
3491 {
3492 // done enough
3493 bCanDoWork = false;
3494 }
3495 }
3496
3497 return bCanDoWork;
3498 }
3499
3500 void FindBestBounds(const int32 StartElemIdx, int32& InOutLastElem, FWorkSnapshot& CurrentSnapshot, int32 MaxAxis, const TVec3<T>& SplitCenter)
3501 {
3502 const T SplitVal = SplitCenter[MaxAxis];
3503
3504 // add all elements to one of the two split infos at this level - root level [ not taking into account the max number allowed or anything
3506 {
3507 const FElement& Elem = CurrentSnapshot.Elems[ElemIdx];
3508 int32 BoxIdx = 0;
3509 const TVec3<T> ElemCenter = Elem.Bounds.Center();
3510
3511 // This was changed to work around a code generation issue on some platforms, don't change it without testing results of ElemCenter computation.
3512 //
3513 // NOTE: This needs review. TVec3<T>::operator[] should now cope with the strict aliasing violation which caused the original codegen breakage.
3514 T CenterVal = ElemCenter[0];
3515 if (MaxAxis == 1)
3516 {
3517 CenterVal = ElemCenter[1];
3518 }
3519 else if(MaxAxis == 2)
3520 {
3521 CenterVal = ElemCenter[2];
3522 }
3523
3524 const int32 MinBoxIdx = CenterVal <= SplitVal ? 0 : 1;
3525
3526 FSplitInfo& SplitInfo = CurrentSnapshot.SplitInfos[MinBoxIdx];
3527 FWorkSnapshot& WorkSnapshot = WorkPool[SplitInfo.WorkSnapshotIdx];
3528 T NumElems = (T)(WorkSnapshot.Elems.Add(Elem) + 1);
3529 SplitInfo.RealBounds.GrowToInclude(Elem.Bounds);
3530
3531 // Include the current particle in the average and scaled variance of the particle centers using Welford's method.
3532 TVec3<T> CenterDelta = ElemCenter - WorkSnapshot.AverageCenter;
3533 WorkSnapshot.AverageCenter += CenterDelta / NumElems;
3534 WorkSnapshot.ScaledCenterVariance += (ElemCenter - WorkSnapshot.AverageCenter) * CenterDelta;
3535
3536 constexpr bool bOnlyUSeTimeStampCheck = false;
3537 if (!CanContinueProcessingNodes(bOnlyUSeTimeStampCheck))
3538 {
3539 // If we ended before processing all the requested nodes, update the out last element index variable
3542 break; // done enough
3543 }
3544 }
3545
3546 NumProcessedThisSlice += InOutLastElem - StartElemIdx;
3547 }
3548
3549 void SplitNode()
3550 {
3551 while (WorkStack.Num())
3552 {
3553 //NOTE: remember to be careful with this since it's a pointer on a tarray
3554 const int32 CurIdx = WorkStack.Last();
3555
3556 if (WorkPool[CurIdx].TimeslicePhase == eTimeSlicePhase::ProcessingChildren)
3557 {
3558 //If we got to this it must be that my children are done, so I'm done as well
3559 WorkStack.Pop(EAllowShrinking::No);
3560 FreeWorkSnapshot(CurIdx);
3561 continue;
3562 }
3563
3564 const int32 NewNodeIdx = WorkPool[CurIdx].NewNodeIdx;
3565
3566 // create the actual node space but might no be filled in (YET) due to time slicing exit
3567 if (NewNodeIdx >= Nodes.Num())
3568 {
3569 Nodes.AddDefaulted((1 + NewNodeIdx) - Nodes.Num());
3570 }
3571
3572 if (!CanContinueProcessingNodes())
3573 {
3574 return; // done enough
3575 }
3576
3577 auto& PayloadToInfoRef = PayloadToInfo;
3578 auto& LeavesRef = Leaves;
3579 auto& NodesRef = Nodes;
3580 auto& WorkPoolRef = WorkPool;
3581 auto& TreeExpensiveStatsRef = TreeExpensiveStats;
3582 auto MakeLeaf = [NewNodeIdx, &PayloadToInfoRef, &WorkPoolRef, CurIdx, &LeavesRef, &NodesRef, &TreeExpensiveStatsRef]()
3583 {
3584 if (bMutable)
3585 {
3586 //todo: does this need time slicing in the case when we have a ton of elements that can't be split?
3587 //hopefully not a real issue
3588 for (const FElement& Elem : WorkPoolRef[CurIdx].Elems)
3589 {
3590 PayloadToInfoRef.Add(Elem.Payload, FAABBTreePayloadInfo{ INDEX_NONE, INDEX_NONE, LeavesRef.Num() });
3591 }
3592 }
3593
3594 NodesRef[NewNodeIdx].bLeaf = true;
3595 NodesRef[NewNodeIdx].ChildrenNodes[0] = LeavesRef.Add(TLeafType{ WorkPoolRef[CurIdx].Elems }); //todo: avoid copy?
3596 };
3597
3598 if (WorkPool[CurIdx].Elems.Num() <= MaxChildrenInLeaf || WorkPool[CurIdx].NodeLevel >= MaxTreeDepth)
3599 {
3600
3601 MakeLeaf();
3602 WorkStack.Pop(EAllowShrinking::No); //finished with this node
3603 FreeWorkSnapshot(CurIdx);
3604 continue;
3605 }
3606
3607 if (WorkPool[CurIdx].TimeslicePhase == eTimeSlicePhase::PreFindBestBounds)
3608 {
3609 //Add two children, remember this invalidates any pointers to current snapshot
3610 const int32 FirstChildIdx = GetNewWorkSnapshot();
3611 const int32 SecondChildIdx = GetNewWorkSnapshot();
3612
3613 //mark idx of children into the work pool
3614 WorkPool[CurIdx].SplitInfos[0].WorkSnapshotIdx = FirstChildIdx;
3615 WorkPool[CurIdx].SplitInfos[1].WorkSnapshotIdx = SecondChildIdx;
3616
3617 for (FSplitInfo& SplitInfo : WorkPool[CurIdx].SplitInfos)
3618 {
3619 SplitInfo.RealBounds = TAABB<T, 3>::EmptyAABB();
3620 }
3621
3622 WorkPool[CurIdx].BestBoundsCurIdx = 0;
3623 WorkPool[CurIdx].TimeslicePhase = eTimeSlicePhase::DuringFindBestBounds;
3624 const int32 ExpectedNumPerChild = WorkPool[CurIdx].Elems.Num() / 2;
3625 {
3626 WorkPool[FirstChildIdx].Elems.Reserve(ExpectedNumPerChild);
3627 WorkPool[SecondChildIdx].Elems.Reserve(ExpectedNumPerChild);
3628
3629 // Initialize the the two info's element center average and scaled variance.
3630 WorkPool[FirstChildIdx].AverageCenter = TVec3<T>(0);
3631 WorkPool[FirstChildIdx].ScaledCenterVariance = TVec3<T>(0);
3632 WorkPool[SecondChildIdx].AverageCenter = TVec3<T>(0);
3633 WorkPool[SecondChildIdx].ScaledCenterVariance = TVec3<T>(0);
3634 }
3635
3636 if (!CanContinueProcessingNodes())
3637 {
3638 // done enough
3639 return;
3640 }
3641 }
3642
3643 if (WorkPool[CurIdx].TimeslicePhase == eTimeSlicePhase::DuringFindBestBounds)
3644 {
3645 int32 LastIdxToProcess = GetLastIndexToProcess(CurIdx);
3646
3647 // Determine the axis to split the AABB on based on the SplitOnVarianceAxis console variable. If it is not 1, simply use the largest axis
3648 // of the work snapshot bounds; otherwise, select the axis with the greatest center variance. Note that the variance times the number of
3649 // elements is actually used but since all that is needed is the axis with the greatest variance the scale factor is irrelevant.
3650 const int32 MaxAxis = (FAABBTreeCVars::SplitOnVarianceAxis != 1) ? WorkPool[CurIdx].Bounds.LargestAxis() :
3651 (WorkPool[CurIdx].ScaledCenterVariance[0] > WorkPool[CurIdx].ScaledCenterVariance[1] ?
3652 (WorkPool[CurIdx].ScaledCenterVariance[0] > WorkPool[CurIdx].ScaledCenterVariance[2] ? 0 : 2) :
3653 (WorkPool[CurIdx].ScaledCenterVariance[1] > WorkPool[CurIdx].ScaledCenterVariance[2] ? 1 : 2));
3654
3655 // Find the point where the AABB will be split based on the SplitAtAverageCenter console variable. If it is not 1, just use the center
3656 // of the AABB; otherwise, use the average of the element centers.
3657 const TVec3<T>& Center = (FAABBTreeCVars::SplitAtAverageCenter != 1) ? WorkPool[CurIdx].Bounds.Center() : WorkPool[CurIdx].AverageCenter;
3658
3659 FindBestBounds(WorkPool[CurIdx].BestBoundsCurIdx, LastIdxToProcess, WorkPool[CurIdx], MaxAxis, Center);
3660 WorkPool[CurIdx].BestBoundsCurIdx = LastIdxToProcess;
3661
3662 if (!CanContinueProcessingNodes())
3663 {
3664 // done enough
3665 return;
3666 }
3667 }
3668
3669 const int32 FirstChildIdx = WorkPool[CurIdx].SplitInfos[0].WorkSnapshotIdx;
3670 const int32 SecondChildIdx = WorkPool[CurIdx].SplitInfos[1].WorkSnapshotIdx;
3671
3672 const bool bChildrenInBothHalves = WorkPool[FirstChildIdx].Elems.Num() && WorkPool[SecondChildIdx].Elems.Num();
3673
3674 // if children in both halves, push them on the stack to continue the split
3676 {
3677 Nodes[NewNodeIdx].bLeaf = false;
3678
3679 Nodes[NewNodeIdx].ChildrenBounds[0] = WorkPool[CurIdx].SplitInfos[0].RealBounds;
3680 WorkPool[FirstChildIdx].Bounds = Nodes[NewNodeIdx].ChildrenBounds[0];
3681 Nodes[NewNodeIdx].ChildrenNodes[0] = Nodes.Num();
3682
3683 Nodes[NewNodeIdx].ChildrenBounds[1] = WorkPool[CurIdx].SplitInfos[1].RealBounds;
3684 WorkPool[SecondChildIdx].Bounds = Nodes[NewNodeIdx].ChildrenBounds[1];
3685 Nodes[NewNodeIdx].ChildrenNodes[1] = Nodes.Num() + 1;
3686
3687 WorkPool[FirstChildIdx].NodeLevel = WorkPool[CurIdx].NodeLevel + 1;
3688 WorkPool[SecondChildIdx].NodeLevel = WorkPool[CurIdx].NodeLevel + 1;
3689
3690 WorkPool[FirstChildIdx].NewNodeIdx = Nodes[NewNodeIdx].ChildrenNodes[0];
3691 WorkPool[SecondChildIdx].NewNodeIdx = Nodes[NewNodeIdx].ChildrenNodes[1];
3692
3693 //push these two new nodes onto the stack
3694 WorkStack.Add(SecondChildIdx);
3695 WorkStack.Add(FirstChildIdx);
3696
3697 // create the actual node so that no one else can use our children node indices
3698 const int32 HighestNodeIdx = Nodes[NewNodeIdx].ChildrenNodes[1];
3699 Nodes.AddDefaulted((1 + HighestNodeIdx) - Nodes.Num());
3700
3701 WorkPool[CurIdx].TimeslicePhase = eTimeSlicePhase::ProcessingChildren;
3702 }
3703 else
3704 {
3705 //couldn't split so just make a leaf - THIS COULD CONTAIN MORE THAN MaxChildrenInLeaf!!!
3706 MakeLeaf();
3707 WorkStack.Pop(EAllowShrinking::No); //we are done with this node
3708 FreeWorkSnapshot(CurIdx);
3709 }
3710 }
3711
3712 check(WorkStack.Num() == 0);
3713 //Stack is empty, clean up pool and mark task as complete
3714
3715 this->SetAsyncTimeSlicingComplete(true);
3716 }
3717
3718 TArray<TPayloadType> FindAllIntersectionsImp(const FAABB3& Intersection) const
3719 {
3720 struct FSimpleVisitor
3721 {
3723 bool VisitOverlap(const TSpatialVisitorData<TPayloadType>& Instance)
3724 {
3725 CollectedResults.Add(Instance.Payload);
3726 return true;
3727 }
3728 bool VisitSweep(const TSpatialVisitorData<TPayloadType>& Instance, FQueryFastData& CurData)
3729 {
3730 check(false);
3731 return true;
3732 }
3733 bool VisitRaycast(const TSpatialVisitorData<TPayloadType>& Instance, FQueryFastData& CurData)
3734 {
3735 check(false);
3736 return true;
3737 }
3738
3739 const void* GetQueryData() const { return nullptr; }
3740 const void* GetSimData() const { return nullptr; }
3741
3743 const void* GetQueryPayload() const
3744 {
3745 return nullptr;
3746 }
3747
3748 bool HasBlockingHit() const
3749 {
3750 return false;
3751 }
3752
3753 bool ShouldIgnore(const TSpatialVisitorData<TPayloadType>& Instance) const { return false; }
3754 TArray<TPayloadType>& CollectedResults;
3755 };
3756
3757 TArray<TPayloadType> Results;
3758 FSimpleVisitor Collector(Results);
3759 Overlap(Intersection, Collector);
3760
3761 return Results;
3762 }
3763
3764 // Set InOutMaxSize to less than 0 for unlimited
3765 //
3766 template<typename ContainerType>
3767 static void AddToContainerHelper(const ContainerType& ContainerFrom, ContainerType& ContainerTo, int32 Index)
3768 {
3769 if constexpr (std::is_same_v<ContainerType, TArrayAsMap<TPayloadType, FAABBTreePayloadInfo>>)
3770 {
3772 }
3773 else if constexpr(std::is_same_v<ContainerType, TSQMap<TPayloadType, FAABBTreePayloadInfo>>)
3774 {
3776 }
3777 else
3778 {
3780 }
3781 }
3782
3783 template<typename ContainerType>
3784 static int32 ContainerElementSizeHelper(const ContainerType& Container, int32 Index)
3785 {
3786 if constexpr (std::is_same_v<ContainerType, TArray<TAABBTreeLeafArray<TPayloadType, true>>>)
3787 {
3788 return sizeof(typename TArray<TAABBTreeLeafArray<TPayloadType, true>>::ElementType) + sizeof(typename decltype(Container[Index].Elems)::ElementType) * Container[Index].GetElementCount();
3789 }
3790 else if constexpr (std::is_same_v<ContainerType, TArray<TAABBTreeLeafArray<TPayloadType, false>>>)
3791 {
3792 return sizeof(typename TArray<TAABBTreeLeafArray<TPayloadType, false>>::ElementType) + sizeof(typename decltype(Container[Index].Elems)::ElementType) * Container[Index].GetElementCount();
3793 }
3794 else
3795 {
3796 return sizeof(typename ContainerType::ElementType);
3797 }
3798 }
3799
3800 template<typename ContainerType, typename TCanContinueCallback>
3801 static bool ContinueTimeSliceCopy(const ContainerType& ContainerFrom, ContainerType& ContainerTo, int32& InOutMaxSize, const TCanContinueCallback& CanContinueCallback)
3802 {
3803 int32 SizeCopied = 0;
3804
3806 {
3807 AddToContainerHelper(ContainerFrom, ContainerTo, Index);
3808 SizeCopied += ContainerElementSizeHelper(ContainerFrom, Index);
3809 }
3810
3811 // Update the maximum size left
3812 if (InOutMaxSize > 0)
3813 {
3815 {
3816 InOutMaxSize = 0;
3817 }
3818 else
3819 {
3821 }
3822 }
3823
3824 bool Done = ContainerTo.Num() == ContainerFrom.Num();
3825 return Done;
3826 }
3827
3828
3829#if !UE_BUILD_SHIPPING
3830 virtual void DebugDraw(ISpacialDebugDrawInterface<T>* InInterface) const override
3831 {
3832 if (InInterface)
3833 {
3834 int32 RootNodeIndex = bDynamicTree ? RootNode : 0;
3835 if (Nodes.Num() > 0 && Nodes.IsValidIndex(RootNodeIndex))
3836 {
3837 Nodes[RootNodeIndex].DebugDraw(*InInterface, Nodes, { 1.f, 1.f, 1.f }, 5.f);
3838 }
3839 for (int LeafIndex = 0; LeafIndex < Leaves.Num(); LeafIndex++)
3840 {
3841 const TLeafType& Leaf = Leaves[LeafIndex];
3842 Leaf.DebugDrawLeaf(*InInterface, FLinearColor::MakeRandomColor(), 10.f);
3843 }
3844 }
3845 }
3846#endif
3847
3848
3849 TAABBTree(const TAABBTree& Other)
3850 : ISpatialAcceleration<TPayloadType, T, 3>(StaticType)
3851 , Nodes(Other.Nodes)
3853 , DirtyElements(Other.DirtyElements)
3854 , bDynamicTree(Other.bDynamicTree)
3855 , RootNode(Other.RootNode)
3856 , FirstFreeInternalNode(Other.FirstFreeInternalNode)
3857 , FirstFreeLeafNode(Other.FirstFreeLeafNode)
3858 , CellHashToFlatArray(Other.CellHashToFlatArray)
3859 , FlattenedCellArrayOfDirtyIndices(Other.FlattenedCellArrayOfDirtyIndices)
3860 , DirtyElementsGridOverflow(Other.DirtyElementsGridOverflow)
3861 , DirtyElementTree(nullptr)
3862 , DirtyElementGridCellSize(Other.DirtyElementGridCellSize)
3863 , DirtyElementGridCellSizeInv(Other.DirtyElementGridCellSizeInv)
3864 , DirtyElementMaxGridCellQueryCount(Other.DirtyElementMaxGridCellQueryCount)
3865 , DirtyElementMaxPhysicalSizeInCells(Other.DirtyElementMaxPhysicalSizeInCells)
3866 , DirtyElementMaxCellCapacity(Other.DirtyElementMaxCellCapacity)
3867 , TreeStats(Other.TreeStats)
3868 , TreeExpensiveStats(Other.TreeExpensiveStats)
3869 , GlobalPayloads(Other.GlobalPayloads)
3870 , PayloadToInfo(Other.PayloadToInfo)
3871 , MaxChildrenInLeaf(Other.MaxChildrenInLeaf)
3872 , MaxTreeDepth(Other.MaxTreeDepth)
3873 , MaxPayloadBounds(Other.MaxPayloadBounds)
3874 , MaxNumToProcess(Other.MaxNumToProcess)
3875 , NumProcessedThisSlice(Other.NumProcessedThisSlice)
3876 , StartSliceTimeStamp(Other.StartSliceTimeStamp)
3877 , bModifyingTreeMultiThreadingFastCheck(Other.bModifyingTreeMultiThreadingFastCheck)
3878 , bShouldRebuild(Other.bShouldRebuild)
3879 , bBuildOverlapCache(Other.bBuildOverlapCache)
3880 , OverlappingLeaves(Other.OverlappingLeaves)
3881 , OverlappingOffsets(Other.OverlappingOffsets)
3882 , OverlappingPairs(Other.OverlappingPairs)
3883 , OverlappingCounts(Other.OverlappingCounts)
3884 {
3885 ensure(bDynamicTree == Other.IsTreeDynamic());
3886 PriorityQ.Reserve(32);
3887 if (Other.DirtyElementTree)
3888 {
3889 DirtyElementTree = TUniquePtr<TAABBTree>(new TAABBTree(*(Other.DirtyElementTree)));
3890 }
3891 }
3892
3893 virtual void DeepAssign(const ISpatialAcceleration<TPayloadType, T, 3>& Other) override
3894 {
3895 check(Other.GetType() == ESpatialAcceleration::AABBTree);
3896 *this = static_cast<const TAABBTree&>(Other);
3897 }
3898
3899 TAABBTree& operator=(const TAABBTree& Rhs)
3900 {
3901 ISpatialAcceleration<TPayloadType, T, 3>::DeepAssign(Rhs);
3902 ensure(Rhs.WorkStack.Num() == 0);
3903 //ensure(Rhs.WorkPool.Num() == 0);
3904 //ensure(Rhs.WorkPoolFreeList.Num() == 0);
3905 WorkStack.Empty();
3906 WorkPool.Empty();
3907 WorkPoolFreeList.Empty();
3908 if(this != &Rhs)
3909 {
3910 Nodes = Rhs.Nodes;
3911 Leaves = Rhs.Leaves;
3912 DirtyElements = Rhs.DirtyElements;
3913 bDynamicTree = Rhs.bDynamicTree;
3914 RootNode = Rhs.RootNode;
3915 FirstFreeInternalNode = Rhs.FirstFreeInternalNode;
3916 FirstFreeLeafNode = Rhs.FirstFreeLeafNode;
3917
3918 CellHashToFlatArray = Rhs.CellHashToFlatArray;
3919 FlattenedCellArrayOfDirtyIndices = Rhs.FlattenedCellArrayOfDirtyIndices;
3920 DirtyElementsGridOverflow = Rhs.DirtyElementsGridOverflow;
3921 TreeStats = Rhs.TreeStats;
3922 TreeExpensiveStats = Rhs.TreeExpensiveStats;
3923
3924 DirtyElementGridCellSize = Rhs.DirtyElementGridCellSize;
3925 DirtyElementGridCellSizeInv = Rhs.DirtyElementGridCellSizeInv;
3926 DirtyElementMaxGridCellQueryCount = Rhs.DirtyElementMaxGridCellQueryCount;
3927 DirtyElementMaxPhysicalSizeInCells = Rhs.DirtyElementMaxPhysicalSizeInCells;
3928 DirtyElementMaxCellCapacity = Rhs.DirtyElementMaxCellCapacity;
3929
3930 GlobalPayloads = Rhs.GlobalPayloads;
3931 PayloadToInfo = Rhs.PayloadToInfo;
3932 MaxChildrenInLeaf = Rhs.MaxChildrenInLeaf;
3933 MaxTreeDepth = Rhs.MaxTreeDepth;
3934 MaxPayloadBounds = Rhs.MaxPayloadBounds;
3935 MaxNumToProcess = Rhs.MaxNumToProcess;
3936 NumProcessedThisSlice = Rhs.NumProcessedThisSlice;
3937 StartSliceTimeStamp = Rhs.StartSliceTimeStamp;
3938 bModifyingTreeMultiThreadingFastCheck = Rhs.bModifyingTreeMultiThreadingFastCheck;
3939 bShouldRebuild = Rhs.bShouldRebuild;
3940 bBuildOverlapCache = Rhs.bBuildOverlapCache;
3941 if (Rhs.DirtyElementTree)
3942 {
3943 if (!DirtyElementTree)
3944 {
3945 DirtyElementTree = TUniquePtr<TAABBTree>(new TAABBTree());
3946 }
3947 *DirtyElementTree = *Rhs.DirtyElementTree;
3948 }
3949 else
3950 {
3951 DirtyElementTree = nullptr;
3952 }
3953 }
3954
3955 return *this;
3956 }
3957
3958 TArray<FNode> Nodes;
3959 TLeafContainer<TLeafType> Leaves;
3960 TArray<FElement> DirtyElements;
3961
3962 // DynamicTree members
3963 bool bDynamicTree = false;
3964 int32 RootNode = INDEX_NONE;
3965 // Free lists (indices are in Nodes array)
3966 int32 FirstFreeInternalNode = INDEX_NONE;
3967 int32 FirstFreeLeafNode = INDEX_NONE;
3968
3969 // Data needed for DirtyElement2DAccelerationGrid
3970 TMap<int32, DirtyGridHashEntry> CellHashToFlatArray; // Index, size into flat grid structure (FlattenedCellArrayOfDirtyIndices)
3971 TArray<int32> FlattenedCellArrayOfDirtyIndices; // Array of indices into dirty Elements array, indices are always sorted within a 2D cell
3972 TArray<int32> DirtyElementsGridOverflow; // Array of indices of DirtyElements that is not in the grid for some reason
3973
3974 // Members for using a dynamic tree as a dirty element acceleration structure
3975 TUniquePtr<TAABBTree> DirtyElementTree;
3976
3977 // Copy of CVARS
3978 T DirtyElementGridCellSize;
3979 T DirtyElementGridCellSizeInv;
3980 int32 DirtyElementMaxGridCellQueryCount;
3981 int32 DirtyElementMaxPhysicalSizeInCells;
3982 int32 DirtyElementMaxCellCapacity;
3983 // Some useful statistics
3984 AABBTreeStatistics TreeStats;
3985 AABBTreeExpensiveStatistics TreeExpensiveStats;
3986
3987 TArray<FElement> GlobalPayloads;
3988 typename StorageTraits::PayloadToInfoType PayloadToInfo;
3989
3990 int32 MaxChildrenInLeaf;
3991 int32 MaxTreeDepth;
3992 T MaxPayloadBounds;
3993 int32 MaxNumToProcess;
3994 int32 NumProcessedThisSlice;
3995
3996 double StartSliceTimeStamp = 0.0;
3997 int32 CurrentProcessedNodesSinceChecked = 0;
3998 int32 CurrentDataElementsCopiedSinceLastCheck = 0;
3999
4000 TArray<int32> WorkStack;
4001 TArray<int32> WorkPoolFreeList;
4002 TArray<FWorkSnapshot> WorkPool;
4003
4004 bool bModifyingTreeMultiThreadingFastCheck; // Used for fast but not perfect multithreading sanity check
4005
4006 bool bShouldRebuild; // Contract: this can only ever be cleared by calling the ClearShouldRebuild method
4007
4008 bool bBuildOverlapCache;
4010 TArray<int32> OverlappingLeaves;
4011
4013 TArray<int32> OverlappingOffsets;
4014
4016 TArray<FIntVector2> OverlappingPairs;
4017
4019 TArray<int32> OverlappingCounts;
4020
4025 using FNodeIndexAndCost = TTuple<FNode&, int32, FReal>;
4026 TArray<FNodeIndexAndCost> PriorityQ;
4027
4028 friend ::FChaosVDDataWrapperUtils;
4029};
4030
4031template<typename TPayloadType, typename TLeafType, bool bMutable, typename T, typename StorageTraits>
4037
4038}
#define FORCEINLINE
Definition AndroidPlatform.h:140
#define check(expr)
Definition AssertionMacros.h:314
#define ensureMsgf( InExpression, InFormat,...)
Definition AssertionMacros.h:465
#define ensure( InExpression)
Definition AssertionMacros.h:464
#define PHYSICS_CSV_CUSTOM_VERY_EXPENSIVE(Category, Name, Value, Op)
Definition ChaosStats.h:164
#define PHYSICS_CSV_SCOPED_VERY_EXPENSIVE(Category, Name)
Definition ChaosStats.h:163
#define GLog
Definition CoreGlobals.h:95
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#define FORCEINLINE_DEBUGGABLE
Definition CoreMiscDefines.h:74
#define TEXT(x)
Definition Platform.h:1272
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
#define RESTRICT
Definition Platform.h:706
#define UNLIKELY(x)
Definition Platform.h:857
FPlatformTypes::uint64 uint64
A 64-bit unsigned integer.
Definition Platform.h:1117
#define DECLARE_CYCLE_STAT(CounterName, StatId, GroupId)
Definition Stats.h:669
#define SCOPE_CYCLE_COUNTER(Stat)
Definition Stats.h:650
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define TRACE_CPUPROFILER_EVENT_SCOPE(Name)
Definition CpuProfilerTrace.h:528
#define CSV_DECLARE_CATEGORY_EXTERN(CategoryName)
Definition CsvProfiler.h:79
#define CSV_CUSTOM_STAT(Category, StatName, Value, Op)
Definition CsvProfiler.h:160
FArchive & operator<<(FArchive &Ar, FEnvQueryDebugProfileData::FStep &Data)
Definition EnvQueryTypes.cpp:489
return true
Definition ExternalRpcRegistry.cpp:601
#define X(Name, Desc)
Definition FormatStringSan.h:47
#define UE_LOG(CategoryName, Verbosity, Format,...)
Definition LogMacros.h:270
UE::Math::TIntVector2< int32 > FIntVector2
Definition MathFwd.h:91
@ Num
Definition MetalRHIPrivate.h:234
USkinnedMeshComponent float
Definition SkinnedMeshComponent.h:60
FORCEINLINE VectorRegister4Float VectorDivide(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:834
FORCEINLINE VectorRegister4Float VectorCompareGT(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:974
FORCEINLINE VectorRegister4Double MakeVectorRegisterDouble(uint64 X, uint64 Y, uint64 Z, uint64 W)
Definition UnrealMathFPU.h:185
FORCEINLINE int32 VectorMaskBits(const VectorRegister4Float &Vec1)
Definition UnrealMathFPU.h:1075
FORCEINLINE VectorRegister4Float VectorAbs(const VectorRegister4Float &Vec)
Definition UnrealMathFPU.h:661
FORCEINLINE VectorRegister4Float VectorBitwiseOr(const VectorRegister4Float &Vec1, const VectorRegister4Float &Vec2)
Definition UnrealMathFPU.h:1140
#define UE_SMALL_NUMBER
Definition UnrealMathUtility.h:130
FORCEINLINE void VectorStoreDouble1(VectorRegister4Double Vec, double *Ptr)
Definition UnrealMathVectorCommon.h.inl:830
FORCEINLINE VectorRegister4Float VectorOne(void)
Definition UnrealMathVectorCommon.h.inl:21
FORCEINLINE VectorRegister4Double VectorSetDouble1(double D)
Definition UnrealMathVectorCommon.h.inl:820
FORCEINLINE VectorRegister4Double VectorLoadDouble3(const double *Ptr)
Definition UnrealMathVectorCommon.h.inl:113
FORCEINLINE VectorRegister4Float VectorBitwiseNotAnd(const VectorRegister4Float &A, const VectorRegister4Float &B)
Definition VectorUtility.h:289
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition AABBVectorizedDouble.h:15
Definition ParticleHandle.h:213
Definition ChaosArchive.h:167
Definition ISpatialAcceleration.h:166
Definition ISpatialAcceleration.h:267
Definition ISpatialAcceleration.h:120
Definition AABBTree.h:786
void DumpStatsTo(FOutputDevice &Ar) const override
Definition AABBTree.h:1308
virtual bool IsTreeDynamic() const override
Definition AABBTree.h:2182
FORCEINLINE_DEBUGGABLE bool DirtyElementGridEnabled() const
Definition AABBTree.h:1052
void DeAllocateInternalNode(int32 NodeIdx)
Definition AABBTree.h:1404
virtual ~TAABBTree()
Definition AABBTree.h:964
void RemoveLeafNode(int32 LeafNodeIdx, const TPayloadType &Payload)
Definition AABBTree.h:1741
void GetCVars()
Definition AABBTree.h:1035
TAABBTree(EmptyInit, int32 InMaxChildrenInLeaf=DefaultMaxChildrenInLeaf, int32 InMaxTreeDepth=DefaultMaxTreeDepth, T InMaxPayloadBounds=DefaultMaxPayloadBounds, int32 InMaxNumToProcess=DefaultMaxNumToProcess, bool bInDynamicTree=false, bool bInUseDirtyTree=false, bool bInBuildOverlapCache=true)
Definition AABBTree.h:913
const int32 GetSubtreeDepth(const int32 NodeIdx)
Definition AABBTree.h:2159
int32 NumDirtyElements() const
Definition AABBTree.h:2127
int32 FindBestSibling(const TAABB< T, 3 > &InNewBounds, bool &bOutAddToLeaf)
Definition AABBTree.h:1454
const AABBTreeStatistics & GetAABBTreeStatistics()
Definition AABBTree.h:2133
virtual void ProgressAsyncTimeSlicing(bool ForceBuildCompletion) override
Definition AABBTree.h:861
FORCEINLINE_DEBUGGABLE bool DeleteDirtyParticleIndexFromGridCell(int32 Hash, int32 DirtyIndex)
Definition AABBTree.h:1104
FORCEINLINE_DEBUGGABLE int32 UpdateDirtyElementInGrid(const TAABB< T, 3 > &NewBounds, int32 DirtyElementIndex, int32 DirtyGridOverflowIdx)
Definition AABBTree.h:1207
virtual TUniquePtr< ISpatialAcceleration< TPayloadType, T, 3 > > Copy() const override
Definition AABBTree.h:971
FORCEINLINE_DEBUGGABLE bool ValidateBounds(const TAABB< T, 3 > &Bounds)
Definition AABBTree.h:2366
FORCEINLINE_DEBUGGABLE int32 AddDirtyElementToGrid(const TAABB< T, 3 > &NewBounds, int32 NewDirtyElement)
Definition AABBTree.h:1171
FORCEINLINE_DEBUGGABLE bool AddNewDirtyParticleIndexToGridCell(int32 Hash, int32 NewDirtyIndex)
Definition AABBTree.h:1076
void Sweep(const FVec3 &Start, const FVec3 &Dir, const FReal Length, const FVec3 QueryHalfExtents, ISpatialVisitor< TPayloadType, FReal > &Visitor) const override
Definition AABBTree.h:995
void ComputeOverlappingCacheFromLeaf()
Definition AABBTree.h:2661
void Sweep(const FVec3 &Start, const FVec3 &Dir, const FReal Length, const FVec3 QueryHalfExtents, SQVisitor &Visitor) const
Definition AABBTree.h:1002
virtual void CacheOverlappingLeaves() override
Definition AABBTree.h:2680
FORCEINLINE_DEBUGGABLE void DeleteDirtyParticleEverywhere(int32 DeleteDirtyParticleIdx, int32 DeleteDirtyGridOverflowIdx)
Definition AABBTree.h:1119
const TArray< TPayloadBoundsElement< TPayloadType, T > > & GlobalObjects() const
Definition AABBTree.h:2172
virtual void PrepareCopyTimeSliced(const ISpatialAcceleration< TPayloadType, T, 3 > &InFrom) override
Definition AABBTree.h:2185
TPayloadType PayloadType
Definition AABBTree.h:791
bool SweepFast(const FVec3 &Start, FQueryFastData &CurData, const FVec3 QueryHalfExtents, SQVisitor &Visitor, const FVec3 &Dir, const FVec3 InvDir, const bool bParallel[3]) const
Definition AABBTree.h:1009
NodeAndLeafIndices AllocateLeafNodeAndLeaf(const TPayloadType &Payload, const TAABB< T, 3 > &NewBounds)
Definition AABBTree.h:1363
bool RaycastFast(const FVec3 &Start, FQueryFastData &CurData, SQVisitor &Visitor, const FVec3 &Dir, const FVec3 InvDir, const bool bParallel[3]) const
Definition AABBTree.h:990
virtual void ProgressCopyTimeSliced(const ISpatialAcceleration< TPayloadType, T, 3 > &InFrom, int MaximumBytesToCopy) override
Definition AABBTree.h:2248
void Reinitialize(const ParticleView &Particles, int32 InMaxChildrenInLeaf=DefaultMaxChildrenInLeaf, int32 InMaxTreeDepth=DefaultMaxTreeDepth, T InMaxPayloadBounds=DefaultMaxPayloadBounds, int32 InMaxNumToProcess=DefaultMaxNumToProcess, bool bInDynamicTree=false, bool bInbBuildOverlapCache=true)
Definition AABBTree.h:934
FORCEINLINE_DEBUGGABLE bool EnoughSpaceInGridCell(int32 Hash)
Definition AABBTree.h:1060
void Overlap(const FAABB3 &QueryBounds, SQVisitor &Visitor) const
Definition AABBTree.h:1021
virtual bool ShouldRebuild() override
Definition AABBTree.h:2178
void CopyFrom(const TAABBTree &Other)
Definition AABBTree.h:966
void Raycast(const FVec3 &Start, const FVec3 &Dir, const FReal Length, SQVisitor &Visitor) const
Definition AABBTree.h:983
void AddRootOverlappingLeaves(const TAABBTreeNode< T > &TreeNode, const bool bDirtyFilter)
Definition AABBTree.h:2538
const TArray< TAABBTreeNode< T > > & GetNodes() const
Definition AABBTree.h:2720
const TArray< TLeafType > & GetLeaves() const
Definition AABBTree.h:2721
void RotateNode(uint32 NodeIdx, bool debugAssert=false)
Definition AABBTree.h:1543
void PropagateLeavesDirtyFlag()
Definition AABBTree.h:2583
TAABBTree(const TParticles &Particles, int32 InMaxChildrenInLeaf=DefaultMaxChildrenInLeaf, int32 InMaxTreeDepth=DefaultMaxTreeDepth, T InMaxPayloadBounds=DefaultMaxPayloadBounds, int32 InMaxNumToProcess=DefaultMaxNumToProcess, bool bInDynamicTree=false, bool bInUseDirtyTree=false, bool bInBuildOverlapCache=true)
Definition AABBTree.h:888
int32 GetSiblingIndex(int32 NodeIdx)
Definition AABBTree.h:1448
void ComputeOverlappingCacheFromRoot(const bool bDirtyFilter)
Definition AABBTree.h:2604
int32 AllocateInternalNode()
Definition AABBTree.h:1335
NodeAndLeafIndices InsertLeaf(const TPayloadType &Payload, const TAABB< T, 3 > &NewBounds)
Definition AABBTree.h:1626
const AABBTreeExpensiveStatistics & GetAABBTreeExpensiveStatistics()
Definition AABBTree.h:2141
void DynamicTreeDebugStats() const
Definition AABBTree.h:1287
T TType
Definition AABBTree.h:793
virtual bool UpdateElement(const TPayloadType &Payload, const TAABB< T, 3 > &NewBounds, bool bInHasBounds) override
Definition AABBTree.h:1889
virtual void ClearShouldRebuild() override
Definition AABBTree.h:2180
virtual bool NeedUpdateElement(const TPayloadType &Payload, const TAABB< T, 3 > &NewBounds) override
Definition AABBTree.h:1867
void DeAllocateLeafNode(int32 NodeIdx)
Definition AABBTree.h:1414
void AddNodesOverlappingLeaves(const TAABBTreeNode< T > &LeftNode, const TAABB< T, 3 > &LeftBounds, const TAABBTreeNode< T > &RightNode, const TAABB< T, 3 > &RightBounds, const bool bDirtyFilter)
Definition AABBTree.h:2496
TAABBTree()
Definition AABBTree.h:800
void FillPersistentOverlappingPairs()
Definition AABBTree.h:2553
void Overlap(const FAABB3 &QueryBounds, ISpatialVisitor< TPayloadType, FReal > &Visitor) const override
Definition AABBTree.h:1014
int32 WhichChildAmI(int32 NodeIdx)
Definition AABBTree.h:1428
void SetTreeToDynamic()
Definition AABBTree.h:2183
virtual void Raycast(const FVec3 &Start, const FVec3 &Dir, const FReal Length, ISpatialVisitor< TPayloadType, FReal > &Visitor) const override
Definition AABBTree.h:976
void UpdateAncestorBounds(int32 NodeIdx, bool bDoRotation=false)
Definition AABBTree.h:1705
virtual bool RemoveElement(const TPayloadType &Payload) override
Definition AABBTree.h:1800
virtual void Reset() override
Definition AABBTree.h:819
void PrintOverlappingLeaves()
Definition AABBTree.h:2703
bool GetAsBoundsArray(TArray< TAABB< T, 3 > > &AllBounds, int32 NodeIdx, int32 ParentNode, TAABB< T, 3 > &Bounds)
Definition AABBTree.h:949
bool OverlapFast(const FAABB3 &QueryBounds, SQVisitor &Visitor) const
Definition AABBTree.h:1027
virtual TArray< TPayloadType > FindAllIntersections(const FAABB3 &Box) const override
Definition AABBTree.h:947
void FindOverlappingLeaf(const int32 FirstNode, const int32 LeafIndex, TArray< int32 > &NodeStack)
Definition AABBTree.h:2460
void DumpStats() const override
Definition AABBTree.h:1300
virtual void Serialize(FChaosArchive &Ar) override
Definition AABBTree.h:2392
FElementsCollection DebugGetElementsCollection() const
Definition AABBTree.h:1245
FORCEINLINE const TVector< T, d > & Max() const
Definition AABB.h:596
FORCEINLINE bool RaycastFast(const TVector< FReal, d > &StartPoint, const TVector< FReal, d > &Dir, const TVector< FReal, d > &InvDir, const bool *bParallel, const FReal Length, FReal &OutEntryTime, FReal &OutExitTime) const
Definition AABB.h:217
FORCEINLINE TAABB< T, d > & Thicken(const T Thickness)
Definition AABB.h:411
FORCEINLINE void GrowToInclude(const TVector< T, d > &V)
Definition AABB.h:393
static FORCEINLINE TAABB< T, d > EmptyAABB()
Definition AABB.h:623
FORCEINLINE const TVector< T, d > & Min() const
Definition AABB.h:595
Definition ISpatialAcceleration.h:509
Definition Box.h:23
Definition ParticleHandle.h:436
Definition ParticleHandle.h:2739
const FLeafType & operator[](SizeType Index) const
Definition AABBTree.h:594
Definition AABBTree.h:566
void Serialize(FChaosArchive &Ar)
Definition AABBTree.h:568
Definition Particles.h:32
Definition ISpatialAcceleration.h:453
Definition Vector.h:1000
Definition Archive.h:1208
virtual void Serialize(void *V, int64 Length)
Definition Archive.h:1689
virtual CORE_API void UsingCustomVersion(const struct FGuid &Guid)
Definition Archive.cpp:590
UE_FORCEINLINE_HINT bool IsLoading() const
Definition Archive.h:236
CORE_API int32 CustomVer(const struct FGuid &Key) const
Definition Archive.cpp:602
Definition IConsoleManager.h:1580
Definition ChaosVDDataWrapperUtils.h:77
Definition OutputDevice.h:133
void Logf(const FmtType &Fmt)
Definition OutputDevice.h:234
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
void Reset(SizeType NewSize=0)
Definition Array.h:2246
InElementType ElementType
Definition Array.h:676
typename InAllocatorType::SizeType SizeType
Definition Array.h:675
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 Append(const TArray< OtherElementType, OtherAllocatorType > &Source)
Definition Array.h:2412
UE_NODEBUG UE_FORCEINLINE_HINT bool Find(const ElementType &Item, SizeType &Index) const
Definition Array.h:1302
ElementType Pop(EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:1196
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition UnrealString.h.inl:34
Definition SharedPointer.h:692
UE_FORCEINLINE_HINT const bool IsValid() const
Definition SharedPointer.h:1085
Definition UniquePtr.h:107
@ HasBoundingBox
Definition ImplicitObjectType.h:68
Definition SkeletalMeshComponent.h:307
FORCEINLINE_DEBUGGABLE bool TooManyRaycastQueryCells(const FVec3 &StartPoint, const FVec3 &Dir, const FReal Length, FReal DirtyElementGridCellSizeInv, int32 DirtyElementMaxGridCellQueryCount)
Definition AABBTreeDirtyGridUtils.h:483
FORCEINLINE bool IsInPhysicsThreadContext()
Definition Threading.h:280
FORCEINLINE_DEBUGGABLE bool DoForOverlappedCells(const TAABB< T, 3 > &AABB, FReal DirtyElementGridCellSize, FReal DirtyElementGridCellSizeInv, FunctionType Function)
Definition AABBTreeDirtyGridUtils.h:69
ESpatialAcceleration
Definition ISpatialAcceleration.h:180
TAABB< T, d > ComputeWorldSpaceBoundingBox(const TParticles< T, d > &Objects, const int32 i, bool bUseVelocity=false, T Dt=0)
Definition BoundingVolumeUtilities.h:167
FRealDouble FReal
Definition Real.h:22
FORCEINLINE_DEBUGGABLE bool DeleteValueFromSortedSubArray(TArray< int32 > &Array, int32 Value, int32 StartIndex, int32 Count)
Definition AABBTreeDirtyGridUtils.h:223
CHAOS_API int32 MaxDirtyElements
Definition BoundingVolume.cpp:21
FORCEINLINE_DEBUGGABLE void DoForRaycastIntersectCells(const FVec3 &StartPoint, const FVec3 &Dir, FReal Length, FReal DirtyElementGridCellSize, FReal DirtyElementGridCellSizeInv, FunctionType InFunction)
Definition AABBTreeDirtyGridUtils.h:516
FORCEINLINE_DEBUGGABLE bool TooManySweepQueryCells(const TVec3< FReal > &QueryHalfExtents, const FVec3 &StartPoint, const FVec3 &Dir, FReal Length, FReal DirtyElementGridCellSizeInv, int32 DirtyElementMaxGridCellQueryCount)
Definition AABBTreeDirtyGridUtils.h:241
bool PrePreFilterHelper(const FAccelerationStructureHandle &Payload, const Private::FSimOverlapVisitor &Visitor)
Definition SpatialAccelerationBroadPhase.h:323
@ Raycast
Definition SimulationModuleBase.h:145
TVector< FReal, 3 > FVec3
Definition Core.h:17
FORCEINLINE_DEBUGGABLE bool TooManyOverlapQueryCells(const TAABB< T, 3 > &AABB, FReal DirtyElementGridCellSizeInv, int32 MaximumOverlap)
Definition AABBTreeDirtyGridUtils.h:46
FORCEINLINE_DEBUGGABLE bool InsertValueIntoSortedSubArray(TArray< int32 > &Array, int32 Value, int32 StartIndex, int32 Count)
Definition AABBTreeDirtyGridUtils.h:192
FORCEINLINE_DEBUGGABLE bool DoForOverlappedCellsExclude(const TAABB< T, 3 > &AABB, const TAABB< T, 3 > &AABBExclude, FReal DirtyElementGridCellSize, FReal DirtyElementGridCellSizeInv, FunctionType Function)
Definition AABBTreeDirtyGridUtils.h:92
TAABB< FReal, 3 > FAABB3
Definition ImplicitObject.h:34
EAABBQueryType
Definition AABBTree.h:84
void DoForSweepIntersectCells(const FVec3 QueryHalfExtents, const FVec3 &StartPoint, const FVec3 &Dir, FReal Length, FReal DirtyElementGridCellSize, FReal DirtyElementGridCellSizeInv, FunctionType InFunction)
Definition AABBTreeDirtyGridUtils.h:442
@ Log
Definition LogVerbosity.h:40
@ NodeEntry
Definition Script.h:356
@ Visitor
Definition XmppMultiUserChat.h:94
constexpr VectorRegister4Double DoubleSmallNumber
Definition UnrealMathVectorConstants.h.inl:71
Chaos::FReal FReal
Definition ImmediatePhysicsCore_Chaos.h:33
@ Start
Definition GeoEnum.h:100
FORCEINLINE UE_STRING_CLASS RhsType && Rhs
Definition String.cpp.inl:718
@ false
Definition radaudio_common.h:23
U16 Index
Definition radfft.cpp:71
Definition AABBTree.h:116
int32 StatMaxDirtyElements
Definition AABBTree.h:136
int32 StatMaxLeafSize
Definition AABBTree.h:137
int32 StatMaxTreeDepth
Definition AABBTree.h:138
AABBTreeExpensiveStatistics & MergeStatistics(const AABBTreeExpensiveStatistics &Rhs)
Definition AABBTree.h:126
void Reset()
Definition AABBTree.h:117
int32 StatGlobalPayloadsSize
Definition AABBTree.h:139
int32 StatMaxNumLeaves
Definition AABBTree.h:135
Definition AABBTree.h:91
int32 StatNumElementsTooLargeForGrid
Definition AABBTree.h:110
void Reset()
Definition AABBTree.h:92
int32 StatNumNonEmptyCellsInGrid
Definition AABBTree.h:109
int32 StatNumGridOverflowElements
Definition AABBTree.h:112
AABBTreeStatistics & MergeStatistics(const AABBTreeStatistics &Rhs)
Definition AABBTree.h:100
int32 StatNumDirtyElements
Definition AABBTree.h:111
Definition AABBTree.h:150
auto Requires(ElementT &InElem, const ElementT &InOtherElem) -> decltype(InElem.UpdateFrom(InOtherElem))
Definition AABBTree.h:758
int32 Index
Definition AABBTree.h:771
DirtyGridHashEntry(const DirtyGridHashEntry &Other)
Definition AABBTree.h:765
DirtyGridHashEntry()
Definition AABBTree.h:759
int32 Count
Definition AABBTree.h:772
Definition AABBTree.h:720
int32 GlobalPayloadIdx
Definition AABBTree.h:721
FAABBTreePayloadInfo(int32 InGlobalPayloadIdx=INDEX_NONE, int32 InDirtyIdx=INDEX_NONE, int32 InLeafIdx=INDEX_NONE, int32 InDirtyGridOverflowIdx=INDEX_NONE, int32 InNodeIdx=INDEX_NONE)
Definition AABBTree.h:727
int32 DirtyPayloadIdx
Definition AABBTree.h:722
int32 NodeIdx
Definition AABBTree.h:725
int32 LeafIdx
Definition AABBTree.h:723
void Serialize(FArchive &Ar)
Definition AABBTree.h:735
int32 DirtyGridOverflowIdx
Definition AABBTree.h:724
Definition ISpatialAcceleration.h:64
Definition ISpatialAcceleration.h:14
const FVec3 & Dir
Definition ISpatialAcceleration.h:24
const FVec3 InvDir
Definition ISpatialAcceleration.h:25
FReal CurrentLength
Definition ISpatialAcceleration.h:27
FReal InvCurrentLength
Definition ISpatialAcceleration.h:28
const bool bParallel[3]
Definition ISpatialAcceleration.h:30
static FORCEINLINE_DEBUGGABLE bool Intersects(const FVec3 &Start, FQueryFastDataVoid &QueryFastData, FReal &TOI, const FAABB3 &Bounds, const FAABB3 &QueryBounds, const FVec3 &QueryHalfExtents, const FVec3 &Dir, const FVec3 InvDir, const bool bParallel[3])
Definition AABBTree.h:209
static FORCEINLINE_DEBUGGABLE bool Intersects(const FVec3 &Start, FQueryFastData &QueryFastData, FReal &TOI, const FAABB3 &Bounds, const FAABB3 &QueryBounds, const FVec3 &QueryHalfExtents, const FVec3 &Dir, const FVec3 InvDir, const bool bParallel[3])
Definition AABBTree.h:184
static FORCEINLINE_DEBUGGABLE bool Intersects(const FVec3 &Start, FQueryFastData &QueryFastData, FReal &TOI, const FAABB3 &Bounds, const FAABB3 &QueryBounds, const FVec3 &QueryHalfExtents, const FVec3 &Dir, const FVec3 InvDir, const bool bParallel[3])
Definition AABBTree.h:196
Definition AABBTree.h:172
static bool Intersects(const FVec3 &Start, TQueryFastData &QueryFastData, FReal &TOI, const FAABB3 &Bounds, const FAABB3 &QueryBounds, const FVec3 &QueryHalfExtents, const FVec3 &Dir, const FVec3 InvDir, const bool bParallel[3])
Definition AABBTree.h:173
Definition AABBTree.h:260
TArray< TPayloadBoundsElement< TPayloadType, T > > Elems
Definition AABBTree.h:547
SIZE_T GetReserveCount() const
Definition AABBTree.h:274
void UpdateElementWithoutDirty(const TPayloadType &Payload, const TAABB< T, 3 > &NewBounds)
Definition AABBTree.h:486
FORCEINLINE_DEBUGGABLE bool SweepFast(const TVec3< T > &Start, TQueryFastData &QueryFastData, const TVec3< T > &QueryHalfExtents, TSQVisitor &Visitor, const TVec3< T > &Dir, const TVec3< T > InvDir, const bool bParallel[3]) const
Definition AABBTree.h:319
FORCEINLINE_DEBUGGABLE bool RaycastFastSimd(const VectorRegister4Double &Start, TQueryFastData &QueryFastData, TSQVisitor &Visitor, const VectorRegister4Double &Dir, const VectorRegister4Double &InvDir, const VectorRegister4Double &Parallel, const VectorRegister4Double &Length) const
Definition AABBTree.h:313
void Reset()
Definition AABBTree.h:505
bool bDirtyLeaf
Definition AABBTree.h:550
FORCEINLINE_DEBUGGABLE bool RaycastFast(const TVec3< T > &Start, TQueryFastData &QueryFastData, TSQVisitor &Visitor, const TVec3< T > &Dir, const TVec3< T > &InvDir, const bool bParallel[3]) const
Definition AABBTree.h:307
void PrintLeaf() const
Definition AABBTree.h:530
FORCEINLINE_DEBUGGABLE bool RaycastImpSimd(const VectorRegister4Double &Start, TQueryFastData &QueryFastData, TSQVisitor &Visitor, const VectorRegister4Double &InvDir, const VectorRegister4Double &Parallel, const VectorRegister4Double &Length) const
Definition AABBTree.h:427
void DebugDrawLeaf(ISpacialDebugDrawInterface< T > &InInterface, const FLinearColor &InLinearColor, float InThickness) const
Definition AABBTree.h:512
SIZE_T GetElementCount() const
Definition AABBTree.h:280
TAABBTreeLeafArray()
Definition AABBTree.h:261
FORCEINLINE_DEBUGGABLE bool RaycastSweepImp(const TVec3< T > &Start, TQueryFastData &QueryFastData, const TVec3< T > &QueryHalfExtents, TSQVisitor &Visitor, const TVec3< T > &Dir, const TVec3< T > InvDir, const bool bParallel[3]) const
Definition AABBTree.h:400
void RecomputeBounds(bool bDynamicTree)
Definition AABBTree.h:285
void UpdateElement(const TPayloadType &Payload, const TAABB< T, 3 > &NewBounds, bool bHasBounds)
Definition AABBTree.h:469
bool IsLeafDirty() const
Definition AABBTree.h:293
bool OverlapFast(const FAABB3 &QueryBounds, TSQVisitor &Visitor) const
Definition AABBTree.h:326
void AddElement(const TPayloadBoundsElement< TPayloadType, T > &Element)
Definition AABBTree.h:499
void Serialize(FChaosArchive &Ar)
Definition AABBTree.h:542
void GatherElements(TArray< TPayloadBoundsElement< TPayloadType, T > > &OutElements) const
Definition AABBTree.h:269
void RemoveElement(TPayloadType Payload)
Definition AABBTree.h:452
TAABBTreeLeafArray(const TArray< TPayloadBoundsElement< TPayloadType, T > > &InElems)
Definition AABBTree.h:263
void SetDirtyState(const bool bDirtyState)
Definition AABBTree.h:301
Definition AABBTree.h:648
TAABB< T, 3 > ChildrenBounds[2]
Definition AABBTree.h:657
bool bDirtyNode
Definition AABBTree.h:661
int32 ChildrenNodes[2]
Definition AABBTree.h:658
void DebugDraw(ISpacialDebugDrawInterface< T > &InInterface, const TArray< TAABBTreeNode< T > > &Nodes, const FVec3 &InLinearColor, float InThickness) const
Definition AABBTree.h:664
bool bLeaf
Definition AABBTree.h:660
void Serialize(FChaosArchive &Ar)
Definition AABBTree.h:687
TAABBTreeNode()
Definition AABBTree.h:649
int32 ParentNode
Definition AABBTree.h:659
Definition AABBTree.h:911
Definition AABBTree.h:1238
TArray< FElement > AllElements
Definition AABBTree.h:1239
int32 DepthTotal
Definition AABBTree.h:1240
int32 DirtyElementCount
Definition AABBTree.h:1242
int32 MaxDepth
Definition AABBTree.h:1241
Definition AABBTree.h:1358
int32 NodeIdx
Definition AABBTree.h:1359
int32 LeafIdx
Definition AABBTree.h:1360
const TAABB< T, 3 > GetBounds() const
Definition AABBTree.h:252
void ComputeBounds(const TArray< TPayloadBoundsElement< TPayloadType, T > > &, bool bDynamicTree)
Definition AABBTree.h:248
void ComputeBounds(const TArray< TPayloadBoundsElement< TPayloadType, T > > &Elems, bool bDynamicTree)
Definition AABBTree.h:224
const TAABB< T, 3 > & GetBounds() const
Definition AABBTree.h:239
Definition AABBTree.h:218
Definition AABBTree.h:777
static void InitPayloadToInfo(PayloadToInfoType &PayloadToInfo)
Definition AABBTree.h:780
Definition ISpatialAcceleration.h:226
Definition ISpatialAcceleration.h:99
Definition AABBTree.h:64
static CHAOS_API FAutoConsoleVariableRef CVarMinNodesChunkToProcessBetweenTimeChecks
Definition AABBTree.h:72
static CHAOS_API float MaxProcessingTimePerSliceSeconds
Definition AABBTree.h:68
static CHAOS_API FAutoConsoleVariableRef CVarUseTimeSliceByMillisecondBudget
Definition AABBTree.h:66
static CHAOS_API bool bUseTimeSliceMillisecondBudget
Definition AABBTree.h:65
static CHAOS_API FAutoConsoleVariableRef CVarMaxProcessingTimePerSlice
Definition AABBTree.h:69
static CHAOS_API int32 MinDataChunkToProcessBetweenTimeChecks
Definition AABBTree.h:74
static CHAOS_API FAutoConsoleVariableRef CVarMinDataChunkToProcessBetweenTimeChecks
Definition AABBTree.h:75
static CHAOS_API int32 MinNodesChunkToProcessBetweenTimeChecks
Definition AABBTree.h:71
Definition AABBTree.h:27
static CHAOS_API int32 SplitAtAverageCenter
Definition AABBTree.h:31
static CHAOS_API float DynamicTreeBoundingBoxPadding
Definition AABBTree.h:37
static CHAOS_API int32 UpdateDirtyElementPayloadData
Definition AABBTree.h:28
static CHAOS_API FAutoConsoleVariableRef CVarSplitAtAverageCenter
Definition AABBTree.h:32
static CHAOS_API float DynamicTreeLeafEnlargePercent
Definition AABBTree.h:40
static CHAOS_API int32 DynamicTreeLeafCapacity
Definition AABBTree.h:43
static CHAOS_API int32 SplitOnVarianceAxis
Definition AABBTree.h:34
static CHAOS_API FAutoConsoleVariableRef CVarUpdateDirtyElementPayloadData
Definition AABBTree.h:29
static CHAOS_API FAutoConsoleVariableRef CVarDynamicTreeLeafCapacity
Definition AABBTree.h:44
static CHAOS_API FAutoConsoleVariableRef CVarSplitOnVarianceAxis
Definition AABBTree.h:35
static CHAOS_API FAutoConsoleVariableRef CVarDynamicTreeLeafEnlargePercent
Definition AABBTree.h:41
static CHAOS_API FAutoConsoleVariableRef CVarDynamicTreeBoundingBoxPadding
Definition AABBTree.h:38
Definition AABBTree.h:49
static CHAOS_API FAutoConsoleVariableRef CVarDirtyElementMaxCellCapacity
Definition AABBTree.h:60
static CHAOS_API FAutoConsoleVariableRef CVarDirtyElementMaxGridCellQueryCount
Definition AABBTree.h:54
static CHAOS_API int32 DirtyElementMaxPhysicalSizeInCells
Definition AABBTree.h:56
static CHAOS_API FAutoConsoleVariableRef CVarDirtyElementGridCellSize
Definition AABBTree.h:51
static CHAOS_API int32 DirtyElementMaxGridCellQueryCount
Definition AABBTree.h:53
static CHAOS_API int32 DirtyElementMaxCellCapacity
Definition AABBTree.h:59
static CHAOS_API int32 DirtyElementGridCellSize
Definition AABBTree.h:50
static CHAOS_API FAutoConsoleVariableRef CVarDirtyElementMaxPhysicalSizeInCells
Definition AABBTree.h:57
static double Seconds()
Definition AndroidPlatformTime.h:20
CORE_API static const FGuid GUID
Definition ExternalPhysicsCustomObjectVersion.h:144
@ ImmutableAABBTree
Definition ExternalPhysicsCustomObjectVersion.h:94
@ RemovedAABBTreeFullBounds
Definition ExternalPhysicsCustomObjectVersion.h:133
Definition Color.h:48
static CORE_API const FLinearColor Green
Definition Color.h:461
static CORE_API const FLinearColor Red
Definition Color.h:460
static CORE_API FLinearColor MakeRandomColor()
Definition Color.cpp:488
static UE_FORCEINLINE_HINT bool IsNearlyZero(float Value, float ErrorTolerance=UE_SMALL_NUMBER)
Definition UnrealMathUtility.h:407
Definition NumericLimits.h:41
Definition SQVisitor.h:43
Definition Tuple.h:652
Definition UnrealMathFPU.h:42