UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
BoundingVolumeUtilities.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2#pragma once
3
4#include "Chaos/Array.h"
5#include "Chaos/Box.h"
6#include "Chaos/Defines.h"
11#include "Chaos/Particles.h"
12#include "Chaos/Sphere.h"
13#include "Chaos/Transform.h"
14
15#define MIN_NUM_OBJECTS 5
16
17namespace Chaos
18{
20
22{
23 for (int i = 0; i < 3; ++i)
24 {
25 const FReal BoundsThickness = FMath::Abs(Vel[i]) * Dt * BoundsVelocityInflation;
27 }
28 return Vel;
29}
30
32{
33 const bool bIsBounded = InParticles.HasBounds(BodyIndex);
34 const bool bIsCCD = InParticles.ControlFlags(BodyIndex).GetCCDEnabled();
35
36 if (!bIsCCD && (BoundsVelocityInflation == FReal(0)))
37 {
39 }
40
41 // See comments in ComputeBoundsThickness<THandle> below
43 if (bIsBounded)
44 {
45 if (bIsCCD)
46 {
47 BoundsVelocityInflation = FMath::Max(FReal(1), BoundsVelocityInflation);
48 }
49 else
50 {
51 MaxBoundsThickness = Chaos_Bounds_MaxInflationScale * InParticles.LocalBounds(BodyIndex).Extents().GetMax();
52 }
53 }
54
55 return ComputeBoundsThickness(InParticles.GetV(BodyIndex), Dt, MinBoundsThickness, MaxBoundsThickness, BoundsVelocityInflation);
56}
57
58template <typename THandle>
59FVec3 ComputeBoundsThickness(const THandle& ParticleHandle, FReal Dt, FReal MinBoundsThickness, FReal BoundsVelocityInflation)
60{
61 const typename THandle::FDynamicParticleHandleType* RigidParticle = ParticleHandle.CastToRigidParticle();
62 const typename THandle::FKinematicParticleHandleType* KinematicParticle = ParticleHandle.CastToKinematicParticle();
63 const bool bIsBounded = ParticleHandle.HasBounds();
64 const bool bIsCCD = (RigidParticle != nullptr) && RigidParticle->CCDEnabled();
65
66 if (!bIsCCD && (BoundsVelocityInflation == FReal(0)))
67 {
69 }
70
71 // Limit the bounds expansion based on the size of the object. This prevents objects that are moved a large
72 // distance without resetting physics from having excessive bounds. Objects that move more than their size per
73 // tick without CCD enabled will have simulation issues anyway, so expanding bounds beyond this is unnecessary.
75 if (bIsBounded)
76 {
77 if (bIsCCD)
78 {
79 BoundsVelocityInflation = FMath::Max(FReal(1), BoundsVelocityInflation);
80 }
81 else
82 {
83 MaxBoundsThickness = Chaos_Bounds_MaxInflationScale * ParticleHandle.LocalBounds().Extents().GetMax();
84 }
85 }
86
87 FVec3 Vel(0);
88 if (KinematicParticle != nullptr)
89 {
91 }
92
93 return ComputeBoundsThickness(Vel, Dt, MinBoundsThickness, MaxBoundsThickness, BoundsVelocityInflation);
94}
95
96template<class OBJECT_ARRAY>
97bool HasBoundingBox(const OBJECT_ARRAY& Objects, const int32 i)
98{
99 return Objects[i]->HasBoundingBox();
100}
101
102template<class T, int d>
103bool HasBoundingBox(const TParticles<T, d>& Objects, const int32 i)
104{
105 return true;
106}
107
108template<class T, int d>
109bool HasBoundingBox(const TGeometryParticles<T, d>& Objects, const int32 i)
110{
111 return Objects.GetGeometry(i)->HasBoundingBox();
112}
113
114template<class T, int d>
115bool HasBoundingBox(const TPBDRigidParticles<T, d>& Objects, const int32 i)
116{
117 if (Objects.GetGeometry(i))
118 {
119 return Objects.GetGeometry(i)->HasBoundingBox();
120 }
121 return Objects.CollisionParticles(i) != nullptr && Objects.CollisionParticles(i)->Size() > 0;
122}
123
124template<typename Generic>
125bool HasBoundingBox(const Generic& Item)
126{
127 return Item.HasBoundingBox();
128}
129
130template<typename T, int d, bool bPersistent>
132{
133 return Handle.HasBounds();
134}
135
136template<typename T, int d, bool bPersistent>
138{
139 return Handle.HasBounds();
140}
141
142template<class OBJECT_ARRAY, class T, int d>
144{
145 return Objects[i]->BoundingBox();
146}
147
148template<class T, int d>
150{
151 return WorldSpaceBoxes[i];
152}
153
154template<class T, int d>
156{
157 return GetWorldSpaceBoundingBox(static_cast<const TParticles<T, d>&>(Objects), i, WorldSpaceBoxes);
158}
159
160template<class T, int d>
162{
163 return GetWorldSpaceBoundingBox(static_cast<const TParticles<T, d>&>(Objects), i, WorldSpaceBoxes);
164}
165
166template<class T, int d>
167TAABB<T, d> ComputeWorldSpaceBoundingBox(const TParticles<T, d>& Objects, const int32 i, bool bUseVelocity = false, T Dt = 0)
168{
170 return TAABB<T, d>(Objects.GetX(i), Objects.GetX(i));
171}
172
173template<class T, int d>
175{
177 TRigidTransform<T, d> LocalToWorld(Objects.GetX(i), Objects.GetR(i));
178 const auto& LocalBoundingBox = Objects.GetGeometry(i)->BoundingBox();
179 return LocalBoundingBox.TransformedAABB(LocalToWorld);
180}
181
182template<class T, int d>
184{
185 TRigidTransform<T, d> LocalToWorld(Objects.GetP(i), Objects.GetQ(i));
187 if (Objects.GetGeometry(i))
188 {
189 const auto& LocalBoundingBox = Objects.GetGeometry(i)->BoundingBox();
190 WorldSpaceBox = LocalBoundingBox.TransformedAABB(LocalToWorld);
191 }
192 else
193 {
194 check(Objects.CollisionParticles(i) && Objects.CollisionParticles(i)->Size());
195 TAABB<T, d> LocalBoundingBox(Objects.CollisionParticles(i)->GetX(0), Objects.CollisionParticles(i)->GetX(0));
196 for (uint32 j = 1; j < Objects.CollisionParticles(i)->Size(); ++j)
197 {
198 LocalBoundingBox.GrowToInclude(Objects.CollisionParticles(i)->GetX(j));
199 }
200 WorldSpaceBox = LocalBoundingBox.TransformedAABB(LocalToWorld);
201 }
202
203 if (bUseVelocity)
204 {
205 WorldSpaceBox.ThickenSymmetrically(ComputeBoundsThickness(Objects, Dt, i, 0, 1));
206 }
207 return WorldSpaceBox;
208}
209
210template<typename THandle, typename T, int d, bool bPersistent>
212{
213 const auto PBDRigid = Handle.CastToRigidParticle();
214 const bool bIsRigidDynamic = PBDRigid && PBDRigid->ObjectState() == EObjectStateType::Dynamic;
215
217 if(Handle.Geometry())
218 {
219 const auto& LocalBoundingBox = Handle.Geometry()->BoundingBox();
220 return LocalBoundingBox.TransformedBox(LocalToWorld);
221 }
222
224 check(PBDRigid->CollisionParticles() && PBDRigid->CollisionParticles()->Size());
225 TAABB<T, d> LocalBoundingBox(PBDRigid->CollisionParticles()->X(0), PBDRigid->CollisionParticles()->X(0));
226 for(uint32 j = 1; j < PBDRigid->CollisionParticles()->Size(); ++j)
227 {
228 LocalBoundingBox.GrowToInclude(PBDRigid->CollisionParticles()->X(j));
229 }
230 return LocalBoundingBox.TransformedBox(LocalToWorld);
231}
232
233template<typename T, int d, bool bPersistent>
235{
236 return Handle.WorldSpaceInflatedBounds();
237}
238
239template<typename T, int d, bool bPersistent>
241{
242 return Handle.WorldSpaceInflatedBounds();
243}
244
245template<typename T, typename GenericEntry>
247{
249 return InEntry.BoundingBox();
250}
251
252template<typename OBJECT_ARRAY, typename T, int d>
254{
256 for (int32 i = 1; i < AllObjects.Num(); ++i)
257 {
258 GlobalBox.GrowToInclude(GetWorldSpaceBoundingBox(Objects, AllObjects[i], WorldSpaceBoxes));
259 }
260 int32 Axis = 0;
263 {
264 Axis = 2;
265 }
266 else if (GlobalExtents[1] > GlobalExtents[0])
267 {
268 Axis = 1;
269 }
270 if (bAllowMultipleSplitting && GlobalExtents[Axis] < GlobalExtents[(Axis + 1) % 3] * 1.25 && GlobalExtents[Axis] < GlobalExtents[(Axis + 2) % 3] * 1.25 && AllObjects.Num() > 4 * MIN_NUM_OBJECTS)
271 {
272 Axis = -1;
273 }
274
275 OutAxis = Axis;
276 return GlobalBox;
277}
278
279template<typename T, int d>
281{
282 //simple particles means we can split more efficiently
283 TPair<int32, int32> Counts[d];
284
285 for (int32 i = 0; i < d; ++i)
286 {
287 Counts[i].Key = 0;
288 Counts[i].Value = 0;
289 };
290
291 auto CountLambda = [&](const TVector<T, d>& Point)
292 {
293 for (int32 i = 0; i < d; ++i)
294 {
295 Counts[i].Key += Point[i] > 0 ? 0 : 1;
296 Counts[i].Value += Point[i] > 0 ? 1 : 0;
297 };
298 };
299
301 CountLambda(GlobalBox.Center());
302 for (int32 i = 1; i < AllObjects.Num(); ++i)
303 {
305 GlobalBox.GrowToInclude(PtBox);
306 CountLambda(PtBox.Center());
307 }
308
309 //we pick the axis that gives us the most culled even in the case when it goes in the wrong direction (i.e the biggest min)
310 int32 BestAxis = 0;
311 int32 MaxCulled = 0;
312 for (int32 Axis = 0; Axis < d; ++Axis)
313 {
314 int32 CulledWorstCase = FMath::Min(Counts[Axis].Key, Counts[Axis].Value);
316 {
318 BestAxis = Axis;
319 }
320 }
321
322 //todo(ocohen): use multi split when CulledWorstCase is similar for every axis
323
325 return GlobalBox;
326}
327
328template<class OBJECT_ARRAY, class T, int d>
329void ComputeAllWorldSpaceBoundingBoxes(const OBJECT_ARRAY& Objects, const TArray<int32>& AllObjects, const bool bUseVelocity, const T Dt, TMap<int32, TAABB<T, d>>& WorldSpaceBoxes)
330{
332}
333
334template<class T, int d>
336{
338 WorldSpaceBoxes.Reserve(AllObjects.Num());
339 //PhysicsParallelFor(AllObjects.Num(), [&](int32 i) {
340 for (int32 i : AllObjects)
341 {
342 WorldSpaceBoxes.FindOrAdd(i) = ComputeWorldSpaceBoundingBox(Objects, i);
343 }
344 //});
345}
346
347template<class T, int d>
349{
351 WorldSpaceBoxes.Reserve(AllObjects.Num());
352 //PhysicsParallelFor(AllObjects.Num(), [&](int32 i) {
353 for (int32 i : AllObjects)
354 {
355 WorldSpaceBoxes.FindOrAdd(i) = ComputeWorldSpaceBoundingBox(Objects, i);
356 }
357 //});
358}
359
360template<class T, int d>
362{
363 WorldSpaceBoxes.Reserve(AllObjects.Num());
364 //PhysicsParallelFor(AllObjects.Num(), [&](int32 i) {
365 for (int32 i = 0; i < AllObjects.Num(); ++i)
366 {
367 const int32 BodyIndex = AllObjects[i];
368 TAABB<T, d>& WorldSpaceBox = WorldSpaceBoxes.FindOrAdd(BodyIndex);
370 }
371 //});
372}
373
374// Tests whether a type is actually a view on particles or just a generic type
375// to separate some extended functionality for particle types
377{
378 template<typename T>
379 auto Requires() -> decltype(T::THandleType);
380};
381
382//todo: how do we protect ourselves and make it const?
383template<typename ParticleView, typename T, int d>
385{
386 WorldSpaceBoxes.AddUninitialized(Particles.Num());
387 ParticlesParallelFor(Particles, [&RequiresBounds, &WorldSpaceBoxes, bUseVelocity, Dt](const auto& Particle, int32 Index)
388 {
390 {
392 if (bUseVelocity)
393 {
394 if (const auto PBDRigid = Particle.AsDynamic())
395 {
396 WorldSpaceBoxes.Last().ThickenSymmetrically(ComputeBoundsThickness(*PBDRigid, Dt, 0, 1));
397 }
398 }
399 }
400 });
401}
402
403template<typename ParticleView, typename T, int d>
405{
406 WorldSpaceBoxes.AddUninitialized(Particles.Num());
407 ParticlesParallelFor(Particles, [&RequiresBounds, &WorldSpaceBoxes, bUseVelocity, Dt](const auto& Particle, int32 Index)
408 {
410 {
412 }
413 });
414}
415
416template<class OBJECT_ARRAY>
418{
419 return Objects.Num();
420}
421
422template<class T, int d>
424{
425 return Objects.Size();
426}
427
428template<class T, int d>
430{
431 return GetObjectCount(static_cast<const TParticles<T, d>&>(Objects));
432}
433
434template<class T, int d>
436{
437 return GetObjectCount(static_cast<const TParticles<T, d>&>(Objects));
438}
439
440template<class OBJECT_ARRAY>
441bool IsDisabled(const OBJECT_ARRAY& Objects, const uint32 Index)
442{
443 return false;
444}
445
446template<class T, int d>
448{
449 return false;
450}
451
452template<class T, int d>
454{
455 return Objects.Disabled(Index);
456}
457}
#define check(expr)
Definition AssertionMacros.h:314
#define ensure( InExpression)
Definition AssertionMacros.h:464
#define MIN_NUM_OBJECTS
Definition BoundingVolumeUtilities.h:15
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
uint32 Size
Definition VulkanMemory.cpp:4034
uint32_t uint32
Definition binka_ue_file_header.h:6
bool HasBoundingBox() const
Definition ImplicitObject.h:275
virtual CHAOS_API const FAABB3 BoundingBox() const
Definition ImplicitObject.cpp:118
Definition AABB.h:37
FORCEINLINE const TAABB< T, d > & BoundingBox() const
Definition AABB.h:156
FORCEINLINE void GrowToInclude(const TVector< T, d > &V)
Definition AABB.h:393
CHAOSCORE_API TAABB< T, d > TransformedAABB(const FTransform &) const
Definition AABB.cpp:385
uint32 Size() const
Definition ArrayCollection.h:66
Definition ParticleHandle.h:436
Definition GeometryParticles.h:152
Definition Handles.h:93
Definition ParticleHandle.h:987
Definition PBDRigidParticles.h:22
FORCEINLINE const TVector< T, d > & GetP(const int32 index) const
Definition PBDRigidParticles.h:66
FORCEINLINE const TRotation< T, d > GetQ(const int32 index) const
Definition PBDRigidParticles.h:73
Definition Particles.h:32
const TVector< T, d > & GetX(const int32 Index) const
Definition Particles.h:156
FORCEINLINE const bool Disabled(const int32 Index) const
Definition RigidParticles.h:230
FORCEINLINE const TUniquePtr< TBVHParticles< T, d > > & CollisionParticles(const int32 Index) const
Definition RigidParticles.h:218
Definition Transform.h:115
FORCEINLINE const TRotation< T, d > GetR(const int32 Index) const
Definition SimpleGeometryParticles.h:54
FORCEINLINE const FImplicitObjectPtr & GetGeometry(const int32 Index) const
Definition SimpleGeometryParticles.h:61
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
Definition EnableIf.h:20
Definition UnrealString.h.inl:34
Definition SkeletalMeshComponent.h:307
const TAABB< T, d > GetWorldSpaceBoundingBox(const OBJECT_ARRAY &Objects, const int32 i, const TMap< int32, TAABB< T, d > > &WorldSpaceBoxes)
Definition BoundingVolumeUtilities.h:143
void ParticlesParallelFor(const TView &Particles, const ContextCreatorType &ContextCreator, const Lambda &Func, bool bForceSingleThreaded=false)
Definition ParticleIterator.h:99
TAABB< T, d > ComputeWorldSpaceBoundingBoxForHandle(const THandle &Handle)
Definition BoundingVolumeUtilities.h:211
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
int32 GetObjectCount(const OBJECT_ARRAY &Objects)
Definition BoundingVolumeUtilities.h:417
bool HasBoundingBox(const OBJECT_ARRAY &Objects, const int32 i)
Definition BoundingVolumeUtilities.h:97
TVector< FReal, 3 > FVec3
Definition Core.h:17
bool IsDisabled(const OBJECT_ARRAY &Objects, const uint32 Index)
Definition BoundingVolumeUtilities.h:441
@ Generic
Definition ParticlePairMidPhase.h:33
void ComputeAllWorldSpaceBoundingBoxes(const OBJECT_ARRAY &Objects, const TArray< int32 > &AllObjects, const bool bUseVelocity, const T Dt, TMap< int32, TAABB< T, d > > &WorldSpaceBoxes)
Definition BoundingVolumeUtilities.h:329
FVec3 ComputeBoundsThickness(FVec3 Vel, FReal Dt, FReal MinBoundsThickness, FReal MaxBoundsThickness, FReal BoundsVelocityInflation)
Definition BoundingVolumeUtilities.h:21
FRealSingle Chaos_Bounds_MaxInflationScale
Definition BoundingVolumeHierarchy.cpp:18
const TAABB< T, d > ComputeGlobalBoxAndSplitAxis(const OBJECT_ARRAY &Objects, const TArray< int32 > &AllObjects, const TMap< int32, TAABB< T, d > > &WorldSpaceBoxes, bool bAllowMultipleSplitting, int32 &OutAxis)
Definition BoundingVolumeUtilities.h:253
U16 Index
Definition radfft.cpp:71
Definition BoundingVolumeUtilities.h:377
auto Requires() -> decltype(T::THandleType)
static constexpr UE_FORCEINLINE_HINT T Clamp(const T X, const T MinValue, const T MaxValue)
Definition UnrealMathUtility.h:592
Definition NumericLimits.h:41
Definition Tuple.h:652