Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/Shape/MeshShape.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/MeshShape.h>
8
#include <Jolt/Physics/Collision/Shape/ConvexShape.h>
9
#include <Jolt/Physics/Collision/Shape/ScaleHelpers.h>
10
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
11
#include <Jolt/Physics/Collision/RayCast.h>
12
#include <Jolt/Physics/Collision/ShapeCast.h>
13
#include <Jolt/Physics/Collision/ShapeFilter.h>
14
#include <Jolt/Physics/Collision/CastResult.h>
15
#include <Jolt/Physics/Collision/CollideConvexVsTriangles.h>
16
#include <Jolt/Physics/Collision/CollideSphereVsTriangles.h>
17
#include <Jolt/Physics/Collision/CastConvexVsTriangles.h>
18
#include <Jolt/Physics/Collision/CastSphereVsTriangles.h>
19
#include <Jolt/Physics/Collision/TransformedShape.h>
20
#include <Jolt/Physics/Collision/ActiveEdges.h>
21
#include <Jolt/Physics/Collision/CollisionDispatch.h>
22
#include <Jolt/Physics/Collision/SortReverseAndStore.h>
23
#include <Jolt/Physics/Collision/CollideSoftBodyVerticesVsTriangles.h>
24
#include <Jolt/Core/StringTools.h>
25
#include <Jolt/Core/StreamIn.h>
26
#include <Jolt/Core/StreamOut.h>
27
#include <Jolt/Core/Profiler.h>
28
#include <Jolt/Core/UnorderedMap.h>
29
#include <Jolt/Geometry/AABox4.h>
30
#include <Jolt/Geometry/RayAABox.h>
31
#include <Jolt/Geometry/Indexify.h>
32
#include <Jolt/Geometry/Plane.h>
33
#include <Jolt/Geometry/OrientedBox.h>
34
#include <Jolt/TriangleSplitter/TriangleSplitterBinning.h>
35
#include <Jolt/TriangleSplitter/TriangleSplitterMean.h>
36
#include <Jolt/AABBTree/AABBTreeBuilder.h>
37
#include <Jolt/AABBTree/AABBTreeToBuffer.h>
38
#include <Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h>
39
#include <Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h>
40
#include <Jolt/ObjectStream/TypeDeclarations.h>
41
42
JPH_NAMESPACE_BEGIN
43
44
#ifdef JPH_DEBUG_RENDERER
45
bool MeshShape::sDrawTriangleGroups = false;
46
bool MeshShape::sDrawTriangleOutlines = false;
47
#endif // JPH_DEBUG_RENDERER
48
49
JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(MeshShapeSettings)
50
{
51
JPH_ADD_BASE_CLASS(MeshShapeSettings, ShapeSettings)
52
53
JPH_ADD_ATTRIBUTE(MeshShapeSettings, mTriangleVertices)
54
JPH_ADD_ATTRIBUTE(MeshShapeSettings, mIndexedTriangles)
55
JPH_ADD_ATTRIBUTE(MeshShapeSettings, mMaterials)
56
JPH_ADD_ATTRIBUTE(MeshShapeSettings, mMaxTrianglesPerLeaf)
57
JPH_ADD_ATTRIBUTE(MeshShapeSettings, mActiveEdgeCosThresholdAngle)
58
JPH_ADD_ATTRIBUTE(MeshShapeSettings, mPerTriangleUserData)
59
JPH_ADD_ENUM_ATTRIBUTE(MeshShapeSettings, mBuildQuality)
60
}
61
62
// Codecs this mesh shape is using
63
using TriangleCodec = TriangleCodecIndexed8BitPackSOA4Flags;
64
using NodeCodec = NodeCodecQuadTreeHalfFloat;
65
66
// Get header for tree
67
static JPH_INLINE const NodeCodec::Header *sGetNodeHeader(const ByteBuffer &inTree)
68
{
69
return inTree.Get<NodeCodec::Header>(0);
70
}
71
72
// Get header for triangles
73
static JPH_INLINE const TriangleCodec::TriangleHeader *sGetTriangleHeader(const ByteBuffer &inTree)
74
{
75
return inTree.Get<TriangleCodec::TriangleHeader>(NodeCodec::HeaderSize);
76
}
77
78
MeshShapeSettings::MeshShapeSettings(const TriangleList &inTriangles, PhysicsMaterialList inMaterials) :
79
mMaterials(std::move(inMaterials))
80
{
81
Indexify(inTriangles, mTriangleVertices, mIndexedTriangles);
82
83
Sanitize();
84
}
85
86
MeshShapeSettings::MeshShapeSettings(VertexList inVertices, IndexedTriangleList inTriangles, PhysicsMaterialList inMaterials) :
87
mTriangleVertices(std::move(inVertices)),
88
mIndexedTriangles(std::move(inTriangles)),
89
mMaterials(std::move(inMaterials))
90
{
91
Sanitize();
92
}
93
94
void MeshShapeSettings::Sanitize()
95
{
96
// Remove degenerate and duplicate triangles
97
UnorderedSet<IndexedTriangle> triangles;
98
triangles.reserve(UnorderedSet<IndexedTriangle>::size_type(mIndexedTriangles.size()));
99
TriangleCodec::ValidationContext validation_ctx(mIndexedTriangles, mTriangleVertices);
100
for (int t = (int)mIndexedTriangles.size() - 1; t >= 0; --t)
101
{
102
const IndexedTriangle &tri = mIndexedTriangles[t];
103
104
if (tri.IsDegenerate(mTriangleVertices) // Degenerate triangle
105
|| validation_ctx.IsDegenerate(tri) // Triangle is degenerate in the quantized space
106
|| !triangles.insert(tri.GetLowestIndexFirst()).second) // Duplicate triangle
107
{
108
// The order of triangles doesn't matter (gets reordered while building the tree), so we can just swap the last triangle into this slot
109
mIndexedTriangles[t] = mIndexedTriangles.back();
110
mIndexedTriangles.pop_back();
111
}
112
}
113
}
114
115
ShapeSettings::ShapeResult MeshShapeSettings::Create() const
116
{
117
if (mCachedResult.IsEmpty())
118
Ref<Shape> shape = new MeshShape(*this, mCachedResult);
119
return mCachedResult;
120
}
121
122
MeshShape::MeshShape(const MeshShapeSettings &inSettings, ShapeResult &outResult) :
123
Shape(EShapeType::Mesh, EShapeSubType::Mesh, inSettings, outResult)
124
{
125
// Check if there are any triangles
126
if (inSettings.mIndexedTriangles.empty())
127
{
128
outResult.SetError("Need triangles to create a mesh shape!");
129
return;
130
}
131
132
// Check triangles
133
TriangleCodec::ValidationContext validation_ctx(inSettings.mIndexedTriangles, inSettings.mTriangleVertices);
134
for (int t = (int)inSettings.mIndexedTriangles.size() - 1; t >= 0; --t)
135
{
136
const IndexedTriangle &triangle = inSettings.mIndexedTriangles[t];
137
if (triangle.IsDegenerate(inSettings.mTriangleVertices)
138
|| validation_ctx.IsDegenerate(triangle))
139
{
140
outResult.SetError(StringFormat("Triangle %d is degenerate!", t));
141
return;
142
}
143
else
144
{
145
// Check vertex indices
146
for (uint32 idx : triangle.mIdx)
147
if (idx >= inSettings.mTriangleVertices.size())
148
{
149
outResult.SetError(StringFormat("Vertex index %u is beyond vertex list (size: %u)", idx, (uint)inSettings.mTriangleVertices.size()));
150
return;
151
}
152
}
153
}
154
155
// Copy materials
156
mMaterials = inSettings.mMaterials;
157
if (!mMaterials.empty())
158
{
159
// Validate materials
160
if (mMaterials.size() > (1 << FLAGS_MATERIAL_BITS))
161
{
162
outResult.SetError(StringFormat("Supporting max %d materials per mesh", 1 << FLAGS_MATERIAL_BITS));
163
return;
164
}
165
for (const IndexedTriangle &t : inSettings.mIndexedTriangles)
166
if (t.mMaterialIndex >= mMaterials.size())
167
{
168
outResult.SetError(StringFormat("Triangle material %u is beyond material list (size: %u)", t.mMaterialIndex, (uint)mMaterials.size()));
169
return;
170
}
171
}
172
else
173
{
174
// No materials assigned, validate that all triangles use material index 0
175
for (const IndexedTriangle &t : inSettings.mIndexedTriangles)
176
if (t.mMaterialIndex != 0)
177
{
178
outResult.SetError("No materials present, all triangles should have material index 0");
179
return;
180
}
181
}
182
183
// Check max triangles
184
if (inSettings.mMaxTrianglesPerLeaf < 1 || inSettings.mMaxTrianglesPerLeaf > MaxTrianglesPerLeaf)
185
{
186
outResult.SetError("Invalid max triangles per leaf");
187
return;
188
}
189
190
// Fill in active edge bits
191
IndexedTriangleList indexed_triangles = inSettings.mIndexedTriangles; // Copy indices since we're adding the 'active edge' flag
192
sFindActiveEdges(inSettings, indexed_triangles);
193
194
// Create triangle splitter
195
union Storage
196
{
197
Storage() { }
198
~Storage() { }
199
200
TriangleSplitterBinning mBinning;
201
TriangleSplitterMean mMean;
202
};
203
Storage storage;
204
TriangleSplitter *splitter = nullptr;
205
switch (inSettings.mBuildQuality)
206
{
207
case MeshShapeSettings::EBuildQuality::FavorRuntimePerformance:
208
splitter = new (&storage.mBinning) TriangleSplitterBinning(inSettings.mTriangleVertices, indexed_triangles);
209
break;
210
211
case MeshShapeSettings::EBuildQuality::FavorBuildSpeed:
212
splitter = new (&storage.mMean) TriangleSplitterMean(inSettings.mTriangleVertices, indexed_triangles);
213
break;
214
215
default:
216
JPH_ASSERT(false);
217
break;
218
}
219
220
// Build tree
221
AABBTreeBuilder builder(*splitter, inSettings.mMaxTrianglesPerLeaf);
222
AABBTreeBuilderStats builder_stats;
223
const AABBTreeBuilder::Node *root = builder.Build(builder_stats);
224
splitter->~TriangleSplitter();
225
226
// Convert to buffer
227
AABBTreeToBuffer<TriangleCodec, NodeCodec> buffer;
228
const char *error = nullptr;
229
if (!buffer.Convert(builder.GetTriangles(), builder.GetNodes(), inSettings.mTriangleVertices, root, inSettings.mPerTriangleUserData, error))
230
{
231
outResult.SetError(error);
232
return;
233
}
234
235
// Move data to this class
236
mTree.swap(buffer.GetBuffer());
237
238
// Check if we're not exceeding the amount of sub shape id bits
239
if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits)
240
{
241
outResult.SetError("Mesh is too big and exceeds the amount of available sub shape ID bits");
242
return;
243
}
244
245
outResult.Set(this);
246
}
247
248
void MeshShape::sFindActiveEdges(const MeshShapeSettings &inSettings, IndexedTriangleList &ioIndices)
249
{
250
// Check if we're requested to make all edges active
251
if (inSettings.mActiveEdgeCosThresholdAngle < 0.0f)
252
{
253
for (IndexedTriangle &triangle : ioIndices)
254
triangle.mMaterialIndex |= 0b111 << FLAGS_ACTIVE_EGDE_SHIFT;
255
return;
256
}
257
258
// A struct to hold the two vertex indices of an edge
259
struct Edge
260
{
261
Edge(int inIdx1, int inIdx2) : mIdx1(min(inIdx1, inIdx2)), mIdx2(max(inIdx1, inIdx2)) { }
262
263
uint GetIndexInTriangle(const IndexedTriangle &inTriangle) const
264
{
265
for (uint edge_idx = 0; edge_idx < 3; ++edge_idx)
266
{
267
Edge edge(inTriangle.mIdx[edge_idx], inTriangle.mIdx[(edge_idx + 1) % 3]);
268
if (*this == edge)
269
return edge_idx;
270
}
271
272
JPH_ASSERT(false);
273
return ~uint(0);
274
}
275
276
bool operator == (const Edge &inRHS) const
277
{
278
return mIdx1 == inRHS.mIdx1 && mIdx2 == inRHS.mIdx2;
279
}
280
281
uint64 GetHash() const
282
{
283
static_assert(sizeof(*this) == 2 * sizeof(int), "No padding expected");
284
return HashBytes(this, sizeof(*this));
285
}
286
287
int mIdx1;
288
int mIdx2;
289
};
290
291
// A struct to hold the triangles that are connected to an edge
292
struct TriangleIndices
293
{
294
uint mNumTriangles = 0;
295
uint mTriangleIndices[2];
296
};
297
298
// Build a list of edge to triangles
299
using EdgeToTriangle = UnorderedMap<Edge, TriangleIndices>;
300
EdgeToTriangle edge_to_triangle;
301
edge_to_triangle.reserve(EdgeToTriangle::size_type(ioIndices.size() * 3));
302
for (uint triangle_idx = 0; triangle_idx < ioIndices.size(); ++triangle_idx)
303
{
304
IndexedTriangle &triangle = ioIndices[triangle_idx];
305
for (uint edge_idx = 0; edge_idx < 3; ++edge_idx)
306
{
307
Edge edge(triangle.mIdx[edge_idx], triangle.mIdx[(edge_idx + 1) % 3]);
308
EdgeToTriangle::iterator edge_to_triangle_it = edge_to_triangle.try_emplace(edge, TriangleIndices()).first;
309
TriangleIndices &indices = edge_to_triangle_it->second;
310
if (indices.mNumTriangles < 2)
311
{
312
// Store index of triangle that connects to this edge
313
indices.mTriangleIndices[indices.mNumTriangles] = triangle_idx;
314
indices.mNumTriangles++;
315
}
316
else
317
{
318
// 3 or more triangles share an edge, mark this edge as active
319
uint32 mask = 1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT);
320
JPH_ASSERT((triangle.mMaterialIndex & mask) == 0);
321
triangle.mMaterialIndex |= mask;
322
}
323
}
324
}
325
326
// Walk over all edges and determine which ones are active
327
for (const EdgeToTriangle::value_type &edge : edge_to_triangle)
328
{
329
uint num_active = 0;
330
if (edge.second.mNumTriangles == 1)
331
{
332
// Edge is not shared, it is an active edge
333
num_active = 1;
334
}
335
else if (edge.second.mNumTriangles == 2)
336
{
337
// Simple shared edge, determine if edge is active based on the two adjacent triangles
338
const IndexedTriangle &triangle1 = ioIndices[edge.second.mTriangleIndices[0]];
339
const IndexedTriangle &triangle2 = ioIndices[edge.second.mTriangleIndices[1]];
340
341
// Find which edge this is for both triangles
342
uint edge_idx1 = edge.first.GetIndexInTriangle(triangle1);
343
uint edge_idx2 = edge.first.GetIndexInTriangle(triangle2);
344
345
// Construct a plane for triangle 1 (e1 = edge vertex 1, e2 = edge vertex 2, op = opposing vertex)
346
Vec3 triangle1_e1 = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[edge_idx1]]);
347
Vec3 triangle1_e2 = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[(edge_idx1 + 1) % 3]]);
348
Vec3 triangle1_op = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[(edge_idx1 + 2) % 3]]);
349
Plane triangle1_plane = Plane::sFromPointsCCW(triangle1_e1, triangle1_e2, triangle1_op);
350
351
// Construct a plane for triangle 2
352
Vec3 triangle2_e1 = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[edge_idx2]]);
353
Vec3 triangle2_e2 = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[(edge_idx2 + 1) % 3]]);
354
Vec3 triangle2_op = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[(edge_idx2 + 2) % 3]]);
355
Plane triangle2_plane = Plane::sFromPointsCCW(triangle2_e1, triangle2_e2, triangle2_op);
356
357
// Determine if the edge is active
358
num_active = ActiveEdges::IsEdgeActive(triangle1_plane.GetNormal(), triangle2_plane.GetNormal(), triangle1_e2 - triangle1_e1, inSettings.mActiveEdgeCosThresholdAngle)? 2 : 0;
359
}
360
else
361
{
362
// More edges incoming, we've already marked all edges beyond the 2nd as active
363
num_active = 2;
364
}
365
366
// Mark edges of all original triangles active
367
for (uint i = 0; i < num_active; ++i)
368
{
369
uint triangle_idx = edge.second.mTriangleIndices[i];
370
IndexedTriangle &triangle = ioIndices[triangle_idx];
371
uint edge_idx = edge.first.GetIndexInTriangle(triangle);
372
uint32 mask = 1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT);
373
JPH_ASSERT((triangle.mMaterialIndex & mask) == 0);
374
triangle.mMaterialIndex |= mask;
375
}
376
}
377
}
378
379
MassProperties MeshShape::GetMassProperties() const
380
{
381
// We cannot calculate the volume for an arbitrary mesh, so we return invalid mass properties.
382
// If you want your mesh to be dynamic, then you should provide the mass properties yourself when
383
// creating a Body:
384
//
385
// BodyCreationSettings::mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;
386
// BodyCreationSettings::mMassPropertiesOverride.SetMassAndInertiaOfSolidBox(Vec3::sOne(), 1000.0f);
387
//
388
// Note that for a mesh shape to simulate properly, it is best if the mesh is manifold
389
// (i.e. closed, all edges shared by only two triangles, consistent winding order).
390
return MassProperties();
391
}
392
393
void MeshShape::DecodeSubShapeID(const SubShapeID &inSubShapeID, const void *&outTriangleBlock, uint32 &outTriangleIndex) const
394
{
395
// Get block
396
SubShapeID triangle_idx_subshape_id;
397
uint32 block_id = inSubShapeID.PopID(NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree)), triangle_idx_subshape_id);
398
outTriangleBlock = NodeCodec::DecodingContext::sGetTriangleBlockStart(&mTree[0], block_id);
399
400
// Fetch the triangle index
401
SubShapeID remainder;
402
outTriangleIndex = triangle_idx_subshape_id.PopID(NumTriangleBits, remainder);
403
JPH_ASSERT(remainder.IsEmpty(), "Invalid subshape ID");
404
}
405
406
uint MeshShape::GetMaterialIndex(const SubShapeID &inSubShapeID) const
407
{
408
// Decode ID
409
const void *block_start;
410
uint32 triangle_idx;
411
DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);
412
413
// Fetch the flags
414
uint8 flags = TriangleCodec::DecodingContext::sGetFlags(block_start, triangle_idx);
415
return flags & FLAGS_MATERIAL_MASK;
416
}
417
418
const PhysicsMaterial *MeshShape::GetMaterial(const SubShapeID &inSubShapeID) const
419
{
420
// Return the default material if there are no materials on this shape
421
if (mMaterials.empty())
422
return PhysicsMaterial::sDefault;
423
424
return mMaterials[GetMaterialIndex(inSubShapeID)];
425
}
426
427
Vec3 MeshShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const
428
{
429
// Decode ID
430
const void *block_start;
431
uint32 triangle_idx;
432
DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);
433
434
// Decode triangle
435
Vec3 v1, v2, v3;
436
const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));
437
triangle_ctx.GetTriangle(block_start, triangle_idx, v1, v2, v3);
438
439
// Calculate normal
440
return (v3 - v2).Cross(v1 - v2).Normalized();
441
}
442
443
void MeshShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const
444
{
445
// Decode ID
446
const void *block_start;
447
uint32 triangle_idx;
448
DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);
449
450
// Decode triangle
451
const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));
452
outVertices.resize(3);
453
triangle_ctx.GetTriangle(block_start, triangle_idx, outVertices[0], outVertices[1], outVertices[2]);
454
455
// Flip triangle if scaled inside out
456
if (ScaleHelpers::IsInsideOut(inScale))
457
std::swap(outVertices[1], outVertices[2]);
458
459
// Calculate transform with scale
460
Mat44 transform = inCenterOfMassTransform.PreScaled(inScale);
461
462
// Transform to world space
463
for (Vec3 &v : outVertices)
464
v = transform * v;
465
}
466
467
AABox MeshShape::GetLocalBounds() const
468
{
469
const NodeCodec::Header *header = sGetNodeHeader(mTree);
470
return AABox(Vec3::sLoadFloat3Unsafe(header->mRootBoundsMin), Vec3::sLoadFloat3Unsafe(header->mRootBoundsMax));
471
}
472
473
uint MeshShape::GetSubShapeIDBitsRecursive() const
474
{
475
return NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree)) + NumTriangleBits;
476
}
477
478
template <class Visitor>
479
JPH_INLINE void MeshShape::WalkTree(Visitor &ioVisitor) const
480
{
481
const NodeCodec::Header *header = sGetNodeHeader(mTree);
482
NodeCodec::DecodingContext node_ctx(header);
483
484
const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));
485
const uint8 *buffer_start = &mTree[0];
486
node_ctx.WalkTree(buffer_start, triangle_ctx, ioVisitor);
487
}
488
489
template <class Visitor>
490
JPH_INLINE void MeshShape::WalkTreePerTriangle(const SubShapeIDCreator &inSubShapeIDCreator2, Visitor &ioVisitor) const
491
{
492
struct ChainedVisitor
493
{
494
JPH_INLINE ChainedVisitor(Visitor &ioVisitor, const SubShapeIDCreator &inSubShapeIDCreator2, uint inTriangleBlockIDBits) :
495
mVisitor(ioVisitor),
496
mSubShapeIDCreator2(inSubShapeIDCreator2),
497
mTriangleBlockIDBits(inTriangleBlockIDBits)
498
{
499
}
500
501
JPH_INLINE bool ShouldAbort() const
502
{
503
return mVisitor.ShouldAbort();
504
}
505
506
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
507
{
508
return mVisitor.ShouldVisitNode(inStackTop);
509
}
510
511
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
512
{
513
return mVisitor.VisitNodes(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, ioProperties, inStackTop);
514
}
515
516
JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID)
517
{
518
// Create ID for triangle block
519
SubShapeIDCreator block_sub_shape_id = mSubShapeIDCreator2.PushID(inTriangleBlockID, mTriangleBlockIDBits);
520
521
// Decode vertices and flags
522
JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);
523
Vec3 vertices[MaxTrianglesPerLeaf * 3];
524
uint8 flags[MaxTrianglesPerLeaf];
525
ioContext.Unpack(inTriangles, inNumTriangles, vertices, flags);
526
527
int triangle_idx = 0;
528
for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, triangle_idx++)
529
{
530
// Determine active edges
531
uint8 active_edges = (flags[triangle_idx] >> FLAGS_ACTIVE_EGDE_SHIFT) & FLAGS_ACTIVE_EDGE_MASK;
532
533
// Create ID for triangle
534
SubShapeIDCreator triangle_sub_shape_id = block_sub_shape_id.PushID(triangle_idx, NumTriangleBits);
535
536
mVisitor.VisitTriangle(v[0], v[1], v[2], active_edges, triangle_sub_shape_id.GetID());
537
538
// Check if we should early out now
539
if (mVisitor.ShouldAbort())
540
break;
541
}
542
}
543
544
Visitor & mVisitor;
545
SubShapeIDCreator mSubShapeIDCreator2;
546
uint mTriangleBlockIDBits;
547
};
548
549
ChainedVisitor visitor(ioVisitor, inSubShapeIDCreator2, NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree)));
550
WalkTree(visitor);
551
}
552
553
#ifdef JPH_DEBUG_RENDERER
554
void MeshShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const
555
{
556
// Reset the batch if we switch coloring mode
557
if (mCachedTrianglesColoredPerGroup != sDrawTriangleGroups || mCachedUseMaterialColors != inUseMaterialColors)
558
{
559
mGeometry = nullptr;
560
mCachedTrianglesColoredPerGroup = sDrawTriangleGroups;
561
mCachedUseMaterialColors = inUseMaterialColors;
562
}
563
564
if (mGeometry == nullptr)
565
{
566
struct Visitor
567
{
568
JPH_INLINE bool ShouldAbort() const
569
{
570
return false;
571
}
572
573
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
574
{
575
return true;
576
}
577
578
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
579
{
580
UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ));
581
return CountAndSortTrues(valid, ioProperties);
582
}
583
584
JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID)
585
{
586
JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);
587
Vec3 vertices[MaxTrianglesPerLeaf * 3];
588
ioContext.Unpack(inTriangles, inNumTriangles, vertices);
589
590
if (mDrawTriangleGroups || !mUseMaterialColors || mMaterials.empty())
591
{
592
// Single color for mesh
593
Color color = mDrawTriangleGroups? Color::sGetDistinctColor(mColorIdx++) : (mUseMaterialColors? PhysicsMaterial::sDefault->GetDebugColor() : Color::sWhite);
594
for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3)
595
mTriangles.push_back({ v[0], v[1], v[2], color });
596
}
597
else
598
{
599
// Per triangle color
600
uint8 flags[MaxTrianglesPerLeaf];
601
TriangleCodec::DecodingContext::sGetFlags(inTriangles, inNumTriangles, flags);
602
603
const uint8 *f = flags;
604
for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, f++)
605
mTriangles.push_back({ v[0], v[1], v[2], mMaterials[*f & FLAGS_MATERIAL_MASK]->GetDebugColor() });
606
}
607
}
608
609
Array<DebugRenderer::Triangle> & mTriangles;
610
const PhysicsMaterialList & mMaterials;
611
bool mUseMaterialColors;
612
bool mDrawTriangleGroups;
613
int mColorIdx = 0;
614
};
615
616
Array<DebugRenderer::Triangle> triangles;
617
Visitor visitor { triangles, mMaterials, mCachedUseMaterialColors, mCachedTrianglesColoredPerGroup };
618
WalkTree(visitor);
619
mGeometry = new DebugRenderer::Geometry(inRenderer->CreateTriangleBatch(triangles), GetLocalBounds());
620
}
621
622
// Test if the shape is scaled inside out
623
DebugRenderer::ECullMode cull_mode = ScaleHelpers::IsInsideOut(inScale)? DebugRenderer::ECullMode::CullFrontFace : DebugRenderer::ECullMode::CullBackFace;
624
625
// Determine the draw mode
626
DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid;
627
628
// Draw the geometry
629
inRenderer->DrawGeometry(inCenterOfMassTransform * Mat44::sScale(inScale), inColor, mGeometry, cull_mode, DebugRenderer::ECastShadow::On, draw_mode);
630
631
if (sDrawTriangleOutlines)
632
{
633
struct Visitor
634
{
635
JPH_INLINE Visitor(DebugRenderer *inRenderer, RMat44Arg inTransform) :
636
mRenderer(inRenderer),
637
mTransform(inTransform)
638
{
639
}
640
641
JPH_INLINE bool ShouldAbort() const
642
{
643
return false;
644
}
645
646
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
647
{
648
return true;
649
}
650
651
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
652
{
653
UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ));
654
return CountAndSortTrues(valid, ioProperties);
655
}
656
657
JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID)
658
{
659
// Decode vertices and flags
660
JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);
661
Vec3 vertices[MaxTrianglesPerLeaf * 3];
662
uint8 flags[MaxTrianglesPerLeaf];
663
ioContext.Unpack(inTriangles, inNumTriangles, vertices, flags);
664
665
// Loop through triangles
666
const uint8 *f = flags;
667
for (Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, ++f)
668
{
669
// Loop through edges
670
for (uint edge_idx = 0; edge_idx < 3; ++edge_idx)
671
{
672
RVec3 v1 = mTransform * v[edge_idx];
673
RVec3 v2 = mTransform * v[(edge_idx + 1) % 3];
674
675
// Draw active edge as a green arrow, other edges as grey
676
if (*f & (1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT)))
677
mRenderer->DrawArrow(v1, v2, Color::sGreen, 0.01f);
678
else
679
mRenderer->DrawLine(v1, v2, Color::sGrey);
680
}
681
}
682
}
683
684
DebugRenderer * mRenderer;
685
RMat44 mTransform;
686
};
687
688
Visitor visitor { inRenderer, inCenterOfMassTransform.PreScaled(inScale) };
689
WalkTree(visitor);
690
}
691
}
692
#endif // JPH_DEBUG_RENDERER
693
694
bool MeshShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const
695
{
696
JPH_PROFILE_FUNCTION();
697
698
struct Visitor
699
{
700
JPH_INLINE explicit Visitor(RayCastResult &ioHit) :
701
mHit(ioHit)
702
{
703
}
704
705
JPH_INLINE bool ShouldAbort() const
706
{
707
return mHit.mFraction <= 0.0f;
708
}
709
710
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
711
{
712
return mDistanceStack[inStackTop] < mHit.mFraction;
713
}
714
715
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
716
{
717
// Test bounds of 4 children
718
Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
719
720
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
721
return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]);
722
}
723
724
JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID)
725
{
726
// Test against triangles
727
uint32 triangle_idx;
728
float fraction = ioContext.TestRay(mRayOrigin, mRayDirection, inTriangles, inNumTriangles, mHit.mFraction, triangle_idx);
729
if (fraction < mHit.mFraction)
730
{
731
mHit.mFraction = fraction;
732
mHit.mSubShapeID2 = mSubShapeIDCreator.PushID(inTriangleBlockID, mTriangleBlockIDBits).PushID(triangle_idx, NumTriangleBits).GetID();
733
mReturnValue = true;
734
}
735
}
736
737
RayCastResult & mHit;
738
Vec3 mRayOrigin;
739
Vec3 mRayDirection;
740
RayInvDirection mRayInvDirection;
741
uint mTriangleBlockIDBits;
742
SubShapeIDCreator mSubShapeIDCreator;
743
bool mReturnValue = false;
744
float mDistanceStack[NodeCodec::StackSize];
745
};
746
747
Visitor visitor(ioHit);
748
visitor.mRayOrigin = inRay.mOrigin;
749
visitor.mRayDirection = inRay.mDirection;
750
visitor.mRayInvDirection.Set(inRay.mDirection);
751
visitor.mTriangleBlockIDBits = NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree));
752
visitor.mSubShapeIDCreator = inSubShapeIDCreator;
753
WalkTree(visitor);
754
755
return visitor.mReturnValue;
756
}
757
758
void MeshShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const
759
{
760
JPH_PROFILE_FUNCTION();
761
762
// Test shape filter
763
if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))
764
return;
765
766
struct Visitor
767
{
768
JPH_INLINE explicit Visitor(CastRayCollector &ioCollector) :
769
mCollector(ioCollector)
770
{
771
}
772
773
JPH_INLINE bool ShouldAbort() const
774
{
775
return mCollector.ShouldEarlyOut();
776
}
777
778
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
779
{
780
return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction();
781
}
782
783
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
784
{
785
// Test bounds of 4 children
786
Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
787
788
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
789
return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);
790
}
791
792
JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, [[maybe_unused]] uint8 inActiveEdges, SubShapeID inSubShapeID2)
793
{
794
// Back facing check
795
if (mBackFaceMode == EBackFaceMode::IgnoreBackFaces && (inV2 - inV0).Cross(inV1 - inV0).Dot(mRayDirection) < 0)
796
return;
797
798
// Check the triangle
799
float fraction = RayTriangle(mRayOrigin, mRayDirection, inV0, inV1, inV2);
800
if (fraction < mCollector.GetEarlyOutFraction())
801
{
802
RayCastResult hit;
803
hit.mBodyID = TransformedShape::sGetBodyID(mCollector.GetContext());
804
hit.mFraction = fraction;
805
hit.mSubShapeID2 = inSubShapeID2;
806
mCollector.AddHit(hit);
807
}
808
}
809
810
CastRayCollector & mCollector;
811
Vec3 mRayOrigin;
812
Vec3 mRayDirection;
813
RayInvDirection mRayInvDirection;
814
EBackFaceMode mBackFaceMode;
815
float mDistanceStack[NodeCodec::StackSize];
816
};
817
818
Visitor visitor(ioCollector);
819
visitor.mBackFaceMode = inRayCastSettings.mBackFaceModeTriangles;
820
visitor.mRayOrigin = inRay.mOrigin;
821
visitor.mRayDirection = inRay.mDirection;
822
visitor.mRayInvDirection.Set(inRay.mDirection);
823
WalkTreePerTriangle(inSubShapeIDCreator, visitor);
824
}
825
826
void MeshShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const
827
{
828
sCollidePointUsingRayCast(*this, inPoint, inSubShapeIDCreator, ioCollector, inShapeFilter);
829
}
830
831
void MeshShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const CollideSoftBodyVertexIterator &inVertices, uint inNumVertices, int inCollidingShapeIndex) const
832
{
833
JPH_PROFILE_FUNCTION();
834
835
struct Visitor : public CollideSoftBodyVerticesVsTriangles
836
{
837
using CollideSoftBodyVerticesVsTriangles::CollideSoftBodyVerticesVsTriangles;
838
839
JPH_INLINE bool ShouldAbort() const
840
{
841
return false;
842
}
843
844
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
845
{
846
return mDistanceStack[inStackTop] < mClosestDistanceSq;
847
}
848
849
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
850
{
851
// Get distance to vertex
852
Vec4 dist_sq = AABox4DistanceSqToPoint(mLocalPosition, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
853
854
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
855
return SortReverseAndStore(dist_sq, mClosestDistanceSq, ioProperties, &mDistanceStack[inStackTop]);
856
}
857
858
JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, [[maybe_unused]] uint8 inActiveEdges, [[maybe_unused]] SubShapeID inSubShapeID2)
859
{
860
ProcessTriangle(inV0, inV1, inV2);
861
}
862
863
float mDistanceStack[NodeCodec::StackSize];
864
};
865
866
Visitor visitor(inCenterOfMassTransform, inScale);
867
868
for (CollideSoftBodyVertexIterator v = inVertices, sbv_end = inVertices + inNumVertices; v != sbv_end; ++v)
869
if (v.GetInvMass() > 0.0f)
870
{
871
visitor.StartVertex(v);
872
WalkTreePerTriangle(SubShapeIDCreator(), visitor);
873
visitor.FinishVertex(v, inCollidingShapeIndex);
874
}
875
}
876
877
void MeshShape::sCastConvexVsMesh(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)
878
{
879
JPH_PROFILE_FUNCTION();
880
881
struct Visitor : public CastConvexVsTriangles
882
{
883
using CastConvexVsTriangles::CastConvexVsTriangles;
884
885
JPH_INLINE bool ShouldAbort() const
886
{
887
return mCollector.ShouldEarlyOut();
888
}
889
890
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
891
{
892
return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction();
893
}
894
895
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
896
{
897
// Scale the bounding boxes of this node
898
Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;
899
AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
900
901
// Enlarge them by the casted shape's box extents
902
AABox4EnlargeWithExtent(mBoxExtent, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
903
904
// Test bounds of 4 children
905
Vec4 distance = RayAABox4(mBoxCenter, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
906
907
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
908
return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);
909
}
910
911
JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)
912
{
913
Cast(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);
914
}
915
916
RayInvDirection mInvDirection;
917
Vec3 mBoxCenter;
918
Vec3 mBoxExtent;
919
float mDistanceStack[NodeCodec::StackSize];
920
};
921
922
JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Mesh);
923
const MeshShape *shape = static_cast<const MeshShape *>(inShape);
924
925
Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector);
926
visitor.mInvDirection.Set(inShapeCast.mDirection);
927
visitor.mBoxCenter = inShapeCast.mShapeWorldBounds.GetCenter();
928
visitor.mBoxExtent = inShapeCast.mShapeWorldBounds.GetExtent();
929
shape->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);
930
}
931
932
void MeshShape::sCastSphereVsMesh(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)
933
{
934
JPH_PROFILE_FUNCTION();
935
936
struct Visitor : public CastSphereVsTriangles
937
{
938
using CastSphereVsTriangles::CastSphereVsTriangles;
939
940
JPH_INLINE bool ShouldAbort() const
941
{
942
return mCollector.ShouldEarlyOut();
943
}
944
945
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
946
{
947
return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction();
948
}
949
950
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
951
{
952
// Scale the bounding boxes of this node
953
Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;
954
AABox4Scale(mScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
955
956
// Enlarge them by the radius of the sphere
957
AABox4EnlargeWithExtent(Vec3::sReplicate(mRadius), bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
958
959
// Test bounds of 4 children
960
Vec4 distance = RayAABox4(mStart, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
961
962
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
963
return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);
964
}
965
966
JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)
967
{
968
Cast(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);
969
}
970
971
RayInvDirection mInvDirection;
972
float mDistanceStack[NodeCodec::StackSize];
973
};
974
975
JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Mesh);
976
const MeshShape *shape = static_cast<const MeshShape *>(inShape);
977
978
Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector);
979
visitor.mInvDirection.Set(inShapeCast.mDirection);
980
shape->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);
981
}
982
983
struct MeshShape::MSGetTrianglesContext
984
{
985
JPH_INLINE MSGetTrianglesContext(const MeshShape *inShape, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) :
986
mDecodeCtx(sGetNodeHeader(inShape->mTree)),
987
mShape(inShape),
988
mLocalBox(Mat44::sInverseRotationTranslation(inRotation, inPositionCOM), inBox),
989
mMeshScale(inScale),
990
mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale)),
991
mIsInsideOut(ScaleHelpers::IsInsideOut(inScale))
992
{
993
}
994
995
JPH_INLINE bool ShouldAbort() const
996
{
997
return mShouldAbort;
998
}
999
1000
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
1001
{
1002
return true;
1003
}
1004
1005
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
1006
{
1007
// Scale the bounding boxes of this node
1008
Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;
1009
AABox4Scale(mMeshScale, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
1010
1011
// Test which nodes collide
1012
UVec4 collides = AABox4VsBox(mLocalBox, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
1013
return CountAndSortTrues(collides, ioProperties);
1014
}
1015
1016
JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID)
1017
{
1018
// When the buffer is full and we cannot process the triangles, abort the tree walk. The next time GetTrianglesNext is called we will continue here.
1019
if (mNumTrianglesFound + inNumTriangles > mMaxTrianglesRequested)
1020
{
1021
mShouldAbort = true;
1022
return;
1023
}
1024
1025
// Decode vertices
1026
JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);
1027
Vec3 vertices[MaxTrianglesPerLeaf * 3];
1028
ioContext.Unpack(inTriangles, inNumTriangles, vertices);
1029
1030
// Store vertices as Float3
1031
if (mIsInsideOut)
1032
{
1033
// Scaled inside out, flip the triangles
1034
for (const Vec3 *v = vertices, *v_end = v + 3 * inNumTriangles; v < v_end; v += 3)
1035
{
1036
(mLocalToWorld * v[0]).StoreFloat3(mTriangleVertices++);
1037
(mLocalToWorld * v[2]).StoreFloat3(mTriangleVertices++);
1038
(mLocalToWorld * v[1]).StoreFloat3(mTriangleVertices++);
1039
}
1040
}
1041
else
1042
{
1043
// Normal scale
1044
for (const Vec3 *v = vertices, *v_end = v + 3 * inNumTriangles; v < v_end; ++v)
1045
(mLocalToWorld * *v).StoreFloat3(mTriangleVertices++);
1046
}
1047
1048
if (mMaterials != nullptr)
1049
{
1050
if (mShape->mMaterials.empty())
1051
{
1052
// No materials, output default
1053
const PhysicsMaterial *default_material = PhysicsMaterial::sDefault;
1054
for (int m = 0; m < inNumTriangles; ++m)
1055
*mMaterials++ = default_material;
1056
}
1057
else
1058
{
1059
// Decode triangle flags
1060
uint8 flags[MaxTrianglesPerLeaf];
1061
TriangleCodec::DecodingContext::sGetFlags(inTriangles, inNumTriangles, flags);
1062
1063
// Store materials
1064
for (const uint8 *f = flags, *f_end = f + inNumTriangles; f < f_end; ++f)
1065
*mMaterials++ = mShape->mMaterials[*f & FLAGS_MATERIAL_MASK].GetPtr();
1066
}
1067
}
1068
1069
// Accumulate triangles found
1070
mNumTrianglesFound += inNumTriangles;
1071
}
1072
1073
NodeCodec::DecodingContext mDecodeCtx;
1074
const MeshShape * mShape;
1075
OrientedBox mLocalBox;
1076
Vec3 mMeshScale;
1077
Mat44 mLocalToWorld;
1078
int mMaxTrianglesRequested;
1079
Float3 * mTriangleVertices;
1080
int mNumTrianglesFound;
1081
const PhysicsMaterial ** mMaterials;
1082
bool mShouldAbort;
1083
bool mIsInsideOut;
1084
};
1085
1086
void MeshShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const
1087
{
1088
static_assert(sizeof(MSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small");
1089
JPH_ASSERT(IsAligned(&ioContext, alignof(MSGetTrianglesContext)));
1090
1091
new (&ioContext) MSGetTrianglesContext(this, inBox, inPositionCOM, inRotation, inScale);
1092
}
1093
1094
int MeshShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const
1095
{
1096
static_assert(cGetTrianglesMinTrianglesRequested >= MaxTrianglesPerLeaf, "cGetTrianglesMinTrianglesRequested is too small");
1097
JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested);
1098
1099
// Check if we're done
1100
MSGetTrianglesContext &context = (MSGetTrianglesContext &)ioContext;
1101
if (context.mDecodeCtx.IsDoneWalking())
1102
return 0;
1103
1104
// Store parameters on context
1105
context.mMaxTrianglesRequested = inMaxTrianglesRequested;
1106
context.mTriangleVertices = outTriangleVertices;
1107
context.mMaterials = outMaterials;
1108
context.mShouldAbort = false; // Reset the abort flag
1109
context.mNumTrianglesFound = 0;
1110
1111
// Continue (or start) walking the tree
1112
const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));
1113
const uint8 *buffer_start = &mTree[0];
1114
context.mDecodeCtx.WalkTree(buffer_start, triangle_ctx, context);
1115
return context.mNumTrianglesFound;
1116
}
1117
1118
void MeshShape::sCollideConvexVsMesh(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)
1119
{
1120
JPH_PROFILE_FUNCTION();
1121
1122
// Get the shapes
1123
JPH_ASSERT(inShape1->GetType() == EShapeType::Convex);
1124
JPH_ASSERT(inShape2->GetType() == EShapeType::Mesh);
1125
const ConvexShape *shape1 = static_cast<const ConvexShape *>(inShape1);
1126
const MeshShape *shape2 = static_cast<const MeshShape *>(inShape2);
1127
1128
struct Visitor : public CollideConvexVsTriangles
1129
{
1130
using CollideConvexVsTriangles::CollideConvexVsTriangles;
1131
1132
JPH_INLINE bool ShouldAbort() const
1133
{
1134
return mCollector.ShouldEarlyOut();
1135
}
1136
1137
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
1138
{
1139
return true;
1140
}
1141
1142
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
1143
{
1144
// Scale the bounding boxes of this node
1145
Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;
1146
AABox4Scale(mScale2, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
1147
1148
// Test which nodes collide
1149
UVec4 collides = AABox4VsBox(mBoundsOf1InSpaceOf2, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
1150
return CountAndSortTrues(collides, ioProperties);
1151
}
1152
1153
JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)
1154
{
1155
Collide(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);
1156
}
1157
};
1158
1159
Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector);
1160
shape2->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);
1161
}
1162
1163
void MeshShape::sCollideSphereVsMesh(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)
1164
{
1165
JPH_PROFILE_FUNCTION();
1166
1167
// Get the shapes
1168
JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Sphere);
1169
JPH_ASSERT(inShape2->GetType() == EShapeType::Mesh);
1170
const SphereShape *shape1 = static_cast<const SphereShape *>(inShape1);
1171
const MeshShape *shape2 = static_cast<const MeshShape *>(inShape2);
1172
1173
struct Visitor : public CollideSphereVsTriangles
1174
{
1175
using CollideSphereVsTriangles::CollideSphereVsTriangles;
1176
1177
JPH_INLINE bool ShouldAbort() const
1178
{
1179
return mCollector.ShouldEarlyOut();
1180
}
1181
1182
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
1183
{
1184
return true;
1185
}
1186
1187
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
1188
{
1189
// Scale the bounding boxes of this node
1190
Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;
1191
AABox4Scale(mScale2, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
1192
1193
// Test which nodes collide
1194
UVec4 collides = AABox4VsSphere(mSphereCenterIn2, mRadiusPlusMaxSeparationSq, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
1195
return CountAndSortTrues(collides, ioProperties);
1196
}
1197
1198
JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)
1199
{
1200
Collide(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);
1201
}
1202
};
1203
1204
Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector);
1205
shape2->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);
1206
}
1207
1208
void MeshShape::SaveBinaryState(StreamOut &inStream) const
1209
{
1210
Shape::SaveBinaryState(inStream);
1211
1212
inStream.Write(static_cast<const ByteBufferVector &>(mTree)); // Make sure we use the Array<> overload
1213
}
1214
1215
void MeshShape::RestoreBinaryState(StreamIn &inStream)
1216
{
1217
Shape::RestoreBinaryState(inStream);
1218
1219
inStream.Read(static_cast<ByteBufferVector &>(mTree)); // Make sure we use the Array<> overload
1220
}
1221
1222
void MeshShape::SaveMaterialState(PhysicsMaterialList &outMaterials) const
1223
{
1224
outMaterials = mMaterials;
1225
}
1226
1227
void MeshShape::RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials)
1228
{
1229
mMaterials.assign(inMaterials, inMaterials + inNumMaterials);
1230
}
1231
1232
Shape::Stats MeshShape::GetStats() const
1233
{
1234
// Walk the tree to count the triangles
1235
struct Visitor
1236
{
1237
JPH_INLINE bool ShouldAbort() const
1238
{
1239
return false;
1240
}
1241
1242
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
1243
{
1244
return true;
1245
}
1246
1247
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
1248
{
1249
// Visit all valid children
1250
UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ));
1251
return CountAndSortTrues(valid, ioProperties);
1252
}
1253
1254
JPH_INLINE void VisitTriangles([[maybe_unused]] const TriangleCodec::DecodingContext &ioContext, [[maybe_unused]] const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID)
1255
{
1256
mNumTriangles += inNumTriangles;
1257
}
1258
1259
uint mNumTriangles = 0;
1260
};
1261
1262
Visitor visitor;
1263
WalkTree(visitor);
1264
1265
return Stats(sizeof(*this) + mMaterials.size() * sizeof(Ref<PhysicsMaterial>) + mTree.size() * sizeof(uint8), visitor.mNumTriangles);
1266
}
1267
1268
uint32 MeshShape::GetTriangleUserData(const SubShapeID &inSubShapeID) const
1269
{
1270
// Decode ID
1271
const void *block_start;
1272
uint32 triangle_idx;
1273
DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);
1274
1275
// Decode triangle
1276
const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));
1277
return triangle_ctx.GetUserData(block_start, triangle_idx);
1278
}
1279
1280
void MeshShape::sRegister()
1281
{
1282
ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Mesh);
1283
f.mConstruct = []() -> Shape * { return new MeshShape; };
1284
f.mColor = Color::sRed;
1285
1286
for (EShapeSubType s : sConvexSubShapeTypes)
1287
{
1288
CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::Mesh, sCollideConvexVsMesh);
1289
CollisionDispatch::sRegisterCastShape(s, EShapeSubType::Mesh, sCastConvexVsMesh);
1290
1291
CollisionDispatch::sRegisterCastShape(EShapeSubType::Mesh, s, CollisionDispatch::sReversedCastShape);
1292
CollisionDispatch::sRegisterCollideShape(EShapeSubType::Mesh, s, CollisionDispatch::sReversedCollideShape);
1293
}
1294
1295
// Specialized collision functions
1296
CollisionDispatch::sRegisterCollideShape(EShapeSubType::Sphere, EShapeSubType::Mesh, sCollideSphereVsMesh);
1297
CollisionDispatch::sRegisterCastShape(EShapeSubType::Sphere, EShapeSubType::Mesh, sCastSphereVsMesh);
1298
}
1299
1300
JPH_NAMESPACE_END
1301
1302