UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
BoundingVolume.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2#pragma once
3
4#include "Chaos/Real.h"
5#include "Chaos/Array.h"
6#include "Chaos/ArrayND.h"
8#include "Chaos/Box.h"
9#include "Chaos/Defines.h"
11#include "Chaos/Transform.h"
12#include "Chaos/UniformGrid.h"
14#include "ChaosStats.h"
15#include "ChaosLog.h"
16#include "HAL/IConsoleManager.h"
18#include "Templates/Models.h"
19
20#include <memory>
21#include <unordered_set>
22
23// Required for debug blocks below in raycasts
24//#include "Engine/Engine.h"
25//#include "Engine/World.h"
26//#include "DrawDebugHelpers.h"
27
29
30template <typename T, bool>
34
35template <typename TSOA>
37{
38 using TPayloadType = typename TSOA::THandleType;
39};
40
41template <typename T>
46
48{
49 template <typename T>
50 auto Requires(typename T::THandleType) ->void;
51};
52
58
65
66namespace Chaos
67{
68template<typename TPayloadType, typename T, int d>
84
85template<typename TPayloadType, class T, int d>
91
92template <typename T, int d>
108
109template <typename T, int d>
111{
112 Info.Serialize(Ar);
113 return Ar;
114}
115
116template<typename InPayloadType, typename T = FReal, int d = 3>
117class TBoundingVolume final : public ISpatialAcceleration<InPayloadType, T,d>
118{
119 public:
122 using TType = T;
123 static constexpr int D = d;
124
125 static constexpr int32 DefaultMaxCells = 15;
126 static constexpr T DefaultMaxPayloadBounds = 100000;
130 , MaxPayloadBounds(DefaultMaxPayloadBounds)
131 {
132 }
133
134 template <typename ParticleView>
135 TBoundingVolume(const ParticleView& Particles, const bool bUseVelocity = false, const T Dt = 0, const int32 MaxCells = DefaultMaxCells, const T InMaxPayloadBounds = DefaultMaxPayloadBounds)
137 , MaxPayloadBounds(InMaxPayloadBounds)
138 {
139 Reinitialize(Particles, bUseVelocity, Dt, MaxCells);
140 }
141
144 , MGlobalPayloads(MoveTemp(Other.MGlobalPayloads))
145 , MGrid(MoveTemp(Other.MGrid))
146 , MElements(MoveTemp(Other.MElements))
147 , MDirtyElements(MoveTemp(Other.MDirtyElements))
148 , MPayloadInfo(MoveTemp(Other.MPayloadInfo))
149 , MaxPayloadBounds(Other.MaxPayloadBounds)
150 , bIsEmpty(Other.bIsEmpty)
151 {
152 }
153
154 //needed for tree of grids, should we have a more explicit way to copy an array of BVs to avoid this being public?
157 , MGlobalPayloads(Other.MGlobalPayloads)
158 , MGrid(Other.MGrid)
159 , MElements(Other.MElements.Copy())
160 , MDirtyElements(Other.MDirtyElements)
161 , MPayloadInfo(Other.MPayloadInfo)
162 , MaxPayloadBounds(Other.MaxPayloadBounds)
163 , bIsEmpty(Other.bIsEmpty)
164 {
165 }
166
167public:
168
170 {
171
173 *this = static_cast<const TBoundingVolume<TPayloadType, T, d>&>(Other);
174 }
175
177 {
179 MGlobalPayloads = Other.MGlobalPayloads;
180 MGrid = Other.MGrid;
181 MElements = Other.MElements;
182 MDirtyElements = Other.MDirtyElements;
183 MPayloadInfo = Other.MPayloadInfo;
184 MaxPayloadBounds = Other.MaxPayloadBounds;
185 bIsEmpty = Other.bIsEmpty;
186 return *this;
187 }
188
190 {
191 MGlobalPayloads = MoveTemp(Other.MGlobalPayloads);
192 MGrid = MoveTemp(Other.MGrid);
193 MElements = MoveTemp(Other.MElements);
194 MDirtyElements = MoveTemp(Other.MDirtyElements);
195 MPayloadInfo = MoveTemp(Other.MPayloadInfo);
196 MaxPayloadBounds = Other.MaxPayloadBounds;
197 bIsEmpty = Other.bIsEmpty;
198 return *this;
199 }
200
205
206 template <typename ParticleView>
207 void Reinitialize(const ParticleView& Particles, const bool bUseVelocity = false, const T Dt = 0, const int32 MaxCells = DefaultMaxCells)
208 {
209 GenerateTree(Particles, bUseVelocity, Dt, MaxCells);
210 }
211
213 {
214 struct FSimpleVisitor
215 {
217 bool VisitOverlap(const TSpatialVisitorData<TPayloadType>& Instance)
218 {
219 CollectedResults.Add(Instance.Payload);
220 return true;
221 }
222 const void* GetQueryData() const { return nullptr; }
223 const void* GetSimData() const { return nullptr; }
224 const void* GetQueryPayload() const { return nullptr; }
225 bool ShouldIgnore(const TSpatialVisitorData<TPayloadType>& Instance) const { return false; }
226 TArray<TPayloadType>& CollectedResults;
227 };
228
229 TArray<TPayloadType> Results;
230 FSimpleVisitor Collector(Results);
231 Overlap(Intersection, Collector);
232
233 return Results;
234 }
235
236 virtual void Reset() override
237 {
238 MGlobalPayloads.Reset();
239 MGrid.Reset();
240 MElements.Reset();
241 MDirtyElements.Reset();
242 MPayloadInfo.Reset();
243 bIsEmpty = true;
244 }
245
246 virtual bool RemoveElement(const TPayloadType& Payload) override
247 {
249 if (const FPayloadInfo* PayloadInfo = MPayloadInfo.Find(Payload))
250 {
251 if (PayloadInfo->GlobalPayloadIdx != INDEX_NONE)
252 {
253 RemoveGlobalElement(Payload, *PayloadInfo);
254 }
255 else
256 {
257 RemoveElementFromExistingGrid(Payload, *PayloadInfo);
258 }
259
260 MPayloadInfo.Remove(Payload);
261 return true;
262 }
263 return false;
264 }
265
266 virtual bool UpdateElement(const TPayloadType& Payload, const TAABB<T,d>& NewBounds, bool bHasBounds) override
267 {
269 bool bElementExisted = true;
270 if (FPayloadInfo* PayloadInfo = MPayloadInfo.Find(Payload))
271 {
272 ensure(bHasBounds || PayloadInfo->GlobalPayloadIdx != INDEX_NONE);
273 if (PayloadInfo->GlobalPayloadIdx == INDEX_NONE)
274 {
275 RemoveElementFromExistingGrid(Payload, *PayloadInfo);
276 AddElementToExistingGrid(Payload, *PayloadInfo, NewBounds, bHasBounds);
277 }
278 else if (bHasBounds)
279 {
280 RemoveGlobalElement(Payload, *PayloadInfo);
281 AddElementToExistingGrid(Payload, *PayloadInfo, NewBounds, bHasBounds);
282 }
283 }
284 else
285 {
286 bElementExisted = false;
287 FPayloadInfo& NewPayloadInfo = MPayloadInfo.Add(Payload);
288 AddElementToExistingGrid(Payload, NewPayloadInfo, NewBounds, bHasBounds);
289 }
290 return bElementExisted;
291 }
292
294 {
295 // Not implemented
296 check(false);
297 }
298
300 {
301 // Not implemented
302 check(false);
303 }
304
305 inline void RecomputeBounds(bool bDynamicTree)
306 {
307 }
308
310 {
311 // Not implemented
312 check(false);
313 return 0;
314 }
315
316
317 // Begin ISpatialAcceleration interface
319
321 {
322 return MGlobalPayloads;
323 }
324
325 virtual void Raycast(const TVector<T, d>& Start, const TVector<T, d>& Dir, const T Length, ISpatialVisitor<TPayloadType, T>& Visitor) const override
326 {
328 return Raycast(Start, Dir, Length, ProxyVisitor);
329 }
330
331 template <typename SQVisitor>
332 bool RaycastFast(const TVector<T,d>& Start, FQueryFastData& CurData, SQVisitor& Visitor, const FVec3& Dir, const FVec3 InvDir, const bool bParallel[3]) const
333 {
334 return RaycastImp(Start, CurData, Visitor, Dir, InvDir, bParallel);
335 }
336
337 template <typename SQVisitor>
339 {
341 VectorStoreDouble3(Start, &StartReal[0]);
342 return RaycastImp(StartReal, CurData, Visitor, CurData.Dir, CurData.InvDir, CurData.bParallel);
343 }
344
345 template <typename SQVisitor, bool bPruneDuplicates = true>
346 void Raycast(const TVector<T, d>& Start, const TVector<T, d>& Dir, const T Length, SQVisitor& Visitor) const
347 {
349 RaycastImp<SQVisitor, bPruneDuplicates>(Start, CurData, Visitor, CurData.Dir, CurData.InvDir, CurData.bParallel);
350 }
351
352 void Sweep(const TVector<T, d>& Start, const TVector<T, d>& Dir, const T Length, const TVector<T, d> QueryHalfExtents, ISpatialVisitor<TPayloadType, T>& Visitor) const override
353 {
356 }
357
358 template <typename SQVisitor>
359 bool SweepFast(const TVector<T,d>& Start, FQueryFastData& CurData, const TVector<T,d>& QueryHalfExtents, SQVisitor& Visitor, const FVec3& Dir, const FVec3 InvDir, const bool bParallel[3]) const
360 {
361 return SweepImp(Start, CurData, QueryHalfExtents, Visitor, Dir, InvDir, bParallel);
362 }
363
364 template <typename SQVisitor, bool bPruneDuplicates = true>
365 void Sweep(const TVector<T, d>& Start, const TVector<T, d>& Dir, const T Length, const TVector<T, d> QueryHalfExtents, SQVisitor& Visitor) const
366 {
369 }
370
376
377 template <typename SQVisitor>
378 bool OverlapFast(const TAABB<T, d>& QueryBounds, SQVisitor& Visitor) const
379 {
380 return OverlapImp(QueryBounds, Visitor);
381 }
382
383 template <typename SQVisitor, bool bPruneDuplicates = true>
384 void Overlap(const TAABB<T, d>& QueryBounds, SQVisitor& Visitor) const
385 {
387 }
388
392 bool IsLeafDirty() const
393 {
394 return false;
395 }
396
400 void SetDirtyState(const bool bDirtyState)
401 {}
402
403 virtual void Serialize(FChaosArchive& Ar) override
404 {
407 {
409 Ar << TmpPayloads;
410 MGlobalPayloads.Reserve(TmpPayloads.Num());
411 for (auto& Payload : TmpPayloads)
412 {
414 }
415 MaxPayloadBounds = DefaultMaxPayloadBounds;
416 }
417 else
418 {
419 Ar << MGlobalPayloads;
420 Ar << MaxPayloadBounds;
421 }
422
423 Ar << MGrid;
424 Ar << MElements;
425 Ar << MDirtyElements;
426 Ar << bIsEmpty;
427
428 Ar << MPayloadInfo;
429
430
431 }
432
434 {
435 OutElements.Reserve(GetReserveCount());
436 OutElements.Append(MGlobalPayloads);
437
438 for (const FCellElement& Elem : MDirtyElements)
439 {
441 }
442
443 const auto& Counts = MGrid.Counts();
444 for (int32 X = 0; X < Counts[0]; ++X)
445 {
446 for (int32 Y = 0; Y < Counts[1]; ++Y)
447 {
448 for (int32 Z = 0; Z < Counts[2]; ++Z)
449 {
450 const auto& Elems = MElements(X, Y, Z);
451 for (const auto& Elem : Elems)
452 {
453 //elements can be in multiple cells, only add for the first cell
454 if (Elem.StartIdx == TVector<int32, 3>(X, Y, Z))
455 {
457 }
458 }
459 }
460 }
461 }
462 }
463
465 {
466 // Optimize for fewer memory allocations.
467 const TVector<int32, d>& Counts = MGrid.Counts();
468 const int32 GridCount = Counts[0] * Counts[1] * Counts[2] * MElements.Num();
469 return MGlobalPayloads.Num() + MDirtyElements.Num() + GridCount;
470 }
471
473 {
474 return TAABB<T, d>(MGrid.MinCorner(), MGrid.MaxCorner());
475 }
476
477private:
478
479 using FCellElement = TBVCellElement<TPayloadType, T, d>;
480 using FPayloadInfo = TBVPayloadInfo<T, d>;
481
482 void RemoveGlobalElement(const TPayloadType& Payload, const FPayloadInfo& PayloadInfo)
483 {
484 ensure(PayloadInfo.DirtyPayloadIdx == INDEX_NONE);
485 auto LastGlobalPayload = MGlobalPayloads.Last().Payload;
486 if (!(LastGlobalPayload == Payload))
487 {
488 MPayloadInfo.FindChecked(LastGlobalPayload).GlobalPayloadIdx = PayloadInfo.GlobalPayloadIdx;
489 }
490 MGlobalPayloads.RemoveAtSwap(PayloadInfo.GlobalPayloadIdx);
491 }
492
493 template <typename SQVisitor, bool bPruneDuplicates = true>
494 bool RaycastImp(const TVector<T, d>& Start, FQueryFastData& CurData, SQVisitor& Visitor, const FVec3& Dir, const FVec3 InvDir, const bool bParallel[3]) const
495 {
496 T TOI, ExitTime;
497
498 for (const auto& Elem : MGlobalPayloads)
499 {
500 if (PrePreFilterHelper(Elem.Payload, Visitor))
501 {
502 continue;
503 }
504
505 const auto& InstanceBounds = Elem.Bounds;
506 if (InstanceBounds.RaycastFast(Start,
507 Dir, InvDir, bParallel, CurData.CurrentLength, CurData.InvCurrentLength, TOI, ExitTime))
508 {
510 const bool bContinue = Visitor.VisitRaycast(VisitData, CurData);
511
512 if (!bContinue)
513 {
514 return false;
515 }
516 }
517 }
518
519 for (const auto& Elem : MDirtyElements)
520 {
521 if (PrePreFilterHelper(Elem.Payload, Visitor))
522 {
523 continue;
524 }
525
526 const auto& InstanceBounds = Elem.Bounds;
527 if (InstanceBounds.RaycastFast(Start, Dir,
528 InvDir, bParallel, CurData.CurrentLength, CurData.InvCurrentLength, TOI, ExitTime))
529 {
531 const bool bContinue = Visitor.VisitRaycast(VisitData, CurData);
532 if (!bContinue)
533 {
534 return false;
535 }
536 }
537 }
538
539 if (bIsEmpty)
540 {
541 return true;
542 }
543
544 TAABB<T, d> GlobalBounds(MGrid.MinCorner(), MGrid.MaxCorner());
545 TVector<T, d> NextStart;
546 TVector<int32, d> CellIdx;
547 bool bCellsLeft = MElements.Num() && GlobalBounds.RaycastFast(Start, Dir,
548 InvDir, bParallel, CurData.CurrentLength, CurData.InvCurrentLength, TOI, NextStart);
549 if (bCellsLeft)
550 {
551 CellIdx = MGrid.Cell(NextStart);
552 FGridSet CellsVisited(MGrid.Counts());
553
554 do
555 {
556 //gather all instances in current cell whose bounds intersect with ray
557 const auto& Elems = MElements(CellIdx);
558 //should we let callback know about max potential?
559
560 for (const auto& Elem : Elems)
561 {
562 if (PrePreFilterHelper(Elem.Payload, Visitor))
563 {
564 continue;
565 }
566
568 {
569 bool bSkip = false;
570 if (Elem.StartIdx[0] != Elem.EndIdx[0] || Elem.StartIdx[1] != Elem.EndIdx[1] || Elem.StartIdx[2] != Elem.EndIdx[2])
571 {
572 for (int32 X = Elem.StartIdx[0]; X <= Elem.EndIdx[0]; ++X)
573 {
574 for (int32 Y = Elem.StartIdx[1]; Y <= Elem.EndIdx[1]; ++Y)
575 {
576 for (int32 Z = Elem.StartIdx[2]; Z <= Elem.EndIdx[2]; ++Z)
577 {
578 if (CellsVisited.Contains(TVector<int32, 3>(X, Y, Z)))
579 {
580 bSkip = true;
581 break;
582 }
583 }
584 }
585 }
586
587 if (bSkip)
588 {
589 continue;
590 }
591 }
592 }
593 const auto& InstanceBounds = Elem.Bounds;
594 if (InstanceBounds.RaycastFast(Start,
595 Dir, InvDir, bParallel, CurData.CurrentLength, CurData.InvCurrentLength, TOI, ExitTime))
596 {
598 const bool bContinue = Visitor.VisitRaycast(VisitData, CurData);
599 if (!bContinue)
600 {
601 return false;
602 }
603 }
604 }
605
606 // Early exit if the raycast has a hit and it has set as bloking hit, it is not necessary to navigate along the raycast
607 if (Visitor.HasBlockingHit())
608 {
609 return false;
610 }
611
612 CellsVisited.Add(CellIdx);
613
614
615 //find next cell
616
617 //We want to know which plane we used to cross into next cell
618 const TVector<T, d> CellCenter = MGrid.Location(CellIdx);
619 const TVector<T, d>& Dx = MGrid.Dx();
620
621 T Times[3];
622 T BestTime = CurData.CurrentLength;
623 bool bTerminate = true;
624 for (int Axis = 0; Axis < d; ++Axis)
625 {
626 if (!bParallel[Axis])
627 {
628 const T CrossPoint = Dir[Axis] > 0 ? CellCenter[Axis] + Dx[Axis] / 2 : CellCenter[Axis] - Dx[Axis] / 2;
629 const T Distance = CrossPoint - NextStart[Axis]; //note: CellCenter already has /2, we probably want to use the corner instead
630 const T Time = Distance * InvDir[Axis];
631 Times[Axis] = Time;
632 if (Time < BestTime)
633 {
634 bTerminate = false; //found at least one plane to pass through
635 BestTime = Time;
636 }
637 }
638 else
639 {
640 Times[Axis] = TNumericLimits<T>::Max();
641 }
642 }
643
644 if (bTerminate)
645 {
646 return true;
647 }
648
649 for (int Axis = 0; Axis < d; ++Axis)
650 {
651 constexpr T Epsilon = 1e-2f; //if raycast is slightly off we still count it as hitting the cell surface
652 CellIdx[Axis] += (Times[Axis] <= BestTime + Epsilon) ? (Dir[Axis] > 0 ? 1 : -1) : 0;
653 if (CellIdx[Axis] < 0 || CellIdx[Axis] >= MGrid.Counts()[Axis])
654 {
655 return true;
656 }
657 }
658
659 NextStart = NextStart + Dir * BestTime;
660 } while (true);
661 }
662
663 return true;
664 }
665
666 template <typename SQVisitor, bool bPruneDuplicates = true>
667 bool SweepImp(const TVector<T, d>& Start, FQueryFastData& CurData, const TVector<T, d> QueryHalfExtents, SQVisitor& Visitor, const FVec3& Dir, const FVec3 InvDir, const bool bParallel[3]) const
668 {
669 T TOI = 0;
670 for (const auto& Elem : MGlobalPayloads)
671 {
672 if (PrePreFilterHelper(Elem.Payload, Visitor))
673 {
674 continue;
675 }
676
677 const TAABB<T, d>& InstanceBounds = Elem.Bounds;
681 if (TAABB<T, d>(Min,Max).RaycastFast(Start, Dir, InvDir, bParallel, CurData.CurrentLength, CurData.InvCurrentLength, TOI, TmpPosition))
682 {
684 const bool bContinue = Visitor.VisitSweep(VisitData, CurData);
685 if (!bContinue)
686 {
687 return false;
688 }
689 }
690 }
691
692 for (const auto& Elem : MDirtyElements)
693 {
694 if (PrePreFilterHelper(Elem.Payload, Visitor))
695 {
696 continue;
697 }
698
699 const TAABB<T, d>& InstanceBounds = Elem.Bounds;
703 if (TAABB<T, d>(Min, Max).RaycastFast(Start, Dir, InvDir, bParallel, CurData.CurrentLength, CurData.InvCurrentLength, TOI, TmpPosition))
704 {
706 const bool bContinue = Visitor.VisitSweep(VisitData, CurData);
707 if (!bContinue)
708 {
709 return false;
710 }
711 }
712 }
713
714 if (bIsEmpty)
715 {
716 return true;
717 }
718
719 TAABB<T, d> GlobalBounds(MGrid.MinCorner() - QueryHalfExtents, MGrid.MaxCorner() + QueryHalfExtents);
720
721
722 struct FCellIntersection
723 {
724 TVector<int32, d> CellIdx;
725 T TOI;
726 };
727
728 const TVector<int32, d> StartMinIndex = MGrid.Cell(Start - QueryHalfExtents);
729 const TVector<int32, d> StartMaxIndex = MGrid.Cell(Start + QueryHalfExtents);
730
732 {
733 const TVector<T, d> End = Start + CurData.CurrentLength * Dir;
734 const TVector<int32, d> EndMinIndex = MGrid.Cell(End - QueryHalfExtents);
735 const TVector<int32, d> EndMaxIndex = MGrid.Cell(End + QueryHalfExtents);
737 {
738 //sweep is fully contained within one cell, this is a common special case - just test all elements
739 const auto& Elems = MElements(StartMinIndex);
741 for (const auto& Elem : Elems)
742 {
743 const TAABB<T, d>& InstanceBounds = Elem.Bounds;
746 if (TAABB<T, d>(Min, Max).RaycastFast(Start, Dir, InvDir, bParallel, CurData.CurrentLength, CurData.InvCurrentLength, TOI, TmpPoint))
747 {
749 const bool bContinue = Visitor.VisitSweep(VisitData, CurData);
750 if (!bContinue)
751 {
752 return false;
753 }
754 }
755 }
756
757 return true;
758 }
759 }
760
761 FGridSet IdxsSeen(MGrid.Counts());
762 FGridSet CellsVisited(MGrid.Counts());
763 TArray<FCellIntersection> IdxsQueue; //cells we need to visit
764
765 for (int32 X = StartMinIndex[0]; X <= StartMaxIndex[0]; ++X)
766 {
767 for (int32 Y = StartMinIndex[1]; Y <= StartMaxIndex[1]; ++Y)
768 {
769 for (int32 Z = StartMinIndex[2]; Z <= StartMaxIndex[2]; ++Z)
770 {
771 const TVector<int32, 3> Idx(X, Y, Z);
772 IdxsQueue.Add({Idx, 0 });
773 IdxsSeen.Add(Idx);
774 }
775 }
776 }
777
779 const bool bInitialHit = GlobalBounds.RaycastFast(Start, Dir, InvDir, bParallel, CurData.CurrentLength, CurData.InvCurrentLength, TOI, HitPoint);
780 if (bInitialHit) //NOTE: it's possible to have a non empty IdxsQueue and bInitialHit be false. This is because the IdxsQueue works off clamped cells which we can skip
781 {
782 //Flood fill from inflated cell so that we get all cells along the ray
783 TVector<int32, d> HitCellIdx = MGrid.Cell(HitPoint);
784
785 if (!IdxsSeen.Contains(HitCellIdx))
786 {
787 ensure(TOI > 0); //Not an initial overlap so TOI must be positive
788 IdxsQueue.Add({ HitCellIdx, TOI });
789 }
790
791 const TVector<T, d> HalfDx = MGrid.Dx() * (T)0.5;
792
793 int32 QueueIdx = 0; //FIFO because early cells are more likely to block later cells we can skip
794 while (QueueIdx < IdxsQueue.Num())
795 {
797 if (CellIntersection.TOI > CurData.CurrentLength)
798 {
799 continue;
800 }
801
802 //ray still visiting this cell so check all neighbors
803 check(d == 3);
804 static const TVector<int32, 3> Neighbors[] =
805 {
806 //grid on z=-1 plane
807 {-1, -1, -1}, {0, -1, -1}, {1, -1, -1},
808 {-1, 0, -1}, {0, 0, -1}, {1, 0, -1},
809 {-1, 1, -1}, {0, 1, -1}, {1, 1, -1},
810
811 //grid on z=0 plane
812 {-1, -1, 0}, {0, -1, 0}, {1, -1, 0},
813 {-1, 0, 0}, {1, 0, 0},
814 {-1, 1, 0}, {0, 1, 0}, {1, 0, 0},
815
816 //grid on z=1 plane
817 {-1, -1, 1}, {0, -1, 1}, {1, -1, 1},
818 {-1, 0, 1}, {0, 0, 1}, {1, 0, 1},
819 {-1, 1, 1}, {0, 1, 1}, {1, 1, 1}
820 };
821
822 for (const TVector<int32, 3>& Neighbor : Neighbors)
823 {
824 const TVector<int32, 3> NeighborIdx = Neighbor + CellIntersection.CellIdx;
825 bool bSkip = false;
826 for (int32 Axis = 0; Axis < d; ++Axis)
827 {
828 if (NeighborIdx[Axis] < 0 || NeighborIdx[Axis] >= MGrid.Counts()[Axis])
829 {
830 bSkip = true;
831 break;
832 }
833 }
834 if (!bSkip && !IdxsSeen.Contains(NeighborIdx))
835 {
837
838 const TVector<T, d> NeighborCenter = MGrid.Location(NeighborIdx);
841 if (TAABB<T, d>(Min, Max).RaycastFast(Start, Dir, InvDir, bParallel, CurData.CurrentLength, CurData.InvCurrentLength, TOI, HitPoint))
842 {
843 IdxsQueue.Add({ NeighborIdx, TOI }); //should we sort by TOI?
844 }
845 }
846 }
847
848 //check if any instances in the cell are hit
849 const auto& Elems = MElements(CellIntersection.CellIdx);
850 for (const auto& Elem : Elems)
851 {
852 if (PrePreFilterHelper(Elem.Payload, Visitor))
853 {
854 continue;
855 }
856
858 {
859 bool bSkip = false;
860 if (Elem.StartIdx[0] != Elem.EndIdx[0] || Elem.StartIdx[1] != Elem.EndIdx[1] || Elem.StartIdx[2] != Elem.EndIdx[2])
861 {
862 for (int32 X = Elem.StartIdx[0]; X <= Elem.EndIdx[0]; ++X)
863 {
864 for (int32 Y = Elem.StartIdx[1]; Y <= Elem.EndIdx[1]; ++Y)
865 {
866 for (int32 Z = Elem.StartIdx[2]; Z <= Elem.EndIdx[2]; ++Z)
867 {
868 if (CellsVisited.Contains(TVector<int32, 3>(X, Y, Z)))
869 {
870 bSkip = true;
871 break;
872 }
873 }
874 }
875 }
876
877 if (bSkip)
878 {
879 continue;
880 }
881 }
882 }
883
884 const TAABB<T, d>& InstanceBounds = Elem.Bounds;
887 if (TAABB<T, d>(Min,Max).RaycastFast(Start,
888 Dir, InvDir, bParallel, CurData.CurrentLength, CurData.InvCurrentLength, TOI, HitPoint))
889 {
891 const bool bContinue = Visitor.VisitSweep(VisitData, CurData);
892 if (!bContinue)
893 {
894 return false;
895 }
896 }
897 }
898 CellsVisited.Add(CellIntersection.CellIdx);
899 }
900 }
901
902 return true;
903 }
904
905 template <typename SQVisitor, bool bPruneDuplicates = true>
906 bool OverlapImp(const TAABB<T, d>& QueryBounds, SQVisitor& Visitor) const
907 {
908 for (const auto& Elem : MGlobalPayloads)
909 {
910 if (PrePreFilterHelper(Elem.Payload, Visitor))
911 {
912 continue;
913 }
914
915 const TAABB<T, d>& InstanceBounds = Elem.Bounds;
916 if (QueryBounds.Intersects(InstanceBounds))
917 {
919 if (Visitor.VisitOverlap(VisitData) == false)
920 {
921 return false;
922 }
923 }
924 }
925
926 for (const auto& Elem : MDirtyElements)
927 {
928 if (PrePreFilterHelper(Elem.Payload, Visitor))
929 {
930 continue;
931 }
932
933 const TAABB<T, d>& InstanceBounds = Elem.Bounds;
934 if (QueryBounds.Intersects(InstanceBounds))
935 {
937 if (Visitor.VisitOverlap(VisitData) == false)
938 {
939 return false;
940 }
941 }
942 }
943
944 if (bIsEmpty)
945 {
946 return true;
947 }
948
949 TAABB<T, d> GlobalBounds(MGrid.MinCorner(), MGrid.MaxCorner());
950
951 const TVector<int32, d> StartIndex = MGrid.Cell(QueryBounds.Min());
952 const TVector<int32, d> EndIndex = MGrid.Cell(QueryBounds.Max());
954
955 for (int32 X = StartIndex[0]; X <= EndIndex[0]; ++X)
956 {
957 for (int32 Y = StartIndex[1]; Y <= EndIndex[1]; ++Y)
958 {
959 for (int32 Z = StartIndex[2]; Z <= EndIndex[2]; ++Z)
960 {
961 const auto& Elems = MElements(X, Y, Z);
962 for (const auto& Elem : Elems)
963 {
964 if (PrePreFilterHelper(Elem.Payload, Visitor))
965 {
966 continue;
967 }
969 {
970 if (InstancesSeen.Contains(GetUniqueIdx(Elem.Payload)))
971 {
972 continue;
973 }
974 InstancesSeen.Add(GetUniqueIdx(Elem.Payload));
975 }
976 const TAABB<T, d>& InstanceBounds = Elem.Bounds;
977 if (QueryBounds.Intersects(InstanceBounds))
978 {
980 if (Visitor.VisitOverlap(VisitData) == false)
981 {
982 return false;
983 }
984 }
985 }
986 }
987 }
988 }
989
990 return true;
991 }
992
993 template <typename ParticleView>
994 void GenerateTree(const ParticleView& Particles, const bool bUseVelocity, const T Dt, const int32 MaxCells)
995 {
996 MGlobalPayloads.Reset();
997 MPayloadInfo.Reset();
998 bIsEmpty = true;
999
1000 if (!Particles.Num())
1001 {
1002 bIsEmpty = true;
1003 return;
1004 }
1005
1008 TArray<bool> HasBounds;
1009
1010 AllBounds.SetNum(Particles.Num());
1011 HasBounds.SetNum(Particles.Num());
1012 T MaxPayloadBoundsCopy = MaxPayloadBounds;
1013 auto GetValidBounds = [MaxPayloadBoundsCopy, bUseVelocity, Dt](const auto& Particle, TAABB<T,d>& OutBounds) -> bool
1014 {
1015 if (HasBoundingBox(Particle))
1016 {
1017 OutBounds = ComputeWorldSpaceBoundingBox(Particle, bUseVelocity, Dt); //todo: avoid computing on the fly
1018 //todo: check if bounds are outside of something we deem reasonable (i.e. object really far out in space)
1019 if (OutBounds.Extents().Max() < MaxPayloadBoundsCopy)
1020 {
1021 return true;
1022 }
1023 }
1024
1025 return false;
1026 };
1027
1028 //compute DX and fill global payloads
1029 auto& GlobalPayloads = MGlobalPayloads;
1030 auto& PayloadInfos = MPayloadInfo;
1032
1033 auto ComputeBoxAndDx = [&Particles, &AllBounds, &HasBounds, &GlobalPayloads, &PayloadInfos, &GetValidBounds, &NumObjectsWithBounds](TAABB<T,d>& OutGlobalBox, bool bFirstPass) -> T
1034 {
1037 constexpr T InvD = (T)1 / d;
1038 int32 Idx = 0;
1039 T Dx = 0;
1041 for (auto& Particle : Particles)
1042 {
1043 TAABB<T,d>& Bounds = AllBounds[Idx];
1044 if ((bFirstPass && GetValidBounds(Particle, Bounds)) || (!bFirstPass && HasBounds[Idx]))
1045 {
1046 HasBounds[Idx] = true;
1047 OutGlobalBox.GrowToInclude(Bounds);
1048 Dx += TVector<T, d>::DotProduct(Bounds.Extents(), TVector<T, d>(1)) * InvD;;
1050 }
1051 else if(bFirstPass)
1052 {
1053 HasBounds[Idx] = false;
1054 auto Payload = Particle.template GetPayload<TPayloadType>(Idx);
1055
1056 const int32 GlobalPayloadIdx = GlobalPayloads.Num();
1057 bool bTooBig = HasBoundingBox(Particle); //todo: avoid this as it was already called in GetValidBounds
1058 GlobalPayloads.Add({ Payload, bTooBig ? Bounds : TAABB<T,d>(TVector<T,d>(TNumericLimits<T>::Lowest()), TVector<T,d>(TNumericLimits<T>::Max())) });
1059 PayloadInfos.Add(Payload, FPayloadInfo{ GlobalPayloadIdx, INDEX_NONE });
1060 }
1061 ++Idx;
1062 }
1063 Dx = NumObjectsWithBounds > 0 ? Dx / NumObjectsWithBounds : (T)0;
1064 return Dx;
1065 };
1066
1068 T Dx = ComputeBoxAndDx(GlobalBox, /*bFirstPass=*/true);
1069
1071 {
1072 bool bRecomputeBoxAndDx = false;
1073 int32 Idx = 0;
1074 for (auto& Particle : Particles)
1075 {
1076 if (HasBounds[Idx])
1077 {
1078 bool bEvictElement = false;
1079 const auto& WorldSpaceBox = AllBounds[Idx];
1080 const TVector<T, d> MinToDXRatio = WorldSpaceBox.Min() / Dx;
1081 for (int32 Axis = 0; Axis < d; ++Axis)
1082 {
1083 if (FMath::Abs(MinToDXRatio[Axis]) > 1e7)
1084 {
1085 bEvictElement = true;
1086 break;
1087 }
1088 }
1089
1090 if (bEvictElement)
1091 {
1092 bRecomputeBoxAndDx = true;
1093 HasBounds[Idx] = false;
1094 auto Payload = Particle.template GetPayload<TPayloadType>(Idx);
1095
1096 const int32 GlobalPayloadIdx = GlobalPayloads.Num();
1097 GlobalPayloads.Add({ Payload, WorldSpaceBox });
1098 MPayloadInfo.Add(Payload, FPayloadInfo{ GlobalPayloadIdx, INDEX_NONE});
1099 }
1100 }
1101
1102 ++Idx;
1103 }
1104
1106 {
1107 Dx = ComputeBoxAndDx(GlobalBox, /*bFirstPass=*/false);
1108 }
1109 }
1110
1111 TVector<int32, d> Cells = Dx > 0 ? GlobalBox.Extents() / Dx : TVector<int32, d>(MaxCells);
1112 Cells += TVector<int32, d>(1);
1113 for (int32 Axis = 0; Axis < d; ++Axis)
1114 {
1115 if (Cells[Axis] > MaxCells)
1116 Cells[Axis] = MaxCells;
1117 if (!(ensure(Cells[Axis] >= 0))) //seeing this because GlobalBox is huge leading to int overflow. Need to investigate why bounds get so big
1118 {
1119 Cells[Axis] = MaxCells;
1120 }
1121 }
1122
1123#if ENABLE_NAN_DIAGNOSTIC
1124 if (!ensure(!GlobalBox.Min().ContainsNaN() && !GlobalBox.Max().ContainsNaN()))
1125 {
1126 UE_LOG(LogChaos, Error, TEXT("BoundingVolume computed invalid GlobalBox from bounds: GlobalBox.Min(): (%f, %f, %f), GlobalBox.Max(): (%f, %f, %f)"),
1127 GlobalBox.Min().X, GlobalBox.Min().Y, GlobalBox.Min().Z, GlobalBox.Max().X, GlobalBox.Max().Y, GlobalBox.Max().Z);
1128 }
1129#endif
1130
1131 MGrid = TUniformGrid<T, d>(GlobalBox.Min(), GlobalBox.Max(), Cells);
1132 MElements = TArrayND<TArray<FCellElement>, d>(MGrid);
1133
1134 //insert into grid cells
1135 T NumObjectsInCells = 0;
1136 {
1138 int32 Idx = 0;
1139 for (auto& Particle : Particles)
1140 {
1141 if (HasBounds[Idx])
1142 {
1143 const TAABB<T, d>& ObjectBox = AllBounds[Idx];
1145 const auto StartIndex = MGrid.Cell(ObjectBox.Min());
1146 const auto EndIndex = MGrid.Cell(ObjectBox.Max());
1147
1148 auto Payload = Particle.template GetPayload<TPayloadType>(Idx);
1149 MPayloadInfo.Add(Payload, FPayloadInfo{ INDEX_NONE, INDEX_NONE, StartIndex, EndIndex });
1150
1151 for (int32 x = StartIndex[0]; x <= EndIndex[0]; ++x)
1152 {
1153 for (int32 y = StartIndex[1]; y <= EndIndex[1]; ++y)
1154 {
1155 for (int32 z = StartIndex[2]; z <= EndIndex[2]; ++z)
1156 {
1157 MElements(x, y, z).Add({ ObjectBox, Payload, StartIndex, EndIndex });
1158 NumObjectsInCells += 1;
1159 }
1160 }
1161 }
1162 }
1163 ++Idx;
1164 }
1165 }
1166
1167 bIsEmpty = NumObjectsInCells == 0;
1168 UE_LOG(LogChaos, Verbose, TEXT("Generated Tree with (%d, %d, %d) Nodes and %f Per Cell"), MGrid.Counts()[0], MGrid.Counts()[1], MGrid.Counts()[2], NumObjectsInCells / NumObjectsWithBounds);
1169 }
1170
1171 void RemoveElementFromExistingGrid(const TPayloadType& Payload, const FPayloadInfo& PayloadInfo)
1172 {
1173 ensure(PayloadInfo.GlobalPayloadIdx == INDEX_NONE);
1174 if (PayloadInfo.DirtyPayloadIdx == INDEX_NONE)
1175 {
1176 for (int32 X = PayloadInfo.StartIdx[0]; X <= PayloadInfo.EndIdx[0]; ++X)
1177 {
1178 for (int32 Y = PayloadInfo.StartIdx[1]; Y <= PayloadInfo.EndIdx[1]; ++Y)
1179 {
1180 for (int32 Z = PayloadInfo.StartIdx[2]; Z <= PayloadInfo.EndIdx[2]; ++Z)
1181 {
1182 TArray<FCellElement>& Elems = MElements(X, Y, Z);
1183 int32 ElemIdx = 0;
1184 for (FCellElement& Elem : Elems)
1185 {
1186 if (Elem.Payload == Payload)
1187 {
1188 Elems.RemoveAtSwap(ElemIdx);
1189 break;
1190 }
1191 ++ElemIdx;
1192 }
1193 }
1194 }
1195 }
1196 }
1197 else
1198 {
1199 //TODO: should we skip this if dirty and still dirty?
1200 auto LastPayload = MDirtyElements.Last().Payload;
1201 if (!(LastPayload == Payload))
1202 {
1203 MPayloadInfo.FindChecked(LastPayload).DirtyPayloadIdx = PayloadInfo.DirtyPayloadIdx;
1204 }
1205 MDirtyElements.RemoveAtSwap(PayloadInfo.DirtyPayloadIdx);
1206 }
1207 }
1208
1209 void AddElementToExistingGrid(const TPayloadType& Payload, FPayloadInfo& PayloadInfo, const TAABB<T, d>& NewBounds, bool bHasBounds)
1210 {
1211 bool bTooBig = false;
1212 if (bHasBounds)
1213 {
1214 if (NewBounds.Extents().Max() > MaxPayloadBounds)
1215 {
1216 bTooBig = true;
1217 bHasBounds = false;
1218 }
1219 }
1220
1221 if(bHasBounds)
1222 {
1223 PayloadInfo.GlobalPayloadIdx = INDEX_NONE;
1224
1225 if (bIsEmpty == false)
1226 {
1227 //add payload to appropriate cells
1228 TVector<int32, 3> StartIndex = MGrid.CellUnsafe(NewBounds.Min());
1229 TVector<int32, 3> EndIndex = MGrid.CellUnsafe(NewBounds.Max());
1230
1231 bool bDirty = false;
1232 for (int Axis = 0; Axis < d; ++Axis)
1233 {
1234 if (StartIndex[Axis] < 0 || EndIndex[Axis] >= MGrid.Counts()[Axis])
1235 {
1236 bDirty = true;
1237 break;
1238 }
1239 }
1240
1241 if (!bDirty)
1242 {
1243 PayloadInfo.DirtyPayloadIdx = INDEX_NONE;
1244 PayloadInfo.StartIdx = StartIndex;
1245 PayloadInfo.EndIdx = EndIndex;
1246
1247 for (int32 x = StartIndex[0]; x <= EndIndex[0]; ++x)
1248 {
1249 for (int32 y = StartIndex[1]; y <= EndIndex[1]; ++y)
1250 {
1251 for (int32 z = StartIndex[2]; z <= EndIndex[2]; ++z)
1252 {
1253 MElements(x, y, z).Add({ NewBounds, Payload, StartIndex, EndIndex });
1254 }
1255 }
1256 }
1257
1258 return;
1259 }
1260 }
1261
1262 PayloadInfo.DirtyPayloadIdx = MDirtyElements.Num();
1263 MDirtyElements.Add({ NewBounds, Payload });
1264 }
1265 else
1266 {
1267 PayloadInfo.GlobalPayloadIdx = MGlobalPayloads.Num();
1268 PayloadInfo.DirtyPayloadIdx = INDEX_NONE;
1270 }
1271 }
1272
1273 TArray<TPayloadType> FindAllIntersectionsHelper(const TAABB<T, d>& ObjectBox) const
1274 {
1275 TArray<TPayloadType> Intersections;
1276 const auto StartIndex = MGrid.Cell(ObjectBox.Min());
1277 const auto EndIndex = MGrid.Cell(ObjectBox.Max());
1278 for (int32 x = StartIndex[0]; x <= EndIndex[0]; ++x)
1279 {
1280 for (int32 y = StartIndex[1]; y <= EndIndex[1]; ++y)
1281 {
1282 for (int32 z = StartIndex[2]; z <= EndIndex[2]; ++z)
1283 {
1284 const TArray<FCellElement>& CellElements = MElements(x, y, z);
1285 Intersections.Reserve(Intersections.Num() + CellElements.Num());
1286 for (const FCellElement& Elem : CellElements)
1287 {
1288 if (ObjectBox.Intersects(Elem.Bounds))
1289 {
1290 Intersections.Add(Elem.Payload);
1291 }
1292 }
1293 }
1294 }
1295 }
1296
1297 Algo::Sort(Intersections,[](const TPayloadType& A,const TPayloadType& B)
1298 {
1299 return GetUniqueIdx(A) < GetUniqueIdx(B);
1300 });
1301
1302 for (int32 i = Intersections.Num() - 1; i > 0; i--)
1303 {
1304 if (Intersections[i] == Intersections[i - 1])
1305 {
1306 Intersections.RemoveAtSwap(i, EAllowShrinking::No);
1307 }
1308 }
1309
1310 return Intersections;
1311 }
1312
1314 struct FGridSet
1315 {
1316 FGridSet(TVector<int32, 3> Size)
1317 : NumX(Size[0])
1318 , NumY(Size[1])
1319 , NumZ(Size[2])
1320 {
1321 int32 BitsNeeded = NumX * NumY * NumZ;
1322 int32 BytesNeeded = 1 + (BitsNeeded) / 8;
1323 Data = new uint8[BytesNeeded];
1325 }
1326
1327 bool Contains(const TVector<int32, 3>& Coordinate)
1328 {
1329 //Most sweeps are straight down the Z so store as adjacent Z, then Y, then X
1330 int32 Idx = (NumY * NumZ) * Coordinate[0] + (NumZ * Coordinate[1]) + Coordinate[2];
1331 int32 ByteIdx = Idx / 8;
1332 int32 BitIdx = Idx % 8;
1333 bool bContains = (Data[ByteIdx] >> BitIdx) & 0x1;
1334 return bContains;
1335 }
1336
1337 void Add(const TVector<int32, 3>& Coordinate)
1338 {
1339 //Most sweeps are straight down the Z so store as adjacent Z, then Y, then X
1340 int32 Idx = (NumY * NumZ) * Coordinate[0] + (NumZ * Coordinate[1]) + Coordinate[2];
1341 int32 ByteIdx = Idx / 8;
1342 int32 BitIdx = Idx % 8;
1343 uint8 Mask = static_cast<uint8>(1 << BitIdx);
1344 Data[ByteIdx] |= Mask;
1345 }
1346
1347 ~FGridSet()
1348 {
1349 delete[] Data;
1350 }
1351
1352 private:
1353 int32 NumX;
1354 int32 NumY;
1355 int32 NumZ;
1356 uint8* Data;
1357 };
1358
1360 TUniformGrid<T, d> MGrid;
1361 TArrayND<TArray<FCellElement>, d> MElements;
1362 TArray<FCellElement> MDirtyElements;
1364 T MaxPayloadBounds;
1365 bool bIsEmpty;
1366
1367 friend ::FChaosVDDataWrapperUtils;
1368};
1369
1370template<typename TPayloadType, class T, int d>
1376
1377template<typename TPayloadType, class T, int d>
1383
1384#if !UE_MERGED_MODULES
1385#if PLATFORM_COMPILER_CLANG
1388#else
1389extern template class TBoundingVolume<int32, FReal, 3>;
1391#endif
1392#endif
1393
1394}
#define check(expr)
Definition AssertionMacros.h:314
#define ensure( InExpression)
Definition AssertionMacros.h:464
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
#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
return true
Definition ExternalRpcRegistry.cpp:601
#define UE_LOG(CategoryName, Verbosity, Format,...)
Definition LogMacros.h:270
FORCEINLINE void VectorStoreDouble3(VectorRegister4Double Vec, double *Ptr)
Definition UnrealMathVectorCommon.h.inl:825
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
uint32 Size
Definition VulkanMemory.cpp:4034
uint8_t uint8
Definition binka_ue_file_header.h:8
Definition ChaosArchive.h:167
Definition ISpatialAcceleration.h:267
virtual void DeepAssign(const ISpatialAcceleration< TPayloadType, T, d > &Other)
Definition ISpatialAcceleration.h:344
Definition ISpatialAcceleration.h:120
Definition AABB.h:37
static FORCEINLINE TAABB< T, d > EmptyAABB()
Definition AABB.h:623
Definition BoundingVolume.h:118
virtual TUniquePtr< ISpatialAcceleration< TPayloadType, T, d > > Copy() const override
Definition BoundingVolume.h:201
virtual void DeepAssign(const ISpatialAcceleration< TPayloadType, T, d > &Other) override
Definition BoundingVolume.h:169
bool RaycastFast(const TVector< T, d > &Start, FQueryFastData &CurData, SQVisitor &Visitor, const FVec3 &Dir, const FVec3 InvDir, const bool bParallel[3]) const
Definition BoundingVolume.h:332
static constexpr T DefaultMaxPayloadBounds
Definition BoundingVolume.h:126
virtual void Serialize(FChaosArchive &Ar) override
Definition BoundingVolume.h:403
void AddElement(const TPayloadBoundsElement< TPayloadType, T > &Payload)
Definition BoundingVolume.h:299
void GatherElements(TArray< TPayloadBoundsElement< TPayloadType, T > > &OutElements) const
Definition BoundingVolume.h:433
static constexpr int32 DefaultMaxCells
Definition BoundingVolume.h:125
virtual TArray< TPayloadType > FindAllIntersections(const FAABB3 &Box) const override
Definition BoundingVolume.h:318
TAABB< T, d > GetBounds() const
Definition BoundingVolume.h:472
void Sweep(const TVector< T, d > &Start, const TVector< T, d > &Dir, const T Length, const TVector< T, d > QueryHalfExtents, SQVisitor &Visitor) const
Definition BoundingVolume.h:365
InPayloadType TPayloadType
Definition BoundingVolume.h:120
TBoundingVolume< TPayloadType, T, d > & operator=(const TBoundingVolume< TPayloadType, T, d > &Other)
Definition BoundingVolume.h:176
TArray< TPayloadType > FindAllIntersectionsImp(const TAABB< T, d > &Intersection) const
Definition BoundingVolume.h:212
bool RaycastFastSimd(const VectorRegister4Double &Start, FQueryFastData &CurData, SQVisitor &Visitor, const VectorRegister4Double &Dir, const VectorRegister4Double &InvDir, const VectorRegister4Double &Parallel, const VectorRegister4Double &Length) const
Definition BoundingVolume.h:338
bool OverlapFast(const TAABB< T, d > &QueryBounds, SQVisitor &Visitor) const
Definition BoundingVolume.h:378
static constexpr ESpatialAcceleration StaticType
Definition BoundingVolume.h:127
int32 GetReserveCount() const
Definition BoundingVolume.h:464
TBoundingVolume(const ParticleView &Particles, const bool bUseVelocity=false, const T Dt=0, const int32 MaxCells=DefaultMaxCells, const T InMaxPayloadBounds=DefaultMaxPayloadBounds)
Definition BoundingVolume.h:135
void Reinitialize(const ParticleView &Particles, const bool bUseVelocity=false, const T Dt=0, const int32 MaxCells=DefaultMaxCells)
Definition BoundingVolume.h:207
T TType
Definition BoundingVolume.h:122
void Raycast(const TVector< T, d > &Start, const TVector< T, d > &Dir, const T Length, SQVisitor &Visitor) const
Definition BoundingVolume.h:346
virtual bool UpdateElement(const TPayloadType &Payload, const TAABB< T, d > &NewBounds, bool bHasBounds) override
Definition BoundingVolume.h:266
virtual void Reset() override
Definition BoundingVolume.h:236
void UpdateElementWithoutDirty(const TPayloadType &Payload, const TAABB< T, 3 > &NewBounds)
Definition BoundingVolume.h:293
static constexpr int D
Definition BoundingVolume.h:123
bool SweepFast(const TVector< T, d > &Start, FQueryFastData &CurData, const TVector< T, d > &QueryHalfExtents, SQVisitor &Visitor, const FVec3 &Dir, const FVec3 InvDir, const bool bParallel[3]) const
Definition BoundingVolume.h:359
void SetDirtyState(const bool bDirtyState)
Definition BoundingVolume.h:400
TBoundingVolume(const TBoundingVolume< TPayloadType, T, d > &Other)
Definition BoundingVolume.h:155
TBoundingVolume()
Definition BoundingVolume.h:128
virtual void Overlap(const TAABB< T, d > &QueryBounds, ISpatialVisitor< TPayloadType, T > &Visitor) const override
Definition BoundingVolume.h:371
bool IsLeafDirty() const
Definition BoundingVolume.h:392
const TArray< TPayloadBoundsElement< TPayloadType, T > > & GlobalObjects() const
Definition BoundingVolume.h:320
void RecomputeBounds(bool bDynamicTree)
Definition BoundingVolume.h:305
void Overlap(const TAABB< T, d > &QueryBounds, SQVisitor &Visitor) const
Definition BoundingVolume.h:384
virtual bool RemoveElement(const TPayloadType &Payload) override
Definition BoundingVolume.h:246
int32 GetElementCount()
Definition BoundingVolume.h:309
void Sweep(const TVector< T, d > &Start, const TVector< T, d > &Dir, const T Length, const TVector< T, d > QueryHalfExtents, ISpatialVisitor< TPayloadType, T > &Visitor) const override
Definition BoundingVolume.h:352
TBoundingVolume(TBoundingVolume< TPayloadType, T, d > &&Other)
Definition BoundingVolume.h:142
TPayloadType PayloadType
Definition BoundingVolume.h:121
TBoundingVolume< TPayloadType, T, d > & operator=(TBoundingVolume< TPayloadType, T, d > &&Other)
Definition BoundingVolume.h:189
virtual void Raycast(const TVector< T, d > &Start, const TVector< T, d > &Dir, const T Length, ISpatialVisitor< TPayloadType, T > &Visitor) const override
Definition BoundingVolume.h:325
static void SerializeAsAABB(FArchive &Ar, TAABB< T, d > &AABB)
Definition Box.h:467
Definition ISpatialAcceleration.h:453
Definition Vector.h:41
virtual void Serialize(void *V, int64 Length) override
Definition ArchiveProxy.h:97
Definition Archive.h:1208
virtual CORE_API void UsingCustomVersion(const struct FGuid &Guid)
Definition Archive.cpp:590
CORE_API int32 CustomVer(const struct FGuid &Key) const
Definition Archive.cpp:602
Definition IConsoleManager.h:1580
Definition ChaosVDDataWrapperUtils.h:77
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
UE_FORCEINLINE_HINT void RemoveAtSwap(SizeType Index, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2185
void SetNum(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2308
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition UniquePtr.h:107
UE_REWRITE void Sort(RangeType &&Range)
Definition Sort.h:16
GeometryCollection::Facades::FMuscleActivationData Data
Definition MuscleActivationConstraints.h:15
Definition SkeletalMeshComponent.h:307
ESpatialAcceleration
Definition ISpatialAcceleration.h:180
@ Y
Definition SimulationModuleBase.h:153
@ X
Definition SimulationModuleBase.h:152
FChaosArchive & operator<<(FChaosArchive &Ar, FRigidParticleControlFlags &Flags)
Definition RigidParticleControlFlags.cpp:15
TAABB< T, d > ComputeWorldSpaceBoundingBox(const TParticles< T, d > &Objects, const int32 i, bool bUseVelocity=false, T Dt=0)
Definition BoundingVolumeUtilities.h:167
@ Add
Definition PendingSpatialData.h:18
bool PrePreFilterHelper(const FAccelerationStructureHandle &Payload, const Private::FSimOverlapVisitor &Visitor)
Definition SpatialAccelerationBroadPhase.h:323
bool HasBoundingBox(const OBJECT_ARRAY &Objects, const int32 i)
Definition BoundingVolumeUtilities.h:97
@ Raycast
Definition SimulationModuleBase.h:145
TVector< FReal, 3 > FVec3
Definition Core.h:17
TEnableIf<!TIsPointer< TPayload >::Value, FUniqueIdx >::Type GetUniqueIdx(const TPayload &Payload)
Definition ISpatialAcceleration.h:196
@ Contains
Definition AutomationTest.h:160
@ Visitor
Definition XmppMultiUserChat.h:94
const FColor Neighbor(0, 192, 128)
constexpr double Epsilon
Definition SlopeUtils.h:114
@ Start
Definition GeoEnum.h:100
@ false
Definition radaudio_common.h:23
Definition BoundingVolume.h:48
auto Requires(typename T::THandleType) -> void
Definition ISpatialAcceleration.h:14
Definition BoundingVolume.h:70
TVector< int32, 3 > StartIdx
Definition BoundingVolume.h:73
TVector< int32, 3 > EndIdx
Definition BoundingVolume.h:74
void Serialize(FChaosArchive &Ar)
Definition BoundingVolume.h:76
TPayloadType Payload
Definition BoundingVolume.h:72
TAABB< T, d > Bounds
Definition BoundingVolume.h:71
Definition BoundingVolume.h:94
int32 GlobalPayloadIdx
Definition BoundingVolume.h:95
TVector< int32, d > EndIdx
Definition BoundingVolume.h:98
void Serialize(FArchive &Ar)
Definition BoundingVolume.h:100
int32 DirtyPayloadIdx
Definition BoundingVolume.h:96
TVector< int32, d > StartIdx
Definition BoundingVolume.h:97
Definition ISpatialAcceleration.h:226
TPayloadType Payload
Definition ISpatialAcceleration.h:227
Definition ISpatialAcceleration.h:99
Definition BoundingVolume.h:54
static int32 FilterFarBodies
Definition BoundingVolume.h:55
static FAutoConsoleVariableRef CVarFilterFarBodies
Definition BoundingVolume.h:56
CORE_API static const FGuid GUID
Definition ExternalPhysicsCustomObjectVersion.h:144
@ GlobalElementsHaveBounds
Definition ExternalPhysicsCustomObjectVersion.h:28
static UE_FORCEINLINE_HINT void * Memzero(void *Dest, SIZE_T Count)
Definition UnrealMemory.h:131
Definition NumericLimits.h:41
typename TSOA::THandleType TPayloadType
Definition BoundingVolume.h:38
int32 TPayloadType
Definition BoundingVolume.h:44
Definition BoundingVolume.h:32
Definition UnrealMathFPU.h:42