Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/Shape/MeshShape.cpp
21420 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>56#include <Jolt/Physics/Collision/Shape/MeshShape.h>7#include <Jolt/Physics/Collision/Shape/ConvexShape.h>8#include <Jolt/Physics/Collision/Shape/ScaleHelpers.h>9#include <Jolt/Physics/Collision/Shape/SphereShape.h>10#include <Jolt/Physics/Collision/RayCast.h>11#include <Jolt/Physics/Collision/ShapeCast.h>12#include <Jolt/Physics/Collision/ShapeFilter.h>13#include <Jolt/Physics/Collision/CastResult.h>14#include <Jolt/Physics/Collision/CollideConvexVsTriangles.h>15#include <Jolt/Physics/Collision/CollideSphereVsTriangles.h>16#include <Jolt/Physics/Collision/CastConvexVsTriangles.h>17#include <Jolt/Physics/Collision/CastSphereVsTriangles.h>18#include <Jolt/Physics/Collision/TransformedShape.h>19#include <Jolt/Physics/Collision/ActiveEdges.h>20#include <Jolt/Physics/Collision/CollisionDispatch.h>21#include <Jolt/Physics/Collision/SortReverseAndStore.h>22#include <Jolt/Physics/Collision/CollideSoftBodyVerticesVsTriangles.h>23#include <Jolt/Core/StringTools.h>24#include <Jolt/Core/StreamIn.h>25#include <Jolt/Core/StreamOut.h>26#include <Jolt/Core/Profiler.h>27#include <Jolt/Core/UnorderedMap.h>28#include <Jolt/Geometry/AABox4.h>29#include <Jolt/Geometry/RayAABox.h>30#include <Jolt/Geometry/Indexify.h>31#include <Jolt/Geometry/Plane.h>32#include <Jolt/Geometry/OrientedBox.h>33#include <Jolt/TriangleSplitter/TriangleSplitterBinning.h>34#include <Jolt/TriangleSplitter/TriangleSplitterMean.h>35#include <Jolt/AABBTree/AABBTreeBuilder.h>36#include <Jolt/AABBTree/AABBTreeToBuffer.h>37#include <Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h>38#include <Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h>39#include <Jolt/ObjectStream/TypeDeclarations.h>4041JPH_NAMESPACE_BEGIN4243#ifdef JPH_DEBUG_RENDERER44bool MeshShape::sDrawTriangleGroups = false;45bool MeshShape::sDrawTriangleOutlines = false;46#endif // JPH_DEBUG_RENDERER4748JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(MeshShapeSettings)49{50JPH_ADD_BASE_CLASS(MeshShapeSettings, ShapeSettings)5152JPH_ADD_ATTRIBUTE(MeshShapeSettings, mTriangleVertices)53JPH_ADD_ATTRIBUTE(MeshShapeSettings, mIndexedTriangles)54JPH_ADD_ATTRIBUTE(MeshShapeSettings, mMaterials)55JPH_ADD_ATTRIBUTE(MeshShapeSettings, mMaxTrianglesPerLeaf)56JPH_ADD_ATTRIBUTE(MeshShapeSettings, mActiveEdgeCosThresholdAngle)57JPH_ADD_ATTRIBUTE(MeshShapeSettings, mPerTriangleUserData)58JPH_ADD_ENUM_ATTRIBUTE(MeshShapeSettings, mBuildQuality)59}6061// Codecs this mesh shape is using62using TriangleCodec = TriangleCodecIndexed8BitPackSOA4Flags;63using NodeCodec = NodeCodecQuadTreeHalfFloat;6465// Get header for tree66static JPH_INLINE const NodeCodec::Header *sGetNodeHeader(const ByteBuffer &inTree)67{68return inTree.Get<NodeCodec::Header>(0);69}7071// Get header for triangles72static JPH_INLINE const TriangleCodec::TriangleHeader *sGetTriangleHeader(const ByteBuffer &inTree)73{74return inTree.Get<TriangleCodec::TriangleHeader>(NodeCodec::HeaderSize);75}7677MeshShapeSettings::MeshShapeSettings(const TriangleList &inTriangles, PhysicsMaterialList inMaterials) :78mMaterials(std::move(inMaterials))79{80Indexify(inTriangles, mTriangleVertices, mIndexedTriangles);8182Sanitize();83}8485MeshShapeSettings::MeshShapeSettings(VertexList inVertices, IndexedTriangleList inTriangles, PhysicsMaterialList inMaterials) :86mTriangleVertices(std::move(inVertices)),87mIndexedTriangles(std::move(inTriangles)),88mMaterials(std::move(inMaterials))89{90Sanitize();91}9293void MeshShapeSettings::Sanitize()94{95// Remove degenerate and duplicate triangles96UnorderedSet<IndexedTriangle> triangles;97triangles.reserve(UnorderedSet<IndexedTriangle>::size_type(mIndexedTriangles.size()));98TriangleCodec::ValidationContext validation_ctx(mIndexedTriangles, mTriangleVertices);99for (int t = (int)mIndexedTriangles.size() - 1; t >= 0; --t)100{101const IndexedTriangle &tri = mIndexedTriangles[t];102103if (tri.IsDegenerate(mTriangleVertices) // Degenerate triangle104|| validation_ctx.IsDegenerate(tri) // Triangle is degenerate in the quantized space105|| !triangles.insert(tri.GetLowestIndexFirst()).second) // Duplicate triangle106{107// The order of triangles doesn't matter (gets reordered while building the tree), so we can just swap the last triangle into this slot108mIndexedTriangles[t] = mIndexedTriangles.back();109mIndexedTriangles.pop_back();110}111}112}113114ShapeSettings::ShapeResult MeshShapeSettings::Create() const115{116if (mCachedResult.IsEmpty())117Ref<Shape> shape = new MeshShape(*this, mCachedResult);118return mCachedResult;119}120121MeshShape::MeshShape(const MeshShapeSettings &inSettings, ShapeResult &outResult) :122Shape(EShapeType::Mesh, EShapeSubType::Mesh, inSettings, outResult)123{124// Check if there are any triangles125if (inSettings.mIndexedTriangles.empty())126{127outResult.SetError("Need triangles to create a mesh shape!");128return;129}130131// Check triangles132TriangleCodec::ValidationContext validation_ctx(inSettings.mIndexedTriangles, inSettings.mTriangleVertices);133for (int t = (int)inSettings.mIndexedTriangles.size() - 1; t >= 0; --t)134{135const IndexedTriangle &triangle = inSettings.mIndexedTriangles[t];136if (triangle.IsDegenerate(inSettings.mTriangleVertices)137|| validation_ctx.IsDegenerate(triangle))138{139outResult.SetError(StringFormat("Triangle %d is degenerate!", t));140return;141}142else143{144// Check vertex indices145for (uint32 idx : triangle.mIdx)146if (idx >= inSettings.mTriangleVertices.size())147{148outResult.SetError(StringFormat("Vertex index %u is beyond vertex list (size: %u)", idx, (uint)inSettings.mTriangleVertices.size()));149return;150}151}152}153154// Copy materials155mMaterials = inSettings.mMaterials;156if (!mMaterials.empty())157{158// Validate materials159if (mMaterials.size() > (1 << FLAGS_MATERIAL_BITS))160{161outResult.SetError(StringFormat("Supporting max %d materials per mesh", 1 << FLAGS_MATERIAL_BITS));162return;163}164for (const IndexedTriangle &t : inSettings.mIndexedTriangles)165if (t.mMaterialIndex >= mMaterials.size())166{167outResult.SetError(StringFormat("Triangle material %u is beyond material list (size: %u)", t.mMaterialIndex, (uint)mMaterials.size()));168return;169}170}171else172{173// No materials assigned, validate that all triangles use material index 0174for (const IndexedTriangle &t : inSettings.mIndexedTriangles)175if (t.mMaterialIndex != 0)176{177outResult.SetError("No materials present, all triangles should have material index 0");178return;179}180}181182// Check max triangles183if (inSettings.mMaxTrianglesPerLeaf < 1 || inSettings.mMaxTrianglesPerLeaf > MaxTrianglesPerLeaf)184{185outResult.SetError("Invalid max triangles per leaf");186return;187}188189// Fill in active edge bits190IndexedTriangleList indexed_triangles = inSettings.mIndexedTriangles; // Copy indices since we're adding the 'active edge' flag191sFindActiveEdges(inSettings, indexed_triangles);192193// Create triangle splitter194union Storage195{196Storage() { }197~Storage() { }198199TriangleSplitterBinning mBinning;200TriangleSplitterMean mMean;201};202Storage storage;203TriangleSplitter *splitter = nullptr;204switch (inSettings.mBuildQuality)205{206case MeshShapeSettings::EBuildQuality::FavorRuntimePerformance:207splitter = new (&storage.mBinning) TriangleSplitterBinning(inSettings.mTriangleVertices, indexed_triangles);208break;209210case MeshShapeSettings::EBuildQuality::FavorBuildSpeed:211splitter = new (&storage.mMean) TriangleSplitterMean(inSettings.mTriangleVertices, indexed_triangles);212break;213214default:215JPH_ASSERT(false);216break;217}218219// Build tree220AABBTreeBuilder builder(*splitter, inSettings.mMaxTrianglesPerLeaf);221AABBTreeBuilderStats builder_stats;222const AABBTreeBuilder::Node *root = builder.Build(builder_stats);223splitter->~TriangleSplitter();224225// Convert to buffer226AABBTreeToBuffer<TriangleCodec, NodeCodec> buffer;227const char *error = nullptr;228if (!buffer.Convert(builder.GetTriangles(), builder.GetNodes(), inSettings.mTriangleVertices, root, inSettings.mPerTriangleUserData, error))229{230outResult.SetError(error);231return;232}233234// Move data to this class235mTree.swap(buffer.GetBuffer());236237// Check if we're not exceeding the amount of sub shape id bits238if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits)239{240outResult.SetError("Mesh is too big and exceeds the amount of available sub shape ID bits");241return;242}243244outResult.Set(this);245}246247void MeshShape::sFindActiveEdges(const MeshShapeSettings &inSettings, IndexedTriangleList &ioIndices)248{249// Check if we're requested to make all edges active250if (inSettings.mActiveEdgeCosThresholdAngle < 0.0f)251{252for (IndexedTriangle &triangle : ioIndices)253triangle.mMaterialIndex |= 0b111 << FLAGS_ACTIVE_EGDE_SHIFT;254return;255}256257// A struct to hold the two vertex indices of an edge258struct Edge259{260Edge(int inIdx1, int inIdx2) : mIdx1(min(inIdx1, inIdx2)), mIdx2(max(inIdx1, inIdx2)) { }261262uint GetIndexInTriangle(const IndexedTriangle &inTriangle) const263{264for (uint edge_idx = 0; edge_idx < 3; ++edge_idx)265{266Edge edge(inTriangle.mIdx[edge_idx], inTriangle.mIdx[(edge_idx + 1) % 3]);267if (*this == edge)268return edge_idx;269}270271JPH_ASSERT(false);272return ~uint(0);273}274275bool operator == (const Edge &inRHS) const276{277return mIdx1 == inRHS.mIdx1 && mIdx2 == inRHS.mIdx2;278}279280uint64 GetHash() const281{282static_assert(sizeof(*this) == 2 * sizeof(int), "No padding expected");283return HashBytes(this, sizeof(*this));284}285286int mIdx1;287int mIdx2;288};289290// A struct to hold the triangles that are connected to an edge291struct TriangleIndices292{293uint mNumTriangles = 0;294uint mTriangleIndices[2];295};296297// Build a list of edge to triangles298using EdgeToTriangle = UnorderedMap<Edge, TriangleIndices>;299EdgeToTriangle edge_to_triangle;300edge_to_triangle.reserve(EdgeToTriangle::size_type(ioIndices.size() * 3));301for (uint triangle_idx = 0; triangle_idx < ioIndices.size(); ++triangle_idx)302{303IndexedTriangle &triangle = ioIndices[triangle_idx];304for (uint edge_idx = 0; edge_idx < 3; ++edge_idx)305{306Edge edge(triangle.mIdx[edge_idx], triangle.mIdx[(edge_idx + 1) % 3]);307EdgeToTriangle::iterator edge_to_triangle_it = edge_to_triangle.try_emplace(edge, TriangleIndices()).first;308TriangleIndices &indices = edge_to_triangle_it->second;309if (indices.mNumTriangles < 2)310{311// Store index of triangle that connects to this edge312indices.mTriangleIndices[indices.mNumTriangles] = triangle_idx;313indices.mNumTriangles++;314}315else316{317// 3 or more triangles share an edge, mark this edge as active318uint32 mask = 1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT);319JPH_ASSERT((triangle.mMaterialIndex & mask) == 0);320triangle.mMaterialIndex |= mask;321indices.mNumTriangles = 3; // Indicate that we have 3 or more triangles322}323}324}325326// Walk over all edges and determine which ones are active327for (const EdgeToTriangle::value_type &edge : edge_to_triangle)328{329uint num_active = 0;330if (edge.second.mNumTriangles == 1)331{332// Edge is not shared, it is an active edge333num_active = 1;334}335else if (edge.second.mNumTriangles == 2)336{337// Simple shared edge, determine if edge is active based on the two adjacent triangles338const IndexedTriangle &triangle1 = ioIndices[edge.second.mTriangleIndices[0]];339const IndexedTriangle &triangle2 = ioIndices[edge.second.mTriangleIndices[1]];340341// Find which edge this is for both triangles342uint edge_idx1 = edge.first.GetIndexInTriangle(triangle1);343uint edge_idx2 = edge.first.GetIndexInTriangle(triangle2);344345// Construct a plane for triangle 1 (e1 = edge vertex 1, e2 = edge vertex 2, op = opposing vertex)346Vec3 triangle1_e1 = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[edge_idx1]]);347Vec3 triangle1_e2 = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[(edge_idx1 + 1) % 3]]);348Vec3 triangle1_op = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[(edge_idx1 + 2) % 3]]);349Plane triangle1_plane = Plane::sFromPointsCCW(triangle1_e1, triangle1_e2, triangle1_op);350351// Construct a plane for triangle 2352Vec3 triangle2_e1 = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[edge_idx2]]);353Vec3 triangle2_e2 = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[(edge_idx2 + 1) % 3]]);354Vec3 triangle2_op = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[(edge_idx2 + 2) % 3]]);355Plane triangle2_plane = Plane::sFromPointsCCW(triangle2_e1, triangle2_e2, triangle2_op);356357// Determine if the edge is active358num_active = ActiveEdges::IsEdgeActive(triangle1_plane.GetNormal(), triangle2_plane.GetNormal(), triangle1_e2 - triangle1_e1, inSettings.mActiveEdgeCosThresholdAngle)? 2 : 0;359}360else361{362// More edges incoming, we've already marked all edges beyond the 2nd as active363num_active = 2;364}365366// Mark edges of all original triangles active367for (uint i = 0; i < num_active; ++i)368{369uint triangle_idx = edge.second.mTriangleIndices[i];370IndexedTriangle &triangle = ioIndices[triangle_idx];371uint edge_idx = edge.first.GetIndexInTriangle(triangle);372uint32 mask = 1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT);373JPH_ASSERT((triangle.mMaterialIndex & mask) == 0);374triangle.mMaterialIndex |= mask;375}376}377}378379MassProperties MeshShape::GetMassProperties() const380{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 when383// 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 manifold389// (i.e. closed, all edges shared by only two triangles, consistent winding order).390return MassProperties();391}392393void MeshShape::DecodeSubShapeID(const SubShapeID &inSubShapeID, const void *&outTriangleBlock, uint32 &outTriangleIndex) const394{395// Get block396SubShapeID triangle_idx_subshape_id;397uint32 block_id = inSubShapeID.PopID(NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree)), triangle_idx_subshape_id);398outTriangleBlock = NodeCodec::DecodingContext::sGetTriangleBlockStart(&mTree[0], block_id);399400// Fetch the triangle index401SubShapeID remainder;402outTriangleIndex = triangle_idx_subshape_id.PopID(NumTriangleBits, remainder);403JPH_ASSERT(remainder.IsEmpty(), "Invalid subshape ID");404}405406uint MeshShape::GetMaterialIndex(const SubShapeID &inSubShapeID) const407{408// Decode ID409const void *block_start;410uint32 triangle_idx;411DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);412413// Fetch the flags414uint8 flags = TriangleCodec::DecodingContext::sGetFlags(block_start, triangle_idx);415return flags & FLAGS_MATERIAL_MASK;416}417418const PhysicsMaterial *MeshShape::GetMaterial(const SubShapeID &inSubShapeID) const419{420// Return the default material if there are no materials on this shape421if (mMaterials.empty())422return PhysicsMaterial::sDefault;423424return mMaterials[GetMaterialIndex(inSubShapeID)];425}426427Vec3 MeshShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const428{429// Decode ID430const void *block_start;431uint32 triangle_idx;432DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);433434// Decode triangle435Vec3 v1, v2, v3;436const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));437triangle_ctx.GetTriangle(block_start, triangle_idx, v1, v2, v3);438439// Calculate normal440return (v3 - v2).Cross(v1 - v2).Normalized();441}442443void MeshShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const444{445// Decode ID446const void *block_start;447uint32 triangle_idx;448DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);449450// Decode triangle451const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));452outVertices.resize(3);453triangle_ctx.GetTriangle(block_start, triangle_idx, outVertices[0], outVertices[1], outVertices[2]);454455// Flip triangle if scaled inside out456if (ScaleHelpers::IsInsideOut(inScale))457std::swap(outVertices[1], outVertices[2]);458459// Calculate transform with scale460Mat44 transform = inCenterOfMassTransform.PreScaled(inScale);461462// Transform to world space463for (Vec3 &v : outVertices)464v = transform * v;465}466467AABox MeshShape::GetLocalBounds() const468{469const NodeCodec::Header *header = sGetNodeHeader(mTree);470return AABox(Vec3::sLoadFloat3Unsafe(header->mRootBoundsMin), Vec3::sLoadFloat3Unsafe(header->mRootBoundsMax));471}472473uint MeshShape::GetSubShapeIDBitsRecursive() const474{475return NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree)) + NumTriangleBits;476}477478template <class Visitor>479JPH_INLINE void MeshShape::WalkTree(Visitor &ioVisitor) const480{481const NodeCodec::Header *header = sGetNodeHeader(mTree);482NodeCodec::DecodingContext node_ctx(header);483484const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));485const uint8 *buffer_start = &mTree[0];486node_ctx.WalkTree(buffer_start, triangle_ctx, ioVisitor);487}488489template <class Visitor>490JPH_INLINE void MeshShape::WalkTreePerTriangle(const SubShapeIDCreator &inSubShapeIDCreator2, Visitor &ioVisitor) const491{492struct ChainedVisitor493{494JPH_INLINE ChainedVisitor(Visitor &ioVisitor, const SubShapeIDCreator &inSubShapeIDCreator2, uint inTriangleBlockIDBits) :495mVisitor(ioVisitor),496mSubShapeIDCreator2(inSubShapeIDCreator2),497mTriangleBlockIDBits(inTriangleBlockIDBits)498{499}500501JPH_INLINE bool ShouldAbort() const502{503return mVisitor.ShouldAbort();504}505506JPH_INLINE bool ShouldVisitNode(int inStackTop) const507{508return mVisitor.ShouldVisitNode(inStackTop);509}510511JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)512{513return mVisitor.VisitNodes(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, ioProperties, inStackTop);514}515516JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID)517{518// Create ID for triangle block519SubShapeIDCreator block_sub_shape_id = mSubShapeIDCreator2.PushID(inTriangleBlockID, mTriangleBlockIDBits);520521// Decode vertices and flags522JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);523Vec3 vertices[MaxTrianglesPerLeaf * 3];524uint8 flags[MaxTrianglesPerLeaf];525ioContext.Unpack(inTriangles, inNumTriangles, vertices, flags);526527int triangle_idx = 0;528for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, triangle_idx++)529{530// Determine active edges531uint8 active_edges = (flags[triangle_idx] >> FLAGS_ACTIVE_EGDE_SHIFT) & FLAGS_ACTIVE_EDGE_MASK;532533// Create ID for triangle534SubShapeIDCreator triangle_sub_shape_id = block_sub_shape_id.PushID(triangle_idx, NumTriangleBits);535536mVisitor.VisitTriangle(v[0], v[1], v[2], active_edges, triangle_sub_shape_id.GetID());537538// Check if we should early out now539if (mVisitor.ShouldAbort())540break;541}542}543544Visitor & mVisitor;545SubShapeIDCreator mSubShapeIDCreator2;546uint mTriangleBlockIDBits;547};548549ChainedVisitor visitor(ioVisitor, inSubShapeIDCreator2, NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree)));550WalkTree(visitor);551}552553#ifdef JPH_DEBUG_RENDERER554void MeshShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const555{556// Reset the batch if we switch coloring mode557if (mCachedTrianglesColoredPerGroup != sDrawTriangleGroups || mCachedUseMaterialColors != inUseMaterialColors)558{559mGeometry = nullptr;560mCachedTrianglesColoredPerGroup = sDrawTriangleGroups;561mCachedUseMaterialColors = inUseMaterialColors;562}563564if (mGeometry == nullptr)565{566struct Visitor567{568JPH_INLINE bool ShouldAbort() const569{570return false;571}572573JPH_INLINE bool ShouldVisitNode(int inStackTop) const574{575return true;576}577578JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)579{580UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ));581return CountAndSortTrues(valid, ioProperties);582}583584JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID)585{586JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);587Vec3 vertices[MaxTrianglesPerLeaf * 3];588ioContext.Unpack(inTriangles, inNumTriangles, vertices);589590if (mDrawTriangleGroups || !mUseMaterialColors || mMaterials.empty())591{592// Single color for mesh593Color color = mDrawTriangleGroups? Color::sGetDistinctColor(mColorIdx++) : (mUseMaterialColors? PhysicsMaterial::sDefault->GetDebugColor() : Color::sWhite);594for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3)595mTriangles.push_back({ v[0], v[1], v[2], color });596}597else598{599// Per triangle color600uint8 flags[MaxTrianglesPerLeaf];601TriangleCodec::DecodingContext::sGetFlags(inTriangles, inNumTriangles, flags);602603const uint8 *f = flags;604for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, f++)605mTriangles.push_back({ v[0], v[1], v[2], mMaterials[*f & FLAGS_MATERIAL_MASK]->GetDebugColor() });606}607}608609Array<DebugRenderer::Triangle> & mTriangles;610const PhysicsMaterialList & mMaterials;611bool mUseMaterialColors;612bool mDrawTriangleGroups;613int mColorIdx = 0;614};615616Array<DebugRenderer::Triangle> triangles;617Visitor visitor { triangles, mMaterials, mCachedUseMaterialColors, mCachedTrianglesColoredPerGroup };618WalkTree(visitor);619mGeometry = new DebugRenderer::Geometry(inRenderer->CreateTriangleBatch(triangles), GetLocalBounds());620}621622// Test if the shape is scaled inside out623DebugRenderer::ECullMode cull_mode = ScaleHelpers::IsInsideOut(inScale)? DebugRenderer::ECullMode::CullFrontFace : DebugRenderer::ECullMode::CullBackFace;624625// Determine the draw mode626DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid;627628// Draw the geometry629inRenderer->DrawGeometry(inCenterOfMassTransform * Mat44::sScale(inScale), inColor, mGeometry, cull_mode, DebugRenderer::ECastShadow::On, draw_mode);630631if (sDrawTriangleOutlines)632{633struct Visitor634{635JPH_INLINE Visitor(DebugRenderer *inRenderer, RMat44Arg inTransform) :636mRenderer(inRenderer),637mTransform(inTransform)638{639}640641JPH_INLINE bool ShouldAbort() const642{643return false;644}645646JPH_INLINE bool ShouldVisitNode(int inStackTop) const647{648return true;649}650651JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)652{653UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ));654return CountAndSortTrues(valid, ioProperties);655}656657JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID)658{659// Decode vertices and flags660JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);661Vec3 vertices[MaxTrianglesPerLeaf * 3];662uint8 flags[MaxTrianglesPerLeaf];663ioContext.Unpack(inTriangles, inNumTriangles, vertices, flags);664665// Loop through triangles666const uint8 *f = flags;667for (Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, ++f)668{669// Loop through edges670for (uint edge_idx = 0; edge_idx < 3; ++edge_idx)671{672RVec3 v1 = mTransform * v[edge_idx];673RVec3 v2 = mTransform * v[(edge_idx + 1) % 3];674675// Draw active edge as a green arrow, other edges as grey676if (*f & (1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT)))677mRenderer->DrawArrow(v1, v2, Color::sGreen, 0.01f);678else679mRenderer->DrawLine(v1, v2, Color::sGrey);680}681}682}683684DebugRenderer * mRenderer;685RMat44 mTransform;686};687688Visitor visitor { inRenderer, inCenterOfMassTransform.PreScaled(inScale) };689WalkTree(visitor);690}691}692#endif // JPH_DEBUG_RENDERER693694bool MeshShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const695{696JPH_PROFILE_FUNCTION();697698struct Visitor699{700JPH_INLINE explicit Visitor(RayCastResult &ioHit) :701mHit(ioHit)702{703}704705JPH_INLINE bool ShouldAbort() const706{707return mHit.mFraction <= 0.0f;708}709710JPH_INLINE bool ShouldVisitNode(int inStackTop) const711{712return mDistanceStack[inStackTop] < mHit.mFraction;713}714715JPH_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 children718Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);719720// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)721return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]);722}723724JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID)725{726// Test against triangles727uint32 triangle_idx;728float fraction = ioContext.TestRay(mRayOrigin, mRayDirection, inTriangles, inNumTriangles, mHit.mFraction, triangle_idx);729if (fraction < mHit.mFraction)730{731mHit.mFraction = fraction;732mHit.mSubShapeID2 = mSubShapeIDCreator.PushID(inTriangleBlockID, mTriangleBlockIDBits).PushID(triangle_idx, NumTriangleBits).GetID();733mReturnValue = true;734}735}736737RayCastResult & mHit;738Vec3 mRayOrigin;739Vec3 mRayDirection;740RayInvDirection mRayInvDirection;741uint mTriangleBlockIDBits;742SubShapeIDCreator mSubShapeIDCreator;743bool mReturnValue = false;744float mDistanceStack[NodeCodec::StackSize];745};746747Visitor visitor(ioHit);748visitor.mRayOrigin = inRay.mOrigin;749visitor.mRayDirection = inRay.mDirection;750visitor.mRayInvDirection.Set(inRay.mDirection);751visitor.mTriangleBlockIDBits = NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree));752visitor.mSubShapeIDCreator = inSubShapeIDCreator;753WalkTree(visitor);754755return visitor.mReturnValue;756}757758void MeshShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const759{760JPH_PROFILE_FUNCTION();761762// Test shape filter763if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))764return;765766struct Visitor767{768JPH_INLINE explicit Visitor(CastRayCollector &ioCollector) :769mCollector(ioCollector)770{771}772773JPH_INLINE bool ShouldAbort() const774{775return mCollector.ShouldEarlyOut();776}777778JPH_INLINE bool ShouldVisitNode(int inStackTop) const779{780return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction();781}782783JPH_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 children786Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);787788// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)789return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);790}791792JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, [[maybe_unused]] uint8 inActiveEdges, SubShapeID inSubShapeID2)793{794// Back facing check795if (mBackFaceMode == EBackFaceMode::IgnoreBackFaces && (inV2 - inV0).Cross(inV1 - inV0).Dot(mRayDirection) < 0)796return;797798// Check the triangle799float fraction = RayTriangle(mRayOrigin, mRayDirection, inV0, inV1, inV2);800if (fraction < mCollector.GetEarlyOutFraction())801{802RayCastResult hit;803hit.mBodyID = TransformedShape::sGetBodyID(mCollector.GetContext());804hit.mFraction = fraction;805hit.mSubShapeID2 = inSubShapeID2;806mCollector.AddHit(hit);807}808}809810CastRayCollector & mCollector;811Vec3 mRayOrigin;812Vec3 mRayDirection;813RayInvDirection mRayInvDirection;814EBackFaceMode mBackFaceMode;815float mDistanceStack[NodeCodec::StackSize];816};817818Visitor visitor(ioCollector);819visitor.mBackFaceMode = inRayCastSettings.mBackFaceModeTriangles;820visitor.mRayOrigin = inRay.mOrigin;821visitor.mRayDirection = inRay.mDirection;822visitor.mRayInvDirection.Set(inRay.mDirection);823WalkTreePerTriangle(inSubShapeIDCreator, visitor);824}825826void MeshShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const827{828sCollidePointUsingRayCast(*this, inPoint, inSubShapeIDCreator, ioCollector, inShapeFilter);829}830831void MeshShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const CollideSoftBodyVertexIterator &inVertices, uint inNumVertices, int inCollidingShapeIndex) const832{833JPH_PROFILE_FUNCTION();834835struct Visitor : public CollideSoftBodyVerticesVsTriangles836{837using CollideSoftBodyVerticesVsTriangles::CollideSoftBodyVerticesVsTriangles;838839JPH_INLINE bool ShouldAbort() const840{841return false;842}843844JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const845{846return mDistanceStack[inStackTop] < mClosestDistanceSq;847}848849JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)850{851// Get distance to vertex852Vec4 dist_sq = AABox4DistanceSqToPoint(mLocalPosition, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);853854// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)855return SortReverseAndStore(dist_sq, mClosestDistanceSq, ioProperties, &mDistanceStack[inStackTop]);856}857858JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, [[maybe_unused]] uint8 inActiveEdges, [[maybe_unused]] SubShapeID inSubShapeID2)859{860ProcessTriangle(inV0, inV1, inV2);861}862863float mDistanceStack[NodeCodec::StackSize];864};865866Visitor visitor(inCenterOfMassTransform, inScale);867868for (CollideSoftBodyVertexIterator v = inVertices, sbv_end = inVertices + inNumVertices; v != sbv_end; ++v)869if (v.GetInvMass() > 0.0f)870{871visitor.StartVertex(v);872WalkTreePerTriangle(SubShapeIDCreator(), visitor);873visitor.FinishVertex(v, inCollidingShapeIndex);874}875}876877void 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{879JPH_PROFILE_FUNCTION();880881struct Visitor : public CastConvexVsTriangles882{883using CastConvexVsTriangles::CastConvexVsTriangles;884885JPH_INLINE bool ShouldAbort() const886{887return mCollector.ShouldEarlyOut();888}889890JPH_INLINE bool ShouldVisitNode(int inStackTop) const891{892return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction();893}894895JPH_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 node898Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;899AABox4Scale(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);900901// Enlarge them by the casted shape's box extents902AABox4EnlargeWithExtent(mBoxExtent, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);903904// Test bounds of 4 children905Vec4 distance = RayAABox4(mBoxCenter, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);906907// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)908return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);909}910911JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)912{913Cast(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);914}915916RayInvDirection mInvDirection;917Vec3 mBoxCenter;918Vec3 mBoxExtent;919float mDistanceStack[NodeCodec::StackSize];920};921922JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Mesh);923const MeshShape *shape = static_cast<const MeshShape *>(inShape);924925Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector);926visitor.mInvDirection.Set(inShapeCast.mDirection);927visitor.mBoxCenter = inShapeCast.mShapeWorldBounds.GetCenter();928visitor.mBoxExtent = inShapeCast.mShapeWorldBounds.GetExtent();929shape->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);930}931932void 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{934JPH_PROFILE_FUNCTION();935936struct Visitor : public CastSphereVsTriangles937{938using CastSphereVsTriangles::CastSphereVsTriangles;939940JPH_INLINE bool ShouldAbort() const941{942return mCollector.ShouldEarlyOut();943}944945JPH_INLINE bool ShouldVisitNode(int inStackTop) const946{947return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction();948}949950JPH_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 node953Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;954AABox4Scale(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);955956// Enlarge them by the radius of the sphere957AABox4EnlargeWithExtent(Vec3::sReplicate(mRadius), bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);958959// Test bounds of 4 children960Vec4 distance = RayAABox4(mStart, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);961962// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)963return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);964}965966JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)967{968Cast(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);969}970971RayInvDirection mInvDirection;972float mDistanceStack[NodeCodec::StackSize];973};974975JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Mesh);976const MeshShape *shape = static_cast<const MeshShape *>(inShape);977978Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector);979visitor.mInvDirection.Set(inShapeCast.mDirection);980shape->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);981}982983struct MeshShape::MSGetTrianglesContext984{985JPH_INLINE MSGetTrianglesContext(const MeshShape *inShape, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) :986mDecodeCtx(sGetNodeHeader(inShape->mTree)),987mShape(inShape),988mLocalBox(Mat44::sInverseRotationTranslation(inRotation, inPositionCOM), inBox),989mMeshScale(inScale),990mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale)),991mIsInsideOut(ScaleHelpers::IsInsideOut(inScale))992{993}994995JPH_INLINE bool ShouldAbort() const996{997return mShouldAbort;998}9991000JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const1001{1002return true;1003}10041005JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const1006{1007// Scale the bounding boxes of this node1008Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;1009AABox4Scale(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);10101011// Test which nodes collide1012UVec4 collides = AABox4VsBox(mLocalBox, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);1013return CountAndSortTrues(collides, ioProperties);1014}10151016JPH_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.1019if (mNumTrianglesFound + inNumTriangles > mMaxTrianglesRequested)1020{1021mShouldAbort = true;1022return;1023}10241025// Decode vertices1026JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);1027Vec3 vertices[MaxTrianglesPerLeaf * 3];1028ioContext.Unpack(inTriangles, inNumTriangles, vertices);10291030// Store vertices as Float31031if (mIsInsideOut)1032{1033// Scaled inside out, flip the triangles1034for (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}1041else1042{1043// Normal scale1044for (const Vec3 *v = vertices, *v_end = v + 3 * inNumTriangles; v < v_end; ++v)1045(mLocalToWorld * *v).StoreFloat3(mTriangleVertices++);1046}10471048if (mMaterials != nullptr)1049{1050if (mShape->mMaterials.empty())1051{1052// No materials, output default1053const PhysicsMaterial *default_material = PhysicsMaterial::sDefault;1054for (int m = 0; m < inNumTriangles; ++m)1055*mMaterials++ = default_material;1056}1057else1058{1059// Decode triangle flags1060uint8 flags[MaxTrianglesPerLeaf];1061TriangleCodec::DecodingContext::sGetFlags(inTriangles, inNumTriangles, flags);10621063// Store materials1064for (const uint8 *f = flags, *f_end = f + inNumTriangles; f < f_end; ++f)1065*mMaterials++ = mShape->mMaterials[*f & FLAGS_MATERIAL_MASK].GetPtr();1066}1067}10681069// Accumulate triangles found1070mNumTrianglesFound += inNumTriangles;1071}10721073NodeCodec::DecodingContext mDecodeCtx;1074const MeshShape * mShape;1075OrientedBox mLocalBox;1076Vec3 mMeshScale;1077Mat44 mLocalToWorld;1078int mMaxTrianglesRequested;1079Float3 * mTriangleVertices;1080int mNumTrianglesFound;1081const PhysicsMaterial ** mMaterials;1082bool mShouldAbort;1083bool mIsInsideOut;1084};10851086void MeshShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const1087{1088static_assert(sizeof(MSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small");1089JPH_ASSERT(IsAligned(&ioContext, alignof(MSGetTrianglesContext)));10901091new (&ioContext) MSGetTrianglesContext(this, inBox, inPositionCOM, inRotation, inScale);1092}10931094int MeshShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const1095{1096static_assert(cGetTrianglesMinTrianglesRequested >= MaxTrianglesPerLeaf, "cGetTrianglesMinTrianglesRequested is too small");1097JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested);10981099// Check if we're done1100MSGetTrianglesContext &context = (MSGetTrianglesContext &)ioContext;1101if (context.mDecodeCtx.IsDoneWalking())1102return 0;11031104// Store parameters on context1105context.mMaxTrianglesRequested = inMaxTrianglesRequested;1106context.mTriangleVertices = outTriangleVertices;1107context.mMaterials = outMaterials;1108context.mShouldAbort = false; // Reset the abort flag1109context.mNumTrianglesFound = 0;11101111// Continue (or start) walking the tree1112const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));1113const uint8 *buffer_start = &mTree[0];1114context.mDecodeCtx.WalkTree(buffer_start, triangle_ctx, context);1115return context.mNumTrianglesFound;1116}11171118void 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{1120JPH_PROFILE_FUNCTION();11211122// Get the shapes1123JPH_ASSERT(inShape1->GetType() == EShapeType::Convex);1124JPH_ASSERT(inShape2->GetType() == EShapeType::Mesh);1125const ConvexShape *shape1 = static_cast<const ConvexShape *>(inShape1);1126const MeshShape *shape2 = static_cast<const MeshShape *>(inShape2);11271128struct Visitor : public CollideConvexVsTriangles1129{1130using CollideConvexVsTriangles::CollideConvexVsTriangles;11311132JPH_INLINE bool ShouldAbort() const1133{1134return mCollector.ShouldEarlyOut();1135}11361137JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const1138{1139return true;1140}11411142JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const1143{1144// Scale the bounding boxes of this node1145Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;1146AABox4Scale(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);11471148// Test which nodes collide1149UVec4 collides = AABox4VsBox(mBoundsOf1InSpaceOf2, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);1150return CountAndSortTrues(collides, ioProperties);1151}11521153JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)1154{1155Collide(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);1156}1157};11581159Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector);1160shape2->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);1161}11621163void 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{1165JPH_PROFILE_FUNCTION();11661167// Get the shapes1168JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Sphere);1169JPH_ASSERT(inShape2->GetType() == EShapeType::Mesh);1170const SphereShape *shape1 = static_cast<const SphereShape *>(inShape1);1171const MeshShape *shape2 = static_cast<const MeshShape *>(inShape2);11721173struct Visitor : public CollideSphereVsTriangles1174{1175using CollideSphereVsTriangles::CollideSphereVsTriangles;11761177JPH_INLINE bool ShouldAbort() const1178{1179return mCollector.ShouldEarlyOut();1180}11811182JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const1183{1184return true;1185}11861187JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const1188{1189// Scale the bounding boxes of this node1190Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;1191AABox4Scale(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);11921193// Test which nodes collide1194UVec4 collides = AABox4VsSphere(mSphereCenterIn2, mRadiusPlusMaxSeparationSq, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);1195return CountAndSortTrues(collides, ioProperties);1196}11971198JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)1199{1200Collide(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);1201}1202};12031204Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector);1205shape2->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);1206}12071208void MeshShape::SaveBinaryState(StreamOut &inStream) const1209{1210Shape::SaveBinaryState(inStream);12111212inStream.Write(static_cast<const ByteBufferVector &>(mTree)); // Make sure we use the Array<> overload1213}12141215void MeshShape::RestoreBinaryState(StreamIn &inStream)1216{1217Shape::RestoreBinaryState(inStream);12181219inStream.Read(static_cast<ByteBufferVector &>(mTree)); // Make sure we use the Array<> overload1220}12211222void MeshShape::SaveMaterialState(PhysicsMaterialList &outMaterials) const1223{1224outMaterials = mMaterials;1225}12261227void MeshShape::RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials)1228{1229mMaterials.assign(inMaterials, inMaterials + inNumMaterials);1230}12311232Shape::Stats MeshShape::GetStats() const1233{1234// Walk the tree to count the triangles1235struct Visitor1236{1237JPH_INLINE bool ShouldAbort() const1238{1239return false;1240}12411242JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const1243{1244return true;1245}12461247JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const1248{1249// Visit all valid children1250UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ));1251return CountAndSortTrues(valid, ioProperties);1252}12531254JPH_INLINE void VisitTriangles([[maybe_unused]] const TriangleCodec::DecodingContext &ioContext, [[maybe_unused]] const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID)1255{1256mNumTriangles += inNumTriangles;1257}12581259uint mNumTriangles = 0;1260};12611262Visitor visitor;1263WalkTree(visitor);12641265return Stats(sizeof(*this) + mMaterials.size() * sizeof(Ref<PhysicsMaterial>) + mTree.size() * sizeof(uint8), visitor.mNumTriangles);1266}12671268uint32 MeshShape::GetTriangleUserData(const SubShapeID &inSubShapeID) const1269{1270// Decode ID1271const void *block_start;1272uint32 triangle_idx;1273DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);12741275// Decode triangle1276const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));1277return triangle_ctx.GetUserData(block_start, triangle_idx);1278}12791280void MeshShape::sRegister()1281{1282ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Mesh);1283f.mConstruct = []() -> Shape * { return new MeshShape; };1284f.mColor = Color::sRed;12851286for (EShapeSubType s : sConvexSubShapeTypes)1287{1288CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::Mesh, sCollideConvexVsMesh);1289CollisionDispatch::sRegisterCastShape(s, EShapeSubType::Mesh, sCastConvexVsMesh);12901291CollisionDispatch::sRegisterCastShape(EShapeSubType::Mesh, s, CollisionDispatch::sReversedCastShape);1292CollisionDispatch::sRegisterCollideShape(EShapeSubType::Mesh, s, CollisionDispatch::sReversedCollideShape);1293}12941295// Specialized collision functions1296CollisionDispatch::sRegisterCollideShape(EShapeSubType::Sphere, EShapeSubType::Mesh, sCollideSphereVsMesh);1297CollisionDispatch::sRegisterCastShape(EShapeSubType::Sphere, EShapeSubType::Mesh, sCastSphereVsMesh);1298}12991300JPH_NAMESPACE_END130113021303