Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/Shape/StaticCompoundShape.cpp
9913 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#include <Jolt/Jolt.h>
6
7
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
8
#include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
9
#include <Jolt/Physics/Collision/Shape/CompoundShapeVisitors.h>
10
#include <Jolt/Core/Profiler.h>
11
#include <Jolt/Core/StreamIn.h>
12
#include <Jolt/Core/StreamOut.h>
13
#include <Jolt/Core/TempAllocator.h>
14
#include <Jolt/Core/ScopeExit.h>
15
#include <Jolt/ObjectStream/TypeDeclarations.h>
16
17
JPH_NAMESPACE_BEGIN
18
19
JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(StaticCompoundShapeSettings)
20
{
21
JPH_ADD_BASE_CLASS(StaticCompoundShapeSettings, CompoundShapeSettings)
22
}
23
24
ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create(TempAllocator &inTempAllocator) const
25
{
26
if (mCachedResult.IsEmpty())
27
{
28
if (mSubShapes.size() == 0)
29
{
30
// It's an error to create a compound with no subshapes (the compound cannot encode this)
31
mCachedResult.SetError("Compound needs a sub shape!");
32
}
33
else if (mSubShapes.size() == 1)
34
{
35
// If there's only 1 part we don't need a StaticCompoundShape
36
const SubShapeSettings &s = mSubShapes[0];
37
if (s.mPosition == Vec3::sZero()
38
&& s.mRotation == Quat::sIdentity())
39
{
40
// No rotation or translation, we can use the shape directly
41
if (s.mShapePtr != nullptr)
42
mCachedResult.Set(const_cast<Shape *>(s.mShapePtr.GetPtr()));
43
else if (s.mShape != nullptr)
44
mCachedResult = s.mShape->Create();
45
else
46
mCachedResult.SetError("Sub shape is null!");
47
}
48
else
49
{
50
// We can use a RotatedTranslatedShape instead
51
RotatedTranslatedShapeSettings settings;
52
settings.mPosition = s.mPosition;
53
settings.mRotation = s.mRotation;
54
settings.mInnerShape = s.mShape;
55
settings.mInnerShapePtr = s.mShapePtr;
56
Ref<Shape> shape = new RotatedTranslatedShape(settings, mCachedResult);
57
}
58
}
59
else
60
{
61
// Build a regular compound shape
62
Ref<Shape> shape = new StaticCompoundShape(*this, inTempAllocator, mCachedResult);
63
}
64
}
65
return mCachedResult;
66
}
67
68
ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create() const
69
{
70
TempAllocatorMalloc allocator;
71
return Create(allocator);
72
}
73
74
void StaticCompoundShape::Node::SetChildInvalid(uint inIndex)
75
{
76
// Make this an invalid node
77
mNodeProperties[inIndex] = INVALID_NODE;
78
79
// Make bounding box invalid
80
mBoundsMinX[inIndex] = HALF_FLT_MAX;
81
mBoundsMinY[inIndex] = HALF_FLT_MAX;
82
mBoundsMinZ[inIndex] = HALF_FLT_MAX;
83
mBoundsMaxX[inIndex] = HALF_FLT_MAX;
84
mBoundsMaxY[inIndex] = HALF_FLT_MAX;
85
mBoundsMaxZ[inIndex] = HALF_FLT_MAX;
86
}
87
88
void StaticCompoundShape::Node::SetChildBounds(uint inIndex, const AABox &inBounds)
89
{
90
mBoundsMinX[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetX());
91
mBoundsMinY[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetY());
92
mBoundsMinZ[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetZ());
93
mBoundsMaxX[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetX());
94
mBoundsMaxY[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetY());
95
mBoundsMaxZ[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetZ());
96
}
97
98
void StaticCompoundShape::sPartition(uint *ioBodyIdx, AABox *ioBounds, int inNumber, int &outMidPoint)
99
{
100
// Handle trivial case
101
if (inNumber <= 4)
102
{
103
outMidPoint = inNumber / 2;
104
return;
105
}
106
107
// Calculate bounding box of box centers
108
Vec3 center_min = Vec3::sReplicate(FLT_MAX);
109
Vec3 center_max = Vec3::sReplicate(-FLT_MAX);
110
for (const AABox *b = ioBounds, *b_end = ioBounds + inNumber; b < b_end; ++b)
111
{
112
Vec3 center = b->GetCenter();
113
center_min = Vec3::sMin(center_min, center);
114
center_max = Vec3::sMax(center_max, center);
115
}
116
117
// Calculate split plane
118
int dimension = (center_max - center_min).GetHighestComponentIndex();
119
float split = 0.5f * (center_min + center_max)[dimension];
120
121
// Divide bodies
122
int start = 0, end = inNumber;
123
while (start < end)
124
{
125
// Search for first element that is on the right hand side of the split plane
126
while (start < end && ioBounds[start].GetCenter()[dimension] < split)
127
++start;
128
129
// Search for the first element that is on the left hand side of the split plane
130
while (start < end && ioBounds[end - 1].GetCenter()[dimension] >= split)
131
--end;
132
133
if (start < end)
134
{
135
// Swap the two elements
136
std::swap(ioBodyIdx[start], ioBodyIdx[end - 1]);
137
std::swap(ioBounds[start], ioBounds[end - 1]);
138
++start;
139
--end;
140
}
141
}
142
JPH_ASSERT(start == end);
143
144
if (start > 0 && start < inNumber)
145
{
146
// Success!
147
outMidPoint = start;
148
}
149
else
150
{
151
// Failed to divide bodies
152
outMidPoint = inNumber / 2;
153
}
154
}
155
156
void StaticCompoundShape::sPartition4(uint *ioBodyIdx, AABox *ioBounds, int inBegin, int inEnd, int *outSplit)
157
{
158
uint *body_idx = ioBodyIdx + inBegin;
159
AABox *node_bounds = ioBounds + inBegin;
160
int number = inEnd - inBegin;
161
162
// Partition entire range
163
sPartition(body_idx, node_bounds, number, outSplit[2]);
164
165
// Partition lower half
166
sPartition(body_idx, node_bounds, outSplit[2], outSplit[1]);
167
168
// Partition upper half
169
sPartition(body_idx + outSplit[2], node_bounds + outSplit[2], number - outSplit[2], outSplit[3]);
170
171
// Convert to proper range
172
outSplit[0] = inBegin;
173
outSplit[1] += inBegin;
174
outSplit[2] += inBegin;
175
outSplit[3] += outSplit[2];
176
outSplit[4] = inEnd;
177
}
178
179
StaticCompoundShape::StaticCompoundShape(const StaticCompoundShapeSettings &inSettings, TempAllocator &inTempAllocator, ShapeResult &outResult) :
180
CompoundShape(EShapeSubType::StaticCompound, inSettings, outResult)
181
{
182
// Check that there's at least 1 shape
183
uint num_subshapes = (uint)inSettings.mSubShapes.size();
184
if (num_subshapes < 2)
185
{
186
outResult.SetError("Compound needs at least 2 sub shapes, otherwise you should use a RotatedTranslatedShape!");
187
return;
188
}
189
190
// Keep track of total mass to calculate center of mass
191
float mass = 0.0f;
192
193
mSubShapes.resize(num_subshapes);
194
for (uint i = 0; i < num_subshapes; ++i)
195
{
196
const CompoundShapeSettings::SubShapeSettings &shape = inSettings.mSubShapes[i];
197
198
// Start constructing the runtime sub shape
199
SubShape &out_shape = mSubShapes[i];
200
if (!out_shape.FromSettings(shape, outResult))
201
return;
202
203
// Calculate mass properties of child
204
MassProperties child = out_shape.mShape->GetMassProperties();
205
206
// Accumulate center of mass
207
mass += child.mMass;
208
mCenterOfMass += out_shape.GetPositionCOM() * child.mMass;
209
}
210
211
if (mass > 0.0f)
212
mCenterOfMass /= mass;
213
214
// Cache the inner radius as it can take a while to recursively iterate over all sub shapes
215
CalculateInnerRadius();
216
217
// Temporary storage for the bounding boxes of all shapes
218
uint bounds_size = num_subshapes * sizeof(AABox);
219
AABox *bounds = (AABox *)inTempAllocator.Allocate(bounds_size);
220
JPH_SCOPE_EXIT([&inTempAllocator, bounds, bounds_size]{ inTempAllocator.Free(bounds, bounds_size); });
221
222
// Temporary storage for body indexes (we're shuffling them)
223
uint body_idx_size = num_subshapes * sizeof(uint);
224
uint *body_idx = (uint *)inTempAllocator.Allocate(body_idx_size);
225
JPH_SCOPE_EXIT([&inTempAllocator, body_idx, body_idx_size]{ inTempAllocator.Free(body_idx, body_idx_size); });
226
227
// Shift all shapes so that the center of mass is now at the origin and calculate bounds
228
for (uint i = 0; i < num_subshapes; ++i)
229
{
230
SubShape &shape = mSubShapes[i];
231
232
// Shift the shape so it's centered around our center of mass
233
shape.SetPositionCOM(shape.GetPositionCOM() - mCenterOfMass);
234
235
// Transform the shape's bounds into our local space
236
Mat44 transform = Mat44::sRotationTranslation(shape.GetRotation(), shape.GetPositionCOM());
237
AABox shape_bounds = shape.mShape->GetWorldSpaceBounds(transform, Vec3::sOne());
238
239
// Store bounds and body index for tree construction
240
bounds[i] = shape_bounds;
241
body_idx[i] = i;
242
243
// Update our local bounds
244
mLocalBounds.Encapsulate(shape_bounds);
245
}
246
247
// The algorithm is a recursive tree build, but to avoid the call overhead we keep track of a stack here
248
struct StackEntry
249
{
250
uint32 mNodeIdx; // Node index of node that is generated
251
int mChildIdx; // Index of child that we're currently processing
252
int mSplit[5]; // Indices where the node ID's have been split to form 4 partitions
253
AABox mBounds; // Bounding box of this node
254
};
255
uint stack_size = num_subshapes * sizeof(StackEntry);
256
StackEntry *stack = (StackEntry *)inTempAllocator.Allocate(stack_size);
257
JPH_SCOPE_EXIT([&inTempAllocator, stack, stack_size]{ inTempAllocator.Free(stack, stack_size); });
258
int top = 0;
259
260
// Reserve enough space so that every sub shape gets its own leaf node
261
uint next_node_idx = 0;
262
mNodes.resize(num_subshapes + (num_subshapes + 2) / 3); // = Sum(num_subshapes * 4^-i) with i = [0, Inf].
263
264
// Create root node
265
stack[0].mNodeIdx = next_node_idx++;
266
stack[0].mChildIdx = -1;
267
stack[0].mBounds = AABox();
268
sPartition4(body_idx, bounds, 0, num_subshapes, stack[0].mSplit);
269
270
for (;;)
271
{
272
StackEntry &cur_stack = stack[top];
273
274
// Next child
275
cur_stack.mChildIdx++;
276
277
// Check if all children processed
278
if (cur_stack.mChildIdx >= 4)
279
{
280
// Terminate if there's nothing left to pop
281
if (top <= 0)
282
break;
283
284
// Add our bounds to our parents bounds
285
StackEntry &prev_stack = stack[top - 1];
286
prev_stack.mBounds.Encapsulate(cur_stack.mBounds);
287
288
// Store this node's properties in the parent node
289
Node &parent_node = mNodes[prev_stack.mNodeIdx];
290
parent_node.mNodeProperties[prev_stack.mChildIdx] = cur_stack.mNodeIdx;
291
parent_node.SetChildBounds(prev_stack.mChildIdx, cur_stack.mBounds);
292
293
// Pop entry from stack
294
--top;
295
}
296
else
297
{
298
// Get low and high index to bodies to process
299
int low = cur_stack.mSplit[cur_stack.mChildIdx];
300
int high = cur_stack.mSplit[cur_stack.mChildIdx + 1];
301
int num_bodies = high - low;
302
303
if (num_bodies == 0)
304
{
305
// Mark invalid
306
Node &node = mNodes[cur_stack.mNodeIdx];
307
node.SetChildInvalid(cur_stack.mChildIdx);
308
}
309
else if (num_bodies == 1)
310
{
311
// Get body info
312
uint child_node_idx = body_idx[low];
313
const AABox &child_bounds = bounds[low];
314
315
// Update node
316
Node &node = mNodes[cur_stack.mNodeIdx];
317
node.mNodeProperties[cur_stack.mChildIdx] = child_node_idx | IS_SUBSHAPE;
318
node.SetChildBounds(cur_stack.mChildIdx, child_bounds);
319
320
// Encapsulate bounding box in parent
321
cur_stack.mBounds.Encapsulate(child_bounds);
322
}
323
else
324
{
325
// Allocate new node
326
StackEntry &new_stack = stack[++top];
327
JPH_ASSERT(top < (int)num_subshapes);
328
new_stack.mNodeIdx = next_node_idx++;
329
new_stack.mChildIdx = -1;
330
new_stack.mBounds = AABox();
331
sPartition4(body_idx, bounds, low, high, new_stack.mSplit);
332
}
333
}
334
}
335
336
// Resize nodes to actual size
337
JPH_ASSERT(next_node_idx <= mNodes.size());
338
mNodes.resize(next_node_idx);
339
mNodes.shrink_to_fit();
340
341
// Check if we ran out of bits for addressing a node
342
if (next_node_idx > IS_SUBSHAPE)
343
{
344
outResult.SetError("Compound hierarchy has too many nodes");
345
return;
346
}
347
348
// Check if we're not exceeding the amount of sub shape id bits
349
if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits)
350
{
351
outResult.SetError("Compound hierarchy is too deep and exceeds the amount of available sub shape ID bits");
352
return;
353
}
354
355
outResult.Set(this);
356
}
357
358
template <class Visitor>
359
inline void StaticCompoundShape::WalkTree(Visitor &ioVisitor) const
360
{
361
uint32 node_stack[cStackSize];
362
node_stack[0] = 0;
363
int top = 0;
364
do
365
{
366
// Test if the node is valid, the node should rarely be invalid but it is possible when testing
367
// a really large box against the tree that the invalid nodes will intersect with the box
368
uint32 node_properties = node_stack[top];
369
if (node_properties != INVALID_NODE)
370
{
371
// Test if node contains triangles
372
bool is_node = (node_properties & IS_SUBSHAPE) == 0;
373
if (is_node)
374
{
375
const Node &node = mNodes[node_properties];
376
377
// Unpack bounds
378
UVec4 bounds_minxy = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node.mBoundsMinX[0]));
379
Vec4 bounds_minx = HalfFloatConversion::ToFloat(bounds_minxy);
380
Vec4 bounds_miny = HalfFloatConversion::ToFloat(bounds_minxy.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
381
382
UVec4 bounds_minzmaxx = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node.mBoundsMinZ[0]));
383
Vec4 bounds_minz = HalfFloatConversion::ToFloat(bounds_minzmaxx);
384
Vec4 bounds_maxx = HalfFloatConversion::ToFloat(bounds_minzmaxx.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
385
386
UVec4 bounds_maxyz = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node.mBoundsMaxY[0]));
387
Vec4 bounds_maxy = HalfFloatConversion::ToFloat(bounds_maxyz);
388
Vec4 bounds_maxz = HalfFloatConversion::ToFloat(bounds_maxyz.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
389
390
// Load properties for 4 children
391
UVec4 properties = UVec4::sLoadInt4(&node.mNodeProperties[0]);
392
393
// Check which sub nodes to visit
394
int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, top);
395
396
// Push them onto the stack
397
JPH_ASSERT(top + 4 < cStackSize);
398
properties.StoreInt4(&node_stack[top]);
399
top += num_results;
400
}
401
else
402
{
403
// Points to a sub shape
404
uint32 sub_shape_idx = node_properties ^ IS_SUBSHAPE;
405
const SubShape &sub_shape = mSubShapes[sub_shape_idx];
406
407
ioVisitor.VisitShape(sub_shape, sub_shape_idx);
408
}
409
410
// Check if we're done
411
if (ioVisitor.ShouldAbort())
412
break;
413
}
414
415
// Fetch next node until we find one that the visitor wants to see
416
do
417
--top;
418
while (top >= 0 && !ioVisitor.ShouldVisitNode(top));
419
}
420
while (top >= 0);
421
}
422
423
bool StaticCompoundShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const
424
{
425
JPH_PROFILE_FUNCTION();
426
427
struct Visitor : public CastRayVisitor
428
{
429
using CastRayVisitor::CastRayVisitor;
430
431
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
432
{
433
return mDistanceStack[inStackTop] < mHit.mFraction;
434
}
435
436
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
437
{
438
// Test bounds of 4 children
439
Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
440
441
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
442
return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]);
443
}
444
445
float mDistanceStack[cStackSize];
446
};
447
448
Visitor visitor(inRay, this, inSubShapeIDCreator, ioHit);
449
WalkTree(visitor);
450
return visitor.mReturnValue;
451
}
452
453
void StaticCompoundShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const
454
{
455
// Test shape filter
456
if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))
457
return;
458
459
JPH_PROFILE_FUNCTION();
460
461
struct Visitor : public CastRayVisitorCollector
462
{
463
using CastRayVisitorCollector::CastRayVisitorCollector;
464
465
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
466
{
467
return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction();
468
}
469
470
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
471
{
472
// Test bounds of 4 children
473
Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
474
475
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
476
return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);
477
}
478
479
float mDistanceStack[cStackSize];
480
};
481
482
Visitor visitor(inRay, inRayCastSettings, this, inSubShapeIDCreator, ioCollector, inShapeFilter);
483
WalkTree(visitor);
484
}
485
486
void StaticCompoundShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const
487
{
488
JPH_PROFILE_FUNCTION();
489
490
struct Visitor : public CollidePointVisitor
491
{
492
using CollidePointVisitor::CollidePointVisitor;
493
494
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
495
{
496
return true;
497
}
498
499
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
500
{
501
// Test if point overlaps with box
502
UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
503
return CountAndSortTrues(collides, ioProperties);
504
}
505
};
506
507
Visitor visitor(inPoint, this, inSubShapeIDCreator, ioCollector, inShapeFilter);
508
WalkTree(visitor);
509
}
510
511
void StaticCompoundShape::sCastShapeVsCompound(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector)
512
{
513
JPH_PROFILE_FUNCTION();
514
515
struct Visitor : public CastShapeVisitor
516
{
517
using CastShapeVisitor::CastShapeVisitor;
518
519
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
520
{
521
return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction();
522
}
523
524
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
525
{
526
// Test bounds of 4 children
527
Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
528
529
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
530
return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);
531
}
532
533
float mDistanceStack[cStackSize];
534
};
535
536
JPH_ASSERT(inShape->GetSubType() == EShapeSubType::StaticCompound);
537
const StaticCompoundShape *shape = static_cast<const StaticCompoundShape *>(inShape);
538
539
Visitor visitor(inShapeCast, inShapeCastSettings, shape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector);
540
shape->WalkTree(visitor);
541
}
542
543
void StaticCompoundShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const
544
{
545
JPH_PROFILE_FUNCTION();
546
547
// Test shape filter
548
if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))
549
return;
550
551
struct Visitor : public CollectTransformedShapesVisitor
552
{
553
using CollectTransformedShapesVisitor::CollectTransformedShapesVisitor;
554
555
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
556
{
557
return true;
558
}
559
560
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
561
{
562
// Test which nodes collide
563
UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
564
return CountAndSortTrues(collides, ioProperties);
565
}
566
};
567
568
Visitor visitor(inBox, this, inPositionCOM, inRotation, inScale, inSubShapeIDCreator, ioCollector, inShapeFilter);
569
WalkTree(visitor);
570
}
571
572
int StaticCompoundShape::GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const
573
{
574
JPH_PROFILE_FUNCTION();
575
576
GetIntersectingSubShapesVisitorSC<AABox> visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices);
577
WalkTree(visitor);
578
return visitor.GetNumResults();
579
}
580
581
int StaticCompoundShape::GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const
582
{
583
JPH_PROFILE_FUNCTION();
584
585
GetIntersectingSubShapesVisitorSC<OrientedBox> visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices);
586
WalkTree(visitor);
587
return visitor.GetNumResults();
588
}
589
590
void StaticCompoundShape::sCollideCompoundVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter)
591
{
592
JPH_PROFILE_FUNCTION();
593
594
JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::StaticCompound);
595
const StaticCompoundShape *shape1 = static_cast<const StaticCompoundShape *>(inShape1);
596
597
struct Visitor : public CollideCompoundVsShapeVisitor
598
{
599
using CollideCompoundVsShapeVisitor::CollideCompoundVsShapeVisitor;
600
601
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
602
{
603
return true;
604
}
605
606
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
607
{
608
// Test which nodes collide
609
UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
610
return CountAndSortTrues(collides, ioProperties);
611
}
612
};
613
614
Visitor visitor(shape1, inShape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter);
615
shape1->WalkTree(visitor);
616
}
617
618
void StaticCompoundShape::sCollideShapeVsCompound(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter)
619
{
620
JPH_PROFILE_FUNCTION();
621
622
struct Visitor : public CollideShapeVsCompoundVisitor
623
{
624
using CollideShapeVsCompoundVisitor::CollideShapeVsCompoundVisitor;
625
626
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
627
{
628
return true;
629
}
630
631
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
632
{
633
// Test which nodes collide
634
UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
635
return CountAndSortTrues(collides, ioProperties);
636
}
637
};
638
639
JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::StaticCompound);
640
const StaticCompoundShape *shape2 = static_cast<const StaticCompoundShape *>(inShape2);
641
642
Visitor visitor(inShape1, shape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter);
643
shape2->WalkTree(visitor);
644
}
645
646
void StaticCompoundShape::SaveBinaryState(StreamOut &inStream) const
647
{
648
CompoundShape::SaveBinaryState(inStream);
649
650
inStream.Write(mNodes);
651
}
652
653
void StaticCompoundShape::RestoreBinaryState(StreamIn &inStream)
654
{
655
CompoundShape::RestoreBinaryState(inStream);
656
657
inStream.Read(mNodes);
658
}
659
660
void StaticCompoundShape::sRegister()
661
{
662
ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::StaticCompound);
663
f.mConstruct = []() -> Shape * { return new StaticCompoundShape; };
664
f.mColor = Color::sOrange;
665
666
for (EShapeSubType s : sAllSubShapeTypes)
667
{
668
CollisionDispatch::sRegisterCollideShape(EShapeSubType::StaticCompound, s, sCollideCompoundVsShape);
669
CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::StaticCompound, sCollideShapeVsCompound);
670
CollisionDispatch::sRegisterCastShape(s, EShapeSubType::StaticCompound, sCastShapeVsCompound);
671
}
672
}
673
674
JPH_NAMESPACE_END
675
676