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
21420 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
indices.mNumTriangles = 3; // Indicate that we have 3 or more triangles
323
}
324
}
325
}
326
327
// Walk over all edges and determine which ones are active
328
for (const EdgeToTriangle::value_type &edge : edge_to_triangle)
329
{
330
uint num_active = 0;
331
if (edge.second.mNumTriangles == 1)
332
{
333
// Edge is not shared, it is an active edge
334
num_active = 1;
335
}
336
else if (edge.second.mNumTriangles == 2)
337
{
338
// Simple shared edge, determine if edge is active based on the two adjacent triangles
339
const IndexedTriangle &triangle1 = ioIndices[edge.second.mTriangleIndices[0]];
340
const IndexedTriangle &triangle2 = ioIndices[edge.second.mTriangleIndices[1]];
341
342
// Find which edge this is for both triangles
343
uint edge_idx1 = edge.first.GetIndexInTriangle(triangle1);
344
uint edge_idx2 = edge.first.GetIndexInTriangle(triangle2);
345
346
// Construct a plane for triangle 1 (e1 = edge vertex 1, e2 = edge vertex 2, op = opposing vertex)
347
Vec3 triangle1_e1 = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[edge_idx1]]);
348
Vec3 triangle1_e2 = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[(edge_idx1 + 1) % 3]]);
349
Vec3 triangle1_op = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[(edge_idx1 + 2) % 3]]);
350
Plane triangle1_plane = Plane::sFromPointsCCW(triangle1_e1, triangle1_e2, triangle1_op);
351
352
// Construct a plane for triangle 2
353
Vec3 triangle2_e1 = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[edge_idx2]]);
354
Vec3 triangle2_e2 = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[(edge_idx2 + 1) % 3]]);
355
Vec3 triangle2_op = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[(edge_idx2 + 2) % 3]]);
356
Plane triangle2_plane = Plane::sFromPointsCCW(triangle2_e1, triangle2_e2, triangle2_op);
357
358
// Determine if the edge is active
359
num_active = ActiveEdges::IsEdgeActive(triangle1_plane.GetNormal(), triangle2_plane.GetNormal(), triangle1_e2 - triangle1_e1, inSettings.mActiveEdgeCosThresholdAngle)? 2 : 0;
360
}
361
else
362
{
363
// More edges incoming, we've already marked all edges beyond the 2nd as active
364
num_active = 2;
365
}
366
367
// Mark edges of all original triangles active
368
for (uint i = 0; i < num_active; ++i)
369
{
370
uint triangle_idx = edge.second.mTriangleIndices[i];
371
IndexedTriangle &triangle = ioIndices[triangle_idx];
372
uint edge_idx = edge.first.GetIndexInTriangle(triangle);
373
uint32 mask = 1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT);
374
JPH_ASSERT((triangle.mMaterialIndex & mask) == 0);
375
triangle.mMaterialIndex |= mask;
376
}
377
}
378
}
379
380
MassProperties MeshShape::GetMassProperties() const
381
{
382
// We cannot calculate the volume for an arbitrary mesh, so we return invalid mass properties.
383
// If you want your mesh to be dynamic, then you should provide the mass properties yourself when
384
// creating a Body:
385
//
386
// BodyCreationSettings::mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;
387
// BodyCreationSettings::mMassPropertiesOverride.SetMassAndInertiaOfSolidBox(Vec3::sOne(), 1000.0f);
388
//
389
// Note that for a mesh shape to simulate properly, it is best if the mesh is manifold
390
// (i.e. closed, all edges shared by only two triangles, consistent winding order).
391
return MassProperties();
392
}
393
394
void MeshShape::DecodeSubShapeID(const SubShapeID &inSubShapeID, const void *&outTriangleBlock, uint32 &outTriangleIndex) const
395
{
396
// Get block
397
SubShapeID triangle_idx_subshape_id;
398
uint32 block_id = inSubShapeID.PopID(NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree)), triangle_idx_subshape_id);
399
outTriangleBlock = NodeCodec::DecodingContext::sGetTriangleBlockStart(&mTree[0], block_id);
400
401
// Fetch the triangle index
402
SubShapeID remainder;
403
outTriangleIndex = triangle_idx_subshape_id.PopID(NumTriangleBits, remainder);
404
JPH_ASSERT(remainder.IsEmpty(), "Invalid subshape ID");
405
}
406
407
uint MeshShape::GetMaterialIndex(const SubShapeID &inSubShapeID) const
408
{
409
// Decode ID
410
const void *block_start;
411
uint32 triangle_idx;
412
DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);
413
414
// Fetch the flags
415
uint8 flags = TriangleCodec::DecodingContext::sGetFlags(block_start, triangle_idx);
416
return flags & FLAGS_MATERIAL_MASK;
417
}
418
419
const PhysicsMaterial *MeshShape::GetMaterial(const SubShapeID &inSubShapeID) const
420
{
421
// Return the default material if there are no materials on this shape
422
if (mMaterials.empty())
423
return PhysicsMaterial::sDefault;
424
425
return mMaterials[GetMaterialIndex(inSubShapeID)];
426
}
427
428
Vec3 MeshShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const
429
{
430
// Decode ID
431
const void *block_start;
432
uint32 triangle_idx;
433
DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);
434
435
// Decode triangle
436
Vec3 v1, v2, v3;
437
const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));
438
triangle_ctx.GetTriangle(block_start, triangle_idx, v1, v2, v3);
439
440
// Calculate normal
441
return (v3 - v2).Cross(v1 - v2).Normalized();
442
}
443
444
void MeshShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const
445
{
446
// Decode ID
447
const void *block_start;
448
uint32 triangle_idx;
449
DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);
450
451
// Decode triangle
452
const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));
453
outVertices.resize(3);
454
triangle_ctx.GetTriangle(block_start, triangle_idx, outVertices[0], outVertices[1], outVertices[2]);
455
456
// Flip triangle if scaled inside out
457
if (ScaleHelpers::IsInsideOut(inScale))
458
std::swap(outVertices[1], outVertices[2]);
459
460
// Calculate transform with scale
461
Mat44 transform = inCenterOfMassTransform.PreScaled(inScale);
462
463
// Transform to world space
464
for (Vec3 &v : outVertices)
465
v = transform * v;
466
}
467
468
AABox MeshShape::GetLocalBounds() const
469
{
470
const NodeCodec::Header *header = sGetNodeHeader(mTree);
471
return AABox(Vec3::sLoadFloat3Unsafe(header->mRootBoundsMin), Vec3::sLoadFloat3Unsafe(header->mRootBoundsMax));
472
}
473
474
uint MeshShape::GetSubShapeIDBitsRecursive() const
475
{
476
return NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree)) + NumTriangleBits;
477
}
478
479
template <class Visitor>
480
JPH_INLINE void MeshShape::WalkTree(Visitor &ioVisitor) const
481
{
482
const NodeCodec::Header *header = sGetNodeHeader(mTree);
483
NodeCodec::DecodingContext node_ctx(header);
484
485
const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));
486
const uint8 *buffer_start = &mTree[0];
487
node_ctx.WalkTree(buffer_start, triangle_ctx, ioVisitor);
488
}
489
490
template <class Visitor>
491
JPH_INLINE void MeshShape::WalkTreePerTriangle(const SubShapeIDCreator &inSubShapeIDCreator2, Visitor &ioVisitor) const
492
{
493
struct ChainedVisitor
494
{
495
JPH_INLINE ChainedVisitor(Visitor &ioVisitor, const SubShapeIDCreator &inSubShapeIDCreator2, uint inTriangleBlockIDBits) :
496
mVisitor(ioVisitor),
497
mSubShapeIDCreator2(inSubShapeIDCreator2),
498
mTriangleBlockIDBits(inTriangleBlockIDBits)
499
{
500
}
501
502
JPH_INLINE bool ShouldAbort() const
503
{
504
return mVisitor.ShouldAbort();
505
}
506
507
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
508
{
509
return mVisitor.ShouldVisitNode(inStackTop);
510
}
511
512
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
513
{
514
return mVisitor.VisitNodes(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, ioProperties, inStackTop);
515
}
516
517
JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID)
518
{
519
// Create ID for triangle block
520
SubShapeIDCreator block_sub_shape_id = mSubShapeIDCreator2.PushID(inTriangleBlockID, mTriangleBlockIDBits);
521
522
// Decode vertices and flags
523
JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);
524
Vec3 vertices[MaxTrianglesPerLeaf * 3];
525
uint8 flags[MaxTrianglesPerLeaf];
526
ioContext.Unpack(inTriangles, inNumTriangles, vertices, flags);
527
528
int triangle_idx = 0;
529
for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, triangle_idx++)
530
{
531
// Determine active edges
532
uint8 active_edges = (flags[triangle_idx] >> FLAGS_ACTIVE_EGDE_SHIFT) & FLAGS_ACTIVE_EDGE_MASK;
533
534
// Create ID for triangle
535
SubShapeIDCreator triangle_sub_shape_id = block_sub_shape_id.PushID(triangle_idx, NumTriangleBits);
536
537
mVisitor.VisitTriangle(v[0], v[1], v[2], active_edges, triangle_sub_shape_id.GetID());
538
539
// Check if we should early out now
540
if (mVisitor.ShouldAbort())
541
break;
542
}
543
}
544
545
Visitor & mVisitor;
546
SubShapeIDCreator mSubShapeIDCreator2;
547
uint mTriangleBlockIDBits;
548
};
549
550
ChainedVisitor visitor(ioVisitor, inSubShapeIDCreator2, NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree)));
551
WalkTree(visitor);
552
}
553
554
#ifdef JPH_DEBUG_RENDERER
555
void MeshShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const
556
{
557
// Reset the batch if we switch coloring mode
558
if (mCachedTrianglesColoredPerGroup != sDrawTriangleGroups || mCachedUseMaterialColors != inUseMaterialColors)
559
{
560
mGeometry = nullptr;
561
mCachedTrianglesColoredPerGroup = sDrawTriangleGroups;
562
mCachedUseMaterialColors = inUseMaterialColors;
563
}
564
565
if (mGeometry == nullptr)
566
{
567
struct Visitor
568
{
569
JPH_INLINE bool ShouldAbort() const
570
{
571
return false;
572
}
573
574
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
575
{
576
return true;
577
}
578
579
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
580
{
581
UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ));
582
return CountAndSortTrues(valid, ioProperties);
583
}
584
585
JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID)
586
{
587
JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);
588
Vec3 vertices[MaxTrianglesPerLeaf * 3];
589
ioContext.Unpack(inTriangles, inNumTriangles, vertices);
590
591
if (mDrawTriangleGroups || !mUseMaterialColors || mMaterials.empty())
592
{
593
// Single color for mesh
594
Color color = mDrawTriangleGroups? Color::sGetDistinctColor(mColorIdx++) : (mUseMaterialColors? PhysicsMaterial::sDefault->GetDebugColor() : Color::sWhite);
595
for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3)
596
mTriangles.push_back({ v[0], v[1], v[2], color });
597
}
598
else
599
{
600
// Per triangle color
601
uint8 flags[MaxTrianglesPerLeaf];
602
TriangleCodec::DecodingContext::sGetFlags(inTriangles, inNumTriangles, flags);
603
604
const uint8 *f = flags;
605
for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, f++)
606
mTriangles.push_back({ v[0], v[1], v[2], mMaterials[*f & FLAGS_MATERIAL_MASK]->GetDebugColor() });
607
}
608
}
609
610
Array<DebugRenderer::Triangle> & mTriangles;
611
const PhysicsMaterialList & mMaterials;
612
bool mUseMaterialColors;
613
bool mDrawTriangleGroups;
614
int mColorIdx = 0;
615
};
616
617
Array<DebugRenderer::Triangle> triangles;
618
Visitor visitor { triangles, mMaterials, mCachedUseMaterialColors, mCachedTrianglesColoredPerGroup };
619
WalkTree(visitor);
620
mGeometry = new DebugRenderer::Geometry(inRenderer->CreateTriangleBatch(triangles), GetLocalBounds());
621
}
622
623
// Test if the shape is scaled inside out
624
DebugRenderer::ECullMode cull_mode = ScaleHelpers::IsInsideOut(inScale)? DebugRenderer::ECullMode::CullFrontFace : DebugRenderer::ECullMode::CullBackFace;
625
626
// Determine the draw mode
627
DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid;
628
629
// Draw the geometry
630
inRenderer->DrawGeometry(inCenterOfMassTransform * Mat44::sScale(inScale), inColor, mGeometry, cull_mode, DebugRenderer::ECastShadow::On, draw_mode);
631
632
if (sDrawTriangleOutlines)
633
{
634
struct Visitor
635
{
636
JPH_INLINE Visitor(DebugRenderer *inRenderer, RMat44Arg inTransform) :
637
mRenderer(inRenderer),
638
mTransform(inTransform)
639
{
640
}
641
642
JPH_INLINE bool ShouldAbort() const
643
{
644
return false;
645
}
646
647
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
648
{
649
return true;
650
}
651
652
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
653
{
654
UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ));
655
return CountAndSortTrues(valid, ioProperties);
656
}
657
658
JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID)
659
{
660
// Decode vertices and flags
661
JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);
662
Vec3 vertices[MaxTrianglesPerLeaf * 3];
663
uint8 flags[MaxTrianglesPerLeaf];
664
ioContext.Unpack(inTriangles, inNumTriangles, vertices, flags);
665
666
// Loop through triangles
667
const uint8 *f = flags;
668
for (Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, ++f)
669
{
670
// Loop through edges
671
for (uint edge_idx = 0; edge_idx < 3; ++edge_idx)
672
{
673
RVec3 v1 = mTransform * v[edge_idx];
674
RVec3 v2 = mTransform * v[(edge_idx + 1) % 3];
675
676
// Draw active edge as a green arrow, other edges as grey
677
if (*f & (1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT)))
678
mRenderer->DrawArrow(v1, v2, Color::sGreen, 0.01f);
679
else
680
mRenderer->DrawLine(v1, v2, Color::sGrey);
681
}
682
}
683
}
684
685
DebugRenderer * mRenderer;
686
RMat44 mTransform;
687
};
688
689
Visitor visitor { inRenderer, inCenterOfMassTransform.PreScaled(inScale) };
690
WalkTree(visitor);
691
}
692
}
693
#endif // JPH_DEBUG_RENDERER
694
695
bool MeshShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const
696
{
697
JPH_PROFILE_FUNCTION();
698
699
struct Visitor
700
{
701
JPH_INLINE explicit Visitor(RayCastResult &ioHit) :
702
mHit(ioHit)
703
{
704
}
705
706
JPH_INLINE bool ShouldAbort() const
707
{
708
return mHit.mFraction <= 0.0f;
709
}
710
711
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
712
{
713
return mDistanceStack[inStackTop] < mHit.mFraction;
714
}
715
716
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
717
{
718
// Test bounds of 4 children
719
Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
720
721
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
722
return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]);
723
}
724
725
JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID)
726
{
727
// Test against triangles
728
uint32 triangle_idx;
729
float fraction = ioContext.TestRay(mRayOrigin, mRayDirection, inTriangles, inNumTriangles, mHit.mFraction, triangle_idx);
730
if (fraction < mHit.mFraction)
731
{
732
mHit.mFraction = fraction;
733
mHit.mSubShapeID2 = mSubShapeIDCreator.PushID(inTriangleBlockID, mTriangleBlockIDBits).PushID(triangle_idx, NumTriangleBits).GetID();
734
mReturnValue = true;
735
}
736
}
737
738
RayCastResult & mHit;
739
Vec3 mRayOrigin;
740
Vec3 mRayDirection;
741
RayInvDirection mRayInvDirection;
742
uint mTriangleBlockIDBits;
743
SubShapeIDCreator mSubShapeIDCreator;
744
bool mReturnValue = false;
745
float mDistanceStack[NodeCodec::StackSize];
746
};
747
748
Visitor visitor(ioHit);
749
visitor.mRayOrigin = inRay.mOrigin;
750
visitor.mRayDirection = inRay.mDirection;
751
visitor.mRayInvDirection.Set(inRay.mDirection);
752
visitor.mTriangleBlockIDBits = NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree));
753
visitor.mSubShapeIDCreator = inSubShapeIDCreator;
754
WalkTree(visitor);
755
756
return visitor.mReturnValue;
757
}
758
759
void MeshShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const
760
{
761
JPH_PROFILE_FUNCTION();
762
763
// Test shape filter
764
if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))
765
return;
766
767
struct Visitor
768
{
769
JPH_INLINE explicit Visitor(CastRayCollector &ioCollector) :
770
mCollector(ioCollector)
771
{
772
}
773
774
JPH_INLINE bool ShouldAbort() const
775
{
776
return mCollector.ShouldEarlyOut();
777
}
778
779
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
780
{
781
return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction();
782
}
783
784
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
785
{
786
// Test bounds of 4 children
787
Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
788
789
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
790
return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);
791
}
792
793
JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, [[maybe_unused]] uint8 inActiveEdges, SubShapeID inSubShapeID2)
794
{
795
// Back facing check
796
if (mBackFaceMode == EBackFaceMode::IgnoreBackFaces && (inV2 - inV0).Cross(inV1 - inV0).Dot(mRayDirection) < 0)
797
return;
798
799
// Check the triangle
800
float fraction = RayTriangle(mRayOrigin, mRayDirection, inV0, inV1, inV2);
801
if (fraction < mCollector.GetEarlyOutFraction())
802
{
803
RayCastResult hit;
804
hit.mBodyID = TransformedShape::sGetBodyID(mCollector.GetContext());
805
hit.mFraction = fraction;
806
hit.mSubShapeID2 = inSubShapeID2;
807
mCollector.AddHit(hit);
808
}
809
}
810
811
CastRayCollector & mCollector;
812
Vec3 mRayOrigin;
813
Vec3 mRayDirection;
814
RayInvDirection mRayInvDirection;
815
EBackFaceMode mBackFaceMode;
816
float mDistanceStack[NodeCodec::StackSize];
817
};
818
819
Visitor visitor(ioCollector);
820
visitor.mBackFaceMode = inRayCastSettings.mBackFaceModeTriangles;
821
visitor.mRayOrigin = inRay.mOrigin;
822
visitor.mRayDirection = inRay.mDirection;
823
visitor.mRayInvDirection.Set(inRay.mDirection);
824
WalkTreePerTriangle(inSubShapeIDCreator, visitor);
825
}
826
827
void MeshShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const
828
{
829
sCollidePointUsingRayCast(*this, inPoint, inSubShapeIDCreator, ioCollector, inShapeFilter);
830
}
831
832
void MeshShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const CollideSoftBodyVertexIterator &inVertices, uint inNumVertices, int inCollidingShapeIndex) const
833
{
834
JPH_PROFILE_FUNCTION();
835
836
struct Visitor : public CollideSoftBodyVerticesVsTriangles
837
{
838
using CollideSoftBodyVerticesVsTriangles::CollideSoftBodyVerticesVsTriangles;
839
840
JPH_INLINE bool ShouldAbort() const
841
{
842
return false;
843
}
844
845
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
846
{
847
return mDistanceStack[inStackTop] < mClosestDistanceSq;
848
}
849
850
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
851
{
852
// Get distance to vertex
853
Vec4 dist_sq = AABox4DistanceSqToPoint(mLocalPosition, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
854
855
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
856
return SortReverseAndStore(dist_sq, mClosestDistanceSq, ioProperties, &mDistanceStack[inStackTop]);
857
}
858
859
JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, [[maybe_unused]] uint8 inActiveEdges, [[maybe_unused]] SubShapeID inSubShapeID2)
860
{
861
ProcessTriangle(inV0, inV1, inV2);
862
}
863
864
float mDistanceStack[NodeCodec::StackSize];
865
};
866
867
Visitor visitor(inCenterOfMassTransform, inScale);
868
869
for (CollideSoftBodyVertexIterator v = inVertices, sbv_end = inVertices + inNumVertices; v != sbv_end; ++v)
870
if (v.GetInvMass() > 0.0f)
871
{
872
visitor.StartVertex(v);
873
WalkTreePerTriangle(SubShapeIDCreator(), visitor);
874
visitor.FinishVertex(v, inCollidingShapeIndex);
875
}
876
}
877
878
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)
879
{
880
JPH_PROFILE_FUNCTION();
881
882
struct Visitor : public CastConvexVsTriangles
883
{
884
using CastConvexVsTriangles::CastConvexVsTriangles;
885
886
JPH_INLINE bool ShouldAbort() const
887
{
888
return mCollector.ShouldEarlyOut();
889
}
890
891
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
892
{
893
return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction();
894
}
895
896
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
897
{
898
// Scale the bounding boxes of this node
899
Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;
900
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);
901
902
// Enlarge them by the casted shape's box extents
903
AABox4EnlargeWithExtent(mBoxExtent, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
904
905
// Test bounds of 4 children
906
Vec4 distance = RayAABox4(mBoxCenter, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
907
908
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
909
return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);
910
}
911
912
JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)
913
{
914
Cast(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);
915
}
916
917
RayInvDirection mInvDirection;
918
Vec3 mBoxCenter;
919
Vec3 mBoxExtent;
920
float mDistanceStack[NodeCodec::StackSize];
921
};
922
923
JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Mesh);
924
const MeshShape *shape = static_cast<const MeshShape *>(inShape);
925
926
Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector);
927
visitor.mInvDirection.Set(inShapeCast.mDirection);
928
visitor.mBoxCenter = inShapeCast.mShapeWorldBounds.GetCenter();
929
visitor.mBoxExtent = inShapeCast.mShapeWorldBounds.GetExtent();
930
shape->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);
931
}
932
933
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)
934
{
935
JPH_PROFILE_FUNCTION();
936
937
struct Visitor : public CastSphereVsTriangles
938
{
939
using CastSphereVsTriangles::CastSphereVsTriangles;
940
941
JPH_INLINE bool ShouldAbort() const
942
{
943
return mCollector.ShouldEarlyOut();
944
}
945
946
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
947
{
948
return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction();
949
}
950
951
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
952
{
953
// Scale the bounding boxes of this node
954
Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;
955
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);
956
957
// Enlarge them by the radius of the sphere
958
AABox4EnlargeWithExtent(Vec3::sReplicate(mRadius), bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
959
960
// Test bounds of 4 children
961
Vec4 distance = RayAABox4(mStart, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
962
963
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
964
return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);
965
}
966
967
JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)
968
{
969
Cast(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);
970
}
971
972
RayInvDirection mInvDirection;
973
float mDistanceStack[NodeCodec::StackSize];
974
};
975
976
JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Mesh);
977
const MeshShape *shape = static_cast<const MeshShape *>(inShape);
978
979
Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector);
980
visitor.mInvDirection.Set(inShapeCast.mDirection);
981
shape->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);
982
}
983
984
struct MeshShape::MSGetTrianglesContext
985
{
986
JPH_INLINE MSGetTrianglesContext(const MeshShape *inShape, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) :
987
mDecodeCtx(sGetNodeHeader(inShape->mTree)),
988
mShape(inShape),
989
mLocalBox(Mat44::sInverseRotationTranslation(inRotation, inPositionCOM), inBox),
990
mMeshScale(inScale),
991
mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale)),
992
mIsInsideOut(ScaleHelpers::IsInsideOut(inScale))
993
{
994
}
995
996
JPH_INLINE bool ShouldAbort() const
997
{
998
return mShouldAbort;
999
}
1000
1001
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
1002
{
1003
return true;
1004
}
1005
1006
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
1007
{
1008
// Scale the bounding boxes of this node
1009
Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;
1010
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);
1011
1012
// Test which nodes collide
1013
UVec4 collides = AABox4VsBox(mLocalBox, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
1014
return CountAndSortTrues(collides, ioProperties);
1015
}
1016
1017
JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID)
1018
{
1019
// 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.
1020
if (mNumTrianglesFound + inNumTriangles > mMaxTrianglesRequested)
1021
{
1022
mShouldAbort = true;
1023
return;
1024
}
1025
1026
// Decode vertices
1027
JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);
1028
Vec3 vertices[MaxTrianglesPerLeaf * 3];
1029
ioContext.Unpack(inTriangles, inNumTriangles, vertices);
1030
1031
// Store vertices as Float3
1032
if (mIsInsideOut)
1033
{
1034
// Scaled inside out, flip the triangles
1035
for (const Vec3 *v = vertices, *v_end = v + 3 * inNumTriangles; v < v_end; v += 3)
1036
{
1037
(mLocalToWorld * v[0]).StoreFloat3(mTriangleVertices++);
1038
(mLocalToWorld * v[2]).StoreFloat3(mTriangleVertices++);
1039
(mLocalToWorld * v[1]).StoreFloat3(mTriangleVertices++);
1040
}
1041
}
1042
else
1043
{
1044
// Normal scale
1045
for (const Vec3 *v = vertices, *v_end = v + 3 * inNumTriangles; v < v_end; ++v)
1046
(mLocalToWorld * *v).StoreFloat3(mTriangleVertices++);
1047
}
1048
1049
if (mMaterials != nullptr)
1050
{
1051
if (mShape->mMaterials.empty())
1052
{
1053
// No materials, output default
1054
const PhysicsMaterial *default_material = PhysicsMaterial::sDefault;
1055
for (int m = 0; m < inNumTriangles; ++m)
1056
*mMaterials++ = default_material;
1057
}
1058
else
1059
{
1060
// Decode triangle flags
1061
uint8 flags[MaxTrianglesPerLeaf];
1062
TriangleCodec::DecodingContext::sGetFlags(inTriangles, inNumTriangles, flags);
1063
1064
// Store materials
1065
for (const uint8 *f = flags, *f_end = f + inNumTriangles; f < f_end; ++f)
1066
*mMaterials++ = mShape->mMaterials[*f & FLAGS_MATERIAL_MASK].GetPtr();
1067
}
1068
}
1069
1070
// Accumulate triangles found
1071
mNumTrianglesFound += inNumTriangles;
1072
}
1073
1074
NodeCodec::DecodingContext mDecodeCtx;
1075
const MeshShape * mShape;
1076
OrientedBox mLocalBox;
1077
Vec3 mMeshScale;
1078
Mat44 mLocalToWorld;
1079
int mMaxTrianglesRequested;
1080
Float3 * mTriangleVertices;
1081
int mNumTrianglesFound;
1082
const PhysicsMaterial ** mMaterials;
1083
bool mShouldAbort;
1084
bool mIsInsideOut;
1085
};
1086
1087
void MeshShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const
1088
{
1089
static_assert(sizeof(MSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small");
1090
JPH_ASSERT(IsAligned(&ioContext, alignof(MSGetTrianglesContext)));
1091
1092
new (&ioContext) MSGetTrianglesContext(this, inBox, inPositionCOM, inRotation, inScale);
1093
}
1094
1095
int MeshShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const
1096
{
1097
static_assert(cGetTrianglesMinTrianglesRequested >= MaxTrianglesPerLeaf, "cGetTrianglesMinTrianglesRequested is too small");
1098
JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested);
1099
1100
// Check if we're done
1101
MSGetTrianglesContext &context = (MSGetTrianglesContext &)ioContext;
1102
if (context.mDecodeCtx.IsDoneWalking())
1103
return 0;
1104
1105
// Store parameters on context
1106
context.mMaxTrianglesRequested = inMaxTrianglesRequested;
1107
context.mTriangleVertices = outTriangleVertices;
1108
context.mMaterials = outMaterials;
1109
context.mShouldAbort = false; // Reset the abort flag
1110
context.mNumTrianglesFound = 0;
1111
1112
// Continue (or start) walking the tree
1113
const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));
1114
const uint8 *buffer_start = &mTree[0];
1115
context.mDecodeCtx.WalkTree(buffer_start, triangle_ctx, context);
1116
return context.mNumTrianglesFound;
1117
}
1118
1119
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)
1120
{
1121
JPH_PROFILE_FUNCTION();
1122
1123
// Get the shapes
1124
JPH_ASSERT(inShape1->GetType() == EShapeType::Convex);
1125
JPH_ASSERT(inShape2->GetType() == EShapeType::Mesh);
1126
const ConvexShape *shape1 = static_cast<const ConvexShape *>(inShape1);
1127
const MeshShape *shape2 = static_cast<const MeshShape *>(inShape2);
1128
1129
struct Visitor : public CollideConvexVsTriangles
1130
{
1131
using CollideConvexVsTriangles::CollideConvexVsTriangles;
1132
1133
JPH_INLINE bool ShouldAbort() const
1134
{
1135
return mCollector.ShouldEarlyOut();
1136
}
1137
1138
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
1139
{
1140
return true;
1141
}
1142
1143
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
1144
{
1145
// Scale the bounding boxes of this node
1146
Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;
1147
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);
1148
1149
// Test which nodes collide
1150
UVec4 collides = AABox4VsBox(mBoundsOf1InSpaceOf2, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
1151
return CountAndSortTrues(collides, ioProperties);
1152
}
1153
1154
JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)
1155
{
1156
Collide(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);
1157
}
1158
};
1159
1160
Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector);
1161
shape2->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);
1162
}
1163
1164
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)
1165
{
1166
JPH_PROFILE_FUNCTION();
1167
1168
// Get the shapes
1169
JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Sphere);
1170
JPH_ASSERT(inShape2->GetType() == EShapeType::Mesh);
1171
const SphereShape *shape1 = static_cast<const SphereShape *>(inShape1);
1172
const MeshShape *shape2 = static_cast<const MeshShape *>(inShape2);
1173
1174
struct Visitor : public CollideSphereVsTriangles
1175
{
1176
using CollideSphereVsTriangles::CollideSphereVsTriangles;
1177
1178
JPH_INLINE bool ShouldAbort() const
1179
{
1180
return mCollector.ShouldEarlyOut();
1181
}
1182
1183
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
1184
{
1185
return true;
1186
}
1187
1188
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
1189
{
1190
// Scale the bounding boxes of this node
1191
Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;
1192
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);
1193
1194
// Test which nodes collide
1195
UVec4 collides = AABox4VsSphere(mSphereCenterIn2, mRadiusPlusMaxSeparationSq, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);
1196
return CountAndSortTrues(collides, ioProperties);
1197
}
1198
1199
JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)
1200
{
1201
Collide(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);
1202
}
1203
};
1204
1205
Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector);
1206
shape2->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);
1207
}
1208
1209
void MeshShape::SaveBinaryState(StreamOut &inStream) const
1210
{
1211
Shape::SaveBinaryState(inStream);
1212
1213
inStream.Write(static_cast<const ByteBufferVector &>(mTree)); // Make sure we use the Array<> overload
1214
}
1215
1216
void MeshShape::RestoreBinaryState(StreamIn &inStream)
1217
{
1218
Shape::RestoreBinaryState(inStream);
1219
1220
inStream.Read(static_cast<ByteBufferVector &>(mTree)); // Make sure we use the Array<> overload
1221
}
1222
1223
void MeshShape::SaveMaterialState(PhysicsMaterialList &outMaterials) const
1224
{
1225
outMaterials = mMaterials;
1226
}
1227
1228
void MeshShape::RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials)
1229
{
1230
mMaterials.assign(inMaterials, inMaterials + inNumMaterials);
1231
}
1232
1233
Shape::Stats MeshShape::GetStats() const
1234
{
1235
// Walk the tree to count the triangles
1236
struct Visitor
1237
{
1238
JPH_INLINE bool ShouldAbort() const
1239
{
1240
return false;
1241
}
1242
1243
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
1244
{
1245
return true;
1246
}
1247
1248
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
1249
{
1250
// Visit all valid children
1251
UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ));
1252
return CountAndSortTrues(valid, ioProperties);
1253
}
1254
1255
JPH_INLINE void VisitTriangles([[maybe_unused]] const TriangleCodec::DecodingContext &ioContext, [[maybe_unused]] const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID)
1256
{
1257
mNumTriangles += inNumTriangles;
1258
}
1259
1260
uint mNumTriangles = 0;
1261
};
1262
1263
Visitor visitor;
1264
WalkTree(visitor);
1265
1266
return Stats(sizeof(*this) + mMaterials.size() * sizeof(Ref<PhysicsMaterial>) + mTree.size() * sizeof(uint8), visitor.mNumTriangles);
1267
}
1268
1269
uint32 MeshShape::GetTriangleUserData(const SubShapeID &inSubShapeID) const
1270
{
1271
// Decode ID
1272
const void *block_start;
1273
uint32 triangle_idx;
1274
DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);
1275
1276
// Decode triangle
1277
const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));
1278
return triangle_ctx.GetUserData(block_start, triangle_idx);
1279
}
1280
1281
void MeshShape::sRegister()
1282
{
1283
ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Mesh);
1284
f.mConstruct = []() -> Shape * { return new MeshShape; };
1285
f.mColor = Color::sRed;
1286
1287
for (EShapeSubType s : sConvexSubShapeTypes)
1288
{
1289
CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::Mesh, sCollideConvexVsMesh);
1290
CollisionDispatch::sRegisterCastShape(s, EShapeSubType::Mesh, sCastConvexVsMesh);
1291
1292
CollisionDispatch::sRegisterCastShape(EShapeSubType::Mesh, s, CollisionDispatch::sReversedCastShape);
1293
CollisionDispatch::sRegisterCollideShape(EShapeSubType::Mesh, s, CollisionDispatch::sReversedCollideShape);
1294
}
1295
1296
// Specialized collision functions
1297
CollisionDispatch::sRegisterCollideShape(EShapeSubType::Sphere, EShapeSubType::Mesh, sCollideSphereVsMesh);
1298
CollisionDispatch::sRegisterCastShape(EShapeSubType::Sphere, EShapeSubType::Mesh, sCastSphereVsMesh);
1299
}
1300
1301
JPH_NAMESPACE_END
1302
1303