Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/Shape/ConvexShape.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/ConvexShape.h>
8
#include <Jolt/Physics/Collision/RayCast.h>
9
#include <Jolt/Physics/Collision/ShapeCast.h>
10
#include <Jolt/Physics/Collision/CollideShape.h>
11
#include <Jolt/Physics/Collision/CastResult.h>
12
#include <Jolt/Physics/Collision/CollidePointResult.h>
13
#include <Jolt/Physics/Collision/Shape/ScaleHelpers.h>
14
#include <Jolt/Physics/Collision/Shape/GetTrianglesContext.h>
15
#include <Jolt/Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h>
16
#include <Jolt/Physics/Collision/TransformedShape.h>
17
#include <Jolt/Physics/Collision/CollisionDispatch.h>
18
#include <Jolt/Physics/Collision/NarrowPhaseStats.h>
19
#include <Jolt/Physics/PhysicsSettings.h>
20
#include <Jolt/Core/StreamIn.h>
21
#include <Jolt/Core/StreamOut.h>
22
#include <Jolt/Geometry/EPAPenetrationDepth.h>
23
#include <Jolt/Geometry/OrientedBox.h>
24
#include <Jolt/ObjectStream/TypeDeclarations.h>
25
26
JPH_NAMESPACE_BEGIN
27
28
JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(ConvexShapeSettings)
29
{
30
JPH_ADD_BASE_CLASS(ConvexShapeSettings, ShapeSettings)
31
32
JPH_ADD_ATTRIBUTE(ConvexShapeSettings, mDensity)
33
JPH_ADD_ATTRIBUTE(ConvexShapeSettings, mMaterial)
34
}
35
36
const StaticArray<Vec3, 384> ConvexShape::sUnitSphereTriangles = []() {
37
const int level = 2;
38
39
StaticArray<Vec3, 384> verts;
40
GetTrianglesContextVertexList::sCreateHalfUnitSphereTop(verts, level);
41
GetTrianglesContextVertexList::sCreateHalfUnitSphereBottom(verts, level);
42
return verts;
43
}();
44
45
void ConvexShape::sCollideConvexVsConvex(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, [[maybe_unused]] const ShapeFilter &inShapeFilter)
46
{
47
JPH_PROFILE_FUNCTION();
48
49
// Get the shapes
50
JPH_ASSERT(inShape1->GetType() == EShapeType::Convex);
51
JPH_ASSERT(inShape2->GetType() == EShapeType::Convex);
52
const ConvexShape *shape1 = static_cast<const ConvexShape *>(inShape1);
53
const ConvexShape *shape2 = static_cast<const ConvexShape *>(inShape2);
54
55
// Get transforms
56
Mat44 inverse_transform1 = inCenterOfMassTransform1.InversedRotationTranslation();
57
Mat44 transform_2_to_1 = inverse_transform1 * inCenterOfMassTransform2;
58
59
// Get bounding boxes
60
float max_separation_distance = inCollideShapeSettings.mMaxSeparationDistance;
61
AABox shape1_bbox = shape1->GetLocalBounds().Scaled(inScale1);
62
shape1_bbox.ExpandBy(Vec3::sReplicate(max_separation_distance));
63
AABox shape2_bbox = shape2->GetLocalBounds().Scaled(inScale2);
64
65
// Check if they overlap
66
if (!OrientedBox(transform_2_to_1, shape2_bbox).Overlaps(shape1_bbox))
67
return;
68
69
// Note: As we don't remember the penetration axis from the last iteration, and it is likely that shape2 is pushed out of
70
// collision relative to shape1 by comparing their COM's, we use that as an initial penetration axis: shape2.com - shape1.com
71
// This has been seen to improve performance by approx. 1% over using a fixed axis like (1, 0, 0).
72
Vec3 penetration_axis = transform_2_to_1.GetTranslation();
73
74
// Ensure that we do not pass in a near zero penetration axis
75
if (penetration_axis.IsNearZero())
76
penetration_axis = Vec3::sAxisX();
77
78
Vec3 point1, point2;
79
EPAPenetrationDepth pen_depth;
80
EPAPenetrationDepth::EStatus status;
81
82
// Scope to limit lifetime of SupportBuffer
83
{
84
// Create support function
85
SupportBuffer buffer1_excl_cvx_radius, buffer2_excl_cvx_radius;
86
const Support *shape1_excl_cvx_radius = shape1->GetSupportFunction(ConvexShape::ESupportMode::ExcludeConvexRadius, buffer1_excl_cvx_radius, inScale1);
87
const Support *shape2_excl_cvx_radius = shape2->GetSupportFunction(ConvexShape::ESupportMode::ExcludeConvexRadius, buffer2_excl_cvx_radius, inScale2);
88
89
// Transform shape 2 in the space of shape 1
90
TransformedConvexObject transformed2_excl_cvx_radius(transform_2_to_1, *shape2_excl_cvx_radius);
91
92
// Perform GJK step
93
status = pen_depth.GetPenetrationDepthStepGJK(*shape1_excl_cvx_radius, shape1_excl_cvx_radius->GetConvexRadius() + max_separation_distance, transformed2_excl_cvx_radius, shape2_excl_cvx_radius->GetConvexRadius(), inCollideShapeSettings.mCollisionTolerance, penetration_axis, point1, point2);
94
}
95
96
// Check result of collision detection
97
switch (status)
98
{
99
case EPAPenetrationDepth::EStatus::Colliding:
100
break;
101
102
case EPAPenetrationDepth::EStatus::NotColliding:
103
return;
104
105
case EPAPenetrationDepth::EStatus::Indeterminate:
106
{
107
// Need to run expensive EPA algorithm
108
109
// We know we're overlapping at this point, so we can set the max separation distance to 0.
110
// Numerically it is possible that GJK finds that the shapes are overlapping but EPA finds that they're separated.
111
// In order to avoid this, we clamp the max separation distance to 1 so that we don't excessively inflate the shape,
112
// but we still inflate it enough to avoid the case where EPA misses the collision.
113
max_separation_distance = min(max_separation_distance, 1.0f);
114
115
// Create support function
116
SupportBuffer buffer1_incl_cvx_radius, buffer2_incl_cvx_radius;
117
const Support *shape1_incl_cvx_radius = shape1->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer1_incl_cvx_radius, inScale1);
118
const Support *shape2_incl_cvx_radius = shape2->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer2_incl_cvx_radius, inScale2);
119
120
// Add separation distance
121
AddConvexRadius shape1_add_max_separation_distance(*shape1_incl_cvx_radius, max_separation_distance);
122
123
// Transform shape 2 in the space of shape 1
124
TransformedConvexObject transformed2_incl_cvx_radius(transform_2_to_1, *shape2_incl_cvx_radius);
125
126
// Perform EPA step
127
if (!pen_depth.GetPenetrationDepthStepEPA(shape1_add_max_separation_distance, transformed2_incl_cvx_radius, inCollideShapeSettings.mPenetrationTolerance, penetration_axis, point1, point2))
128
return;
129
break;
130
}
131
}
132
133
// Check if the penetration is bigger than the early out fraction
134
float penetration_depth = (point2 - point1).Length() - max_separation_distance;
135
if (-penetration_depth >= ioCollector.GetEarlyOutFraction())
136
return;
137
138
// Correct point1 for the added separation distance
139
float penetration_axis_len = penetration_axis.Length();
140
if (penetration_axis_len > 0.0f)
141
point1 -= penetration_axis * (max_separation_distance / penetration_axis_len);
142
143
// Convert to world space
144
point1 = inCenterOfMassTransform1 * point1;
145
point2 = inCenterOfMassTransform1 * point2;
146
Vec3 penetration_axis_world = inCenterOfMassTransform1.Multiply3x3(penetration_axis);
147
148
// Create collision result
149
CollideShapeResult result(point1, point2, penetration_axis_world, penetration_depth, inSubShapeIDCreator1.GetID(), inSubShapeIDCreator2.GetID(), TransformedShape::sGetBodyID(ioCollector.GetContext()));
150
151
// Gather faces
152
if (inCollideShapeSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces)
153
{
154
// Get supporting face of shape 1
155
shape1->GetSupportingFace(SubShapeID(), -penetration_axis, inScale1, inCenterOfMassTransform1, result.mShape1Face);
156
157
// Get supporting face of shape 2
158
shape2->GetSupportingFace(SubShapeID(), transform_2_to_1.Multiply3x3Transposed(penetration_axis), inScale2, inCenterOfMassTransform2, result.mShape2Face);
159
}
160
161
// Notify the collector
162
JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;)
163
ioCollector.AddHit(result);
164
}
165
166
bool ConvexShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const
167
{
168
// Note: This is a fallback routine, most convex shapes should implement a more performant version!
169
170
JPH_PROFILE_FUNCTION();
171
172
// Create support function
173
SupportBuffer buffer;
174
const Support *support = GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sOne());
175
176
// Cast ray
177
GJKClosestPoint gjk;
178
if (gjk.CastRay(inRay.mOrigin, inRay.mDirection, cDefaultCollisionTolerance, *support, ioHit.mFraction))
179
{
180
ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID();
181
return true;
182
}
183
184
return false;
185
}
186
187
void ConvexShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const
188
{
189
// Note: This is a fallback routine, most convex shapes should implement a more performant version!
190
191
// Test shape filter
192
if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))
193
return;
194
195
// First do a normal raycast, limited to the early out fraction
196
RayCastResult hit;
197
hit.mFraction = ioCollector.GetEarlyOutFraction();
198
if (CastRay(inRay, inSubShapeIDCreator, hit))
199
{
200
// Check front side
201
if (inRayCastSettings.mTreatConvexAsSolid || hit.mFraction > 0.0f)
202
{
203
hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext());
204
ioCollector.AddHit(hit);
205
}
206
207
// Check if we want back facing hits and the collector still accepts additional hits
208
if (inRayCastSettings.mBackFaceModeConvex == EBackFaceMode::CollideWithBackFaces && !ioCollector.ShouldEarlyOut())
209
{
210
// Invert the ray, going from the early out fraction back to the fraction where we found our forward hit
211
float start_fraction = min(1.0f, ioCollector.GetEarlyOutFraction());
212
float delta_fraction = hit.mFraction - start_fraction;
213
if (delta_fraction < 0.0f)
214
{
215
RayCast inverted_ray { inRay.mOrigin + start_fraction * inRay.mDirection, delta_fraction * inRay.mDirection };
216
217
// Cast another ray
218
RayCastResult inverted_hit;
219
inverted_hit.mFraction = 1.0f;
220
if (CastRay(inverted_ray, inSubShapeIDCreator, inverted_hit)
221
&& inverted_hit.mFraction > 0.0f) // Ignore hits with fraction 0, this means the ray ends inside the object and we don't want to report it as a back facing hit
222
{
223
// Invert fraction and rescale it to the fraction of the original ray
224
inverted_hit.mFraction = hit.mFraction + (inverted_hit.mFraction - 1.0f) * delta_fraction;
225
inverted_hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext());
226
ioCollector.AddHit(inverted_hit);
227
}
228
}
229
}
230
}
231
}
232
233
void ConvexShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const
234
{
235
// Test shape filter
236
if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))
237
return;
238
239
// First test bounding box
240
if (GetLocalBounds().Contains(inPoint))
241
{
242
// Create support function
243
SupportBuffer buffer;
244
const Support *support = GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sOne());
245
246
// Create support function for point
247
PointConvexSupport point { inPoint };
248
249
// Test intersection
250
GJKClosestPoint gjk;
251
Vec3 v = inPoint;
252
if (gjk.Intersects(*support, point, cDefaultCollisionTolerance, v))
253
ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() });
254
}
255
}
256
257
void ConvexShape::sCastConvexVsConvex(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, [[maybe_unused]] const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector)
258
{
259
JPH_PROFILE_FUNCTION();
260
261
// Only supported for convex shapes
262
JPH_ASSERT(inShapeCast.mShape->GetType() == EShapeType::Convex);
263
const ConvexShape *cast_shape = static_cast<const ConvexShape *>(inShapeCast.mShape);
264
265
JPH_ASSERT(inShape->GetType() == EShapeType::Convex);
266
const ConvexShape *shape = static_cast<const ConvexShape *>(inShape);
267
268
// Determine if we want to use the actual shape or a shrunken shape with convex radius
269
ConvexShape::ESupportMode support_mode = inShapeCastSettings.mUseShrunkenShapeAndConvexRadius? ConvexShape::ESupportMode::ExcludeConvexRadius : ConvexShape::ESupportMode::Default;
270
271
// Create support function for shape to cast
272
SupportBuffer cast_buffer;
273
const Support *cast_support = cast_shape->GetSupportFunction(support_mode, cast_buffer, inShapeCast.mScale);
274
275
// Create support function for target shape
276
SupportBuffer target_buffer;
277
const Support *target_support = shape->GetSupportFunction(support_mode, target_buffer, inScale);
278
279
// Do a raycast against the result
280
EPAPenetrationDepth epa;
281
float fraction = ioCollector.GetEarlyOutFraction();
282
Vec3 contact_point_a, contact_point_b, contact_normal;
283
if (epa.CastShape(inShapeCast.mCenterOfMassStart, inShapeCast.mDirection, inShapeCastSettings.mCollisionTolerance, inShapeCastSettings.mPenetrationTolerance, *cast_support, *target_support, cast_support->GetConvexRadius(), target_support->GetConvexRadius(), inShapeCastSettings.mReturnDeepestPoint, fraction, contact_point_a, contact_point_b, contact_normal)
284
&& (inShapeCastSettings.mBackFaceModeConvex == EBackFaceMode::CollideWithBackFaces
285
|| contact_normal.Dot(inShapeCast.mDirection) > 0.0f)) // Test if backfacing
286
{
287
// Convert to world space
288
contact_point_a = inCenterOfMassTransform2 * contact_point_a;
289
contact_point_b = inCenterOfMassTransform2 * contact_point_b;
290
Vec3 contact_normal_world = inCenterOfMassTransform2.Multiply3x3(contact_normal);
291
292
ShapeCastResult result(fraction, contact_point_a, contact_point_b, contact_normal_world, false, inSubShapeIDCreator1.GetID(), inSubShapeIDCreator2.GetID(), TransformedShape::sGetBodyID(ioCollector.GetContext()));
293
294
// Early out if this hit is deeper than the collector's early out value
295
if (fraction == 0.0f && -result.mPenetrationDepth >= ioCollector.GetEarlyOutFraction())
296
return;
297
298
// Gather faces
299
if (inShapeCastSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces)
300
{
301
// Get supporting face of shape 1
302
Mat44 transform_1_to_2 = inShapeCast.mCenterOfMassStart;
303
transform_1_to_2.SetTranslation(transform_1_to_2.GetTranslation() + fraction * inShapeCast.mDirection);
304
cast_shape->GetSupportingFace(SubShapeID(), transform_1_to_2.Multiply3x3Transposed(-contact_normal), inShapeCast.mScale, inCenterOfMassTransform2 * transform_1_to_2, result.mShape1Face);
305
306
// Get supporting face of shape 2
307
shape->GetSupportingFace(SubShapeID(), contact_normal, inScale, inCenterOfMassTransform2, result.mShape2Face);
308
}
309
310
JPH_IF_TRACK_NARROWPHASE_STATS(TrackNarrowPhaseCollector track;)
311
ioCollector.AddHit(result);
312
}
313
}
314
315
class ConvexShape::CSGetTrianglesContext
316
{
317
public:
318
CSGetTrianglesContext(const ConvexShape *inShape, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) :
319
mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale)),
320
mIsInsideOut(ScaleHelpers::IsInsideOut(inScale))
321
{
322
mSupport = inShape->GetSupportFunction(ESupportMode::IncludeConvexRadius, mSupportBuffer, Vec3::sOne());
323
}
324
325
SupportBuffer mSupportBuffer;
326
const Support * mSupport;
327
Mat44 mLocalToWorld;
328
bool mIsInsideOut;
329
size_t mCurrentVertex = 0;
330
};
331
332
void ConvexShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const
333
{
334
static_assert(sizeof(CSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small");
335
JPH_ASSERT(IsAligned(&ioContext, alignof(CSGetTrianglesContext)));
336
337
new (&ioContext) CSGetTrianglesContext(this, inPositionCOM, inRotation, inScale);
338
}
339
340
int ConvexShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const
341
{
342
JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested);
343
344
CSGetTrianglesContext &context = (CSGetTrianglesContext &)ioContext;
345
346
int total_num_vertices = min(inMaxTrianglesRequested * 3, int(sUnitSphereTriangles.size() - context.mCurrentVertex));
347
348
if (context.mIsInsideOut)
349
{
350
// Store triangles flipped
351
for (const Vec3 *v = sUnitSphereTriangles.data() + context.mCurrentVertex, *v_end = v + total_num_vertices; v < v_end; v += 3)
352
{
353
(context.mLocalToWorld * context.mSupport->GetSupport(v[0])).StoreFloat3(outTriangleVertices++);
354
(context.mLocalToWorld * context.mSupport->GetSupport(v[2])).StoreFloat3(outTriangleVertices++);
355
(context.mLocalToWorld * context.mSupport->GetSupport(v[1])).StoreFloat3(outTriangleVertices++);
356
}
357
}
358
else
359
{
360
// Store triangles
361
for (const Vec3 *v = sUnitSphereTriangles.data() + context.mCurrentVertex, *v_end = v + total_num_vertices; v < v_end; v += 3)
362
{
363
(context.mLocalToWorld * context.mSupport->GetSupport(v[0])).StoreFloat3(outTriangleVertices++);
364
(context.mLocalToWorld * context.mSupport->GetSupport(v[1])).StoreFloat3(outTriangleVertices++);
365
(context.mLocalToWorld * context.mSupport->GetSupport(v[2])).StoreFloat3(outTriangleVertices++);
366
}
367
}
368
369
context.mCurrentVertex += total_num_vertices;
370
int total_num_triangles = total_num_vertices / 3;
371
372
// Store materials
373
if (outMaterials != nullptr)
374
{
375
const PhysicsMaterial *material = GetMaterial();
376
for (const PhysicsMaterial **m = outMaterials, **m_end = outMaterials + total_num_triangles; m < m_end; ++m)
377
*m = material;
378
}
379
380
return total_num_triangles;
381
}
382
383
void ConvexShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const
384
{
385
// Calculate total volume
386
Vec3 abs_scale = inScale.Abs();
387
Vec3 extent = GetLocalBounds().GetExtent() * abs_scale;
388
outTotalVolume = 8.0f * extent.GetX() * extent.GetY() * extent.GetZ();
389
390
// Points of the bounding box
391
Vec3 points[] =
392
{
393
Vec3(-1, -1, -1),
394
Vec3( 1, -1, -1),
395
Vec3(-1, 1, -1),
396
Vec3( 1, 1, -1),
397
Vec3(-1, -1, 1),
398
Vec3( 1, -1, 1),
399
Vec3(-1, 1, 1),
400
Vec3( 1, 1, 1),
401
};
402
403
// Faces of the bounding box
404
using Face = int[5];
405
#define MAKE_FACE(a, b, c, d) { a, b, c, d, ((1 << a) | (1 << b) | (1 << c) | (1 << d)) } // Last int is a bit mask that indicates which indices are used
406
Face faces[] =
407
{
408
MAKE_FACE(0, 2, 3, 1),
409
MAKE_FACE(4, 6, 2, 0),
410
MAKE_FACE(4, 5, 7, 6),
411
MAKE_FACE(1, 3, 7, 5),
412
MAKE_FACE(2, 6, 7, 3),
413
MAKE_FACE(0, 1, 5, 4),
414
};
415
416
PolyhedronSubmergedVolumeCalculator::Point *buffer = (PolyhedronSubmergedVolumeCalculator::Point *)JPH_STACK_ALLOC(8 * sizeof(PolyhedronSubmergedVolumeCalculator::Point));
417
PolyhedronSubmergedVolumeCalculator submerged_vol_calc(inCenterOfMassTransform * Mat44::sScale(extent), points, sizeof(Vec3), 8, inSurface, buffer JPH_IF_DEBUG_RENDERER(, inBaseOffset));
418
419
if (submerged_vol_calc.AreAllAbove())
420
{
421
// We're above the water
422
outSubmergedVolume = 0.0f;
423
outCenterOfBuoyancy = Vec3::sZero();
424
}
425
else if (submerged_vol_calc.AreAllBelow())
426
{
427
// We're fully submerged
428
outSubmergedVolume = outTotalVolume;
429
outCenterOfBuoyancy = inCenterOfMassTransform.GetTranslation();
430
}
431
else
432
{
433
// Calculate submerged volume
434
int reference_point_bit = 1 << submerged_vol_calc.GetReferencePointIdx();
435
for (const Face &f : faces)
436
{
437
// Test if this face includes the reference point
438
if ((f[4] & reference_point_bit) == 0)
439
{
440
// Triangulate the face (a quad)
441
submerged_vol_calc.AddFace(f[0], f[1], f[2]);
442
submerged_vol_calc.AddFace(f[0], f[2], f[3]);
443
}
444
}
445
446
submerged_vol_calc.GetResult(outSubmergedVolume, outCenterOfBuoyancy);
447
}
448
}
449
450
#ifdef JPH_DEBUG_RENDERER
451
void ConvexShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const
452
{
453
// Get the support function with convex radius
454
SupportBuffer buffer;
455
const Support *support = GetSupportFunction(ESupportMode::ExcludeConvexRadius, buffer, inScale);
456
AddConvexRadius add_convex(*support, support->GetConvexRadius());
457
458
// Draw the shape
459
DebugRenderer::GeometryRef geometry = inRenderer->CreateTriangleGeometryForConvex([&add_convex](Vec3Arg inDirection) { return add_convex.GetSupport(inDirection); });
460
AABox bounds = geometry->mBounds.Transformed(inCenterOfMassTransform);
461
float lod_scale_sq = geometry->mBounds.GetExtent().LengthSq();
462
inRenderer->DrawGeometry(inCenterOfMassTransform, bounds, lod_scale_sq, inColor, geometry);
463
464
if (inDrawSupportDirection)
465
{
466
// Iterate on all directions and draw the support point and an arrow in the direction that was sampled to test if the support points make sense
467
for (Vec3 v : Vec3::sUnitSphere)
468
{
469
Vec3 direction = 0.05f * v;
470
Vec3 pos = add_convex.GetSupport(direction);
471
RVec3 from = inCenterOfMassTransform * pos;
472
RVec3 to = inCenterOfMassTransform * (pos + direction);
473
inRenderer->DrawMarker(from, Color::sWhite, 0.001f);
474
inRenderer->DrawArrow(from, to, Color::sWhite, 0.001f);
475
}
476
}
477
}
478
479
void ConvexShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const
480
{
481
// Sample directions and map which faces belong to which directions
482
using FaceToDirection = UnorderedMap<SupportingFace, Array<Vec3>>;
483
FaceToDirection faces;
484
for (Vec3 v : Vec3::sUnitSphere)
485
{
486
Vec3 direction = 0.05f * v;
487
488
SupportingFace face;
489
GetSupportingFace(SubShapeID(), direction, inScale, Mat44::sIdentity(), face);
490
491
if (!face.empty())
492
{
493
JPH_ASSERT(face.size() >= 2, "The GetSupportingFace function should either return nothing or at least an edge");
494
faces[face].push_back(direction);
495
}
496
}
497
498
// Draw each face in a unique color and draw corresponding directions
499
int color_it = 0;
500
for (FaceToDirection::value_type &ftd : faces)
501
{
502
Color color = Color::sGetDistinctColor(color_it++);
503
504
// Create copy of face (key in map is read only)
505
SupportingFace face = ftd.first;
506
507
// Displace the face a little bit forward so it is easier to see
508
Vec3 normal = face.size() >= 3? (face[2] - face[1]).Cross(face[0] - face[1]).NormalizedOr(Vec3::sZero()) : Vec3::sZero();
509
Vec3 displacement = 0.001f * normal;
510
511
// Transform face to world space and calculate center of mass
512
Vec3 com_ls = Vec3::sZero();
513
for (Vec3 &v : face)
514
{
515
v = inCenterOfMassTransform.Multiply3x3(v + displacement);
516
com_ls += v;
517
}
518
RVec3 com = inCenterOfMassTransform.GetTranslation() + com_ls / (float)face.size();
519
520
// Draw the polygon and directions
521
inRenderer->DrawWirePolygon(RMat44::sTranslation(inCenterOfMassTransform.GetTranslation()), face, color, face.size() >= 3? 0.001f : 0.0f);
522
if (face.size() >= 3)
523
inRenderer->DrawArrow(com, com + inCenterOfMassTransform.Multiply3x3(normal), color, 0.01f);
524
for (Vec3 &v : ftd.second)
525
inRenderer->DrawArrow(com, com + inCenterOfMassTransform.Multiply3x3(-v), color, 0.001f);
526
}
527
}
528
#endif // JPH_DEBUG_RENDERER
529
530
void ConvexShape::SaveBinaryState(StreamOut &inStream) const
531
{
532
Shape::SaveBinaryState(inStream);
533
534
inStream.Write(mDensity);
535
}
536
537
void ConvexShape::RestoreBinaryState(StreamIn &inStream)
538
{
539
Shape::RestoreBinaryState(inStream);
540
541
inStream.Read(mDensity);
542
}
543
544
void ConvexShape::SaveMaterialState(PhysicsMaterialList &outMaterials) const
545
{
546
outMaterials.clear();
547
outMaterials.push_back(mMaterial);
548
}
549
550
void ConvexShape::RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials)
551
{
552
JPH_ASSERT(inNumMaterials == 1);
553
mMaterial = inMaterials[0];
554
}
555
556
void ConvexShape::sRegister()
557
{
558
for (EShapeSubType s1 : sConvexSubShapeTypes)
559
for (EShapeSubType s2 : sConvexSubShapeTypes)
560
{
561
CollisionDispatch::sRegisterCollideShape(s1, s2, sCollideConvexVsConvex);
562
CollisionDispatch::sRegisterCastShape(s1, s2, sCastConvexVsConvex);
563
}
564
}
565
566
JPH_NAMESPACE_END
567
568