Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/Shape/MeshShape.cpp
9913 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;321}322}323}324325// Walk over all edges and determine which ones are active326for (const EdgeToTriangle::value_type &edge : edge_to_triangle)327{328uint num_active = 0;329if (edge.second.mNumTriangles == 1)330{331// Edge is not shared, it is an active edge332num_active = 1;333}334else if (edge.second.mNumTriangles == 2)335{336// Simple shared edge, determine if edge is active based on the two adjacent triangles337const IndexedTriangle &triangle1 = ioIndices[edge.second.mTriangleIndices[0]];338const IndexedTriangle &triangle2 = ioIndices[edge.second.mTriangleIndices[1]];339340// Find which edge this is for both triangles341uint edge_idx1 = edge.first.GetIndexInTriangle(triangle1);342uint edge_idx2 = edge.first.GetIndexInTriangle(triangle2);343344// Construct a plane for triangle 1 (e1 = edge vertex 1, e2 = edge vertex 2, op = opposing vertex)345Vec3 triangle1_e1 = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[edge_idx1]]);346Vec3 triangle1_e2 = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[(edge_idx1 + 1) % 3]]);347Vec3 triangle1_op = Vec3(inSettings.mTriangleVertices[triangle1.mIdx[(edge_idx1 + 2) % 3]]);348Plane triangle1_plane = Plane::sFromPointsCCW(triangle1_e1, triangle1_e2, triangle1_op);349350// Construct a plane for triangle 2351Vec3 triangle2_e1 = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[edge_idx2]]);352Vec3 triangle2_e2 = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[(edge_idx2 + 1) % 3]]);353Vec3 triangle2_op = Vec3(inSettings.mTriangleVertices[triangle2.mIdx[(edge_idx2 + 2) % 3]]);354Plane triangle2_plane = Plane::sFromPointsCCW(triangle2_e1, triangle2_e2, triangle2_op);355356// Determine if the edge is active357num_active = ActiveEdges::IsEdgeActive(triangle1_plane.GetNormal(), triangle2_plane.GetNormal(), triangle1_e2 - triangle1_e1, inSettings.mActiveEdgeCosThresholdAngle)? 2 : 0;358}359else360{361// More edges incoming, we've already marked all edges beyond the 2nd as active362num_active = 2;363}364365// Mark edges of all original triangles active366for (uint i = 0; i < num_active; ++i)367{368uint triangle_idx = edge.second.mTriangleIndices[i];369IndexedTriangle &triangle = ioIndices[triangle_idx];370uint edge_idx = edge.first.GetIndexInTriangle(triangle);371uint32 mask = 1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT);372JPH_ASSERT((triangle.mMaterialIndex & mask) == 0);373triangle.mMaterialIndex |= mask;374}375}376}377378MassProperties MeshShape::GetMassProperties() const379{380// We cannot calculate the volume for an arbitrary mesh, so we return invalid mass properties.381// If you want your mesh to be dynamic, then you should provide the mass properties yourself when382// creating a Body:383//384// BodyCreationSettings::mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;385// BodyCreationSettings::mMassPropertiesOverride.SetMassAndInertiaOfSolidBox(Vec3::sOne(), 1000.0f);386//387// Note that for a mesh shape to simulate properly, it is best if the mesh is manifold388// (i.e. closed, all edges shared by only two triangles, consistent winding order).389return MassProperties();390}391392void MeshShape::DecodeSubShapeID(const SubShapeID &inSubShapeID, const void *&outTriangleBlock, uint32 &outTriangleIndex) const393{394// Get block395SubShapeID triangle_idx_subshape_id;396uint32 block_id = inSubShapeID.PopID(NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree)), triangle_idx_subshape_id);397outTriangleBlock = NodeCodec::DecodingContext::sGetTriangleBlockStart(&mTree[0], block_id);398399// Fetch the triangle index400SubShapeID remainder;401outTriangleIndex = triangle_idx_subshape_id.PopID(NumTriangleBits, remainder);402JPH_ASSERT(remainder.IsEmpty(), "Invalid subshape ID");403}404405uint MeshShape::GetMaterialIndex(const SubShapeID &inSubShapeID) const406{407// Decode ID408const void *block_start;409uint32 triangle_idx;410DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);411412// Fetch the flags413uint8 flags = TriangleCodec::DecodingContext::sGetFlags(block_start, triangle_idx);414return flags & FLAGS_MATERIAL_MASK;415}416417const PhysicsMaterial *MeshShape::GetMaterial(const SubShapeID &inSubShapeID) const418{419// Return the default material if there are no materials on this shape420if (mMaterials.empty())421return PhysicsMaterial::sDefault;422423return mMaterials[GetMaterialIndex(inSubShapeID)];424}425426Vec3 MeshShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const427{428// Decode ID429const void *block_start;430uint32 triangle_idx;431DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);432433// Decode triangle434Vec3 v1, v2, v3;435const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));436triangle_ctx.GetTriangle(block_start, triangle_idx, v1, v2, v3);437438// Calculate normal439return (v3 - v2).Cross(v1 - v2).Normalized();440}441442void MeshShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const443{444// Decode ID445const void *block_start;446uint32 triangle_idx;447DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);448449// Decode triangle450const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));451outVertices.resize(3);452triangle_ctx.GetTriangle(block_start, triangle_idx, outVertices[0], outVertices[1], outVertices[2]);453454// Flip triangle if scaled inside out455if (ScaleHelpers::IsInsideOut(inScale))456std::swap(outVertices[1], outVertices[2]);457458// Calculate transform with scale459Mat44 transform = inCenterOfMassTransform.PreScaled(inScale);460461// Transform to world space462for (Vec3 &v : outVertices)463v = transform * v;464}465466AABox MeshShape::GetLocalBounds() const467{468const NodeCodec::Header *header = sGetNodeHeader(mTree);469return AABox(Vec3::sLoadFloat3Unsafe(header->mRootBoundsMin), Vec3::sLoadFloat3Unsafe(header->mRootBoundsMax));470}471472uint MeshShape::GetSubShapeIDBitsRecursive() const473{474return NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree)) + NumTriangleBits;475}476477template <class Visitor>478JPH_INLINE void MeshShape::WalkTree(Visitor &ioVisitor) const479{480const NodeCodec::Header *header = sGetNodeHeader(mTree);481NodeCodec::DecodingContext node_ctx(header);482483const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));484const uint8 *buffer_start = &mTree[0];485node_ctx.WalkTree(buffer_start, triangle_ctx, ioVisitor);486}487488template <class Visitor>489JPH_INLINE void MeshShape::WalkTreePerTriangle(const SubShapeIDCreator &inSubShapeIDCreator2, Visitor &ioVisitor) const490{491struct ChainedVisitor492{493JPH_INLINE ChainedVisitor(Visitor &ioVisitor, const SubShapeIDCreator &inSubShapeIDCreator2, uint inTriangleBlockIDBits) :494mVisitor(ioVisitor),495mSubShapeIDCreator2(inSubShapeIDCreator2),496mTriangleBlockIDBits(inTriangleBlockIDBits)497{498}499500JPH_INLINE bool ShouldAbort() const501{502return mVisitor.ShouldAbort();503}504505JPH_INLINE bool ShouldVisitNode(int inStackTop) const506{507return mVisitor.ShouldVisitNode(inStackTop);508}509510JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)511{512return mVisitor.VisitNodes(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ, ioProperties, inStackTop);513}514515JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID)516{517// Create ID for triangle block518SubShapeIDCreator block_sub_shape_id = mSubShapeIDCreator2.PushID(inTriangleBlockID, mTriangleBlockIDBits);519520// Decode vertices and flags521JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);522Vec3 vertices[MaxTrianglesPerLeaf * 3];523uint8 flags[MaxTrianglesPerLeaf];524ioContext.Unpack(inTriangles, inNumTriangles, vertices, flags);525526int triangle_idx = 0;527for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, triangle_idx++)528{529// Determine active edges530uint8 active_edges = (flags[triangle_idx] >> FLAGS_ACTIVE_EGDE_SHIFT) & FLAGS_ACTIVE_EDGE_MASK;531532// Create ID for triangle533SubShapeIDCreator triangle_sub_shape_id = block_sub_shape_id.PushID(triangle_idx, NumTriangleBits);534535mVisitor.VisitTriangle(v[0], v[1], v[2], active_edges, triangle_sub_shape_id.GetID());536537// Check if we should early out now538if (mVisitor.ShouldAbort())539break;540}541}542543Visitor & mVisitor;544SubShapeIDCreator mSubShapeIDCreator2;545uint mTriangleBlockIDBits;546};547548ChainedVisitor visitor(ioVisitor, inSubShapeIDCreator2, NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree)));549WalkTree(visitor);550}551552#ifdef JPH_DEBUG_RENDERER553void MeshShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const554{555// Reset the batch if we switch coloring mode556if (mCachedTrianglesColoredPerGroup != sDrawTriangleGroups || mCachedUseMaterialColors != inUseMaterialColors)557{558mGeometry = nullptr;559mCachedTrianglesColoredPerGroup = sDrawTriangleGroups;560mCachedUseMaterialColors = inUseMaterialColors;561}562563if (mGeometry == nullptr)564{565struct Visitor566{567JPH_INLINE bool ShouldAbort() const568{569return false;570}571572JPH_INLINE bool ShouldVisitNode(int inStackTop) const573{574return true;575}576577JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)578{579UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ));580return CountAndSortTrues(valid, ioProperties);581}582583JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID)584{585JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);586Vec3 vertices[MaxTrianglesPerLeaf * 3];587ioContext.Unpack(inTriangles, inNumTriangles, vertices);588589if (mDrawTriangleGroups || !mUseMaterialColors || mMaterials.empty())590{591// Single color for mesh592Color color = mDrawTriangleGroups? Color::sGetDistinctColor(mColorIdx++) : (mUseMaterialColors? PhysicsMaterial::sDefault->GetDebugColor() : Color::sWhite);593for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3)594mTriangles.push_back({ v[0], v[1], v[2], color });595}596else597{598// Per triangle color599uint8 flags[MaxTrianglesPerLeaf];600TriangleCodec::DecodingContext::sGetFlags(inTriangles, inNumTriangles, flags);601602const uint8 *f = flags;603for (const Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, f++)604mTriangles.push_back({ v[0], v[1], v[2], mMaterials[*f & FLAGS_MATERIAL_MASK]->GetDebugColor() });605}606}607608Array<DebugRenderer::Triangle> & mTriangles;609const PhysicsMaterialList & mMaterials;610bool mUseMaterialColors;611bool mDrawTriangleGroups;612int mColorIdx = 0;613};614615Array<DebugRenderer::Triangle> triangles;616Visitor visitor { triangles, mMaterials, mCachedUseMaterialColors, mCachedTrianglesColoredPerGroup };617WalkTree(visitor);618mGeometry = new DebugRenderer::Geometry(inRenderer->CreateTriangleBatch(triangles), GetLocalBounds());619}620621// Test if the shape is scaled inside out622DebugRenderer::ECullMode cull_mode = ScaleHelpers::IsInsideOut(inScale)? DebugRenderer::ECullMode::CullFrontFace : DebugRenderer::ECullMode::CullBackFace;623624// Determine the draw mode625DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid;626627// Draw the geometry628inRenderer->DrawGeometry(inCenterOfMassTransform * Mat44::sScale(inScale), inColor, mGeometry, cull_mode, DebugRenderer::ECastShadow::On, draw_mode);629630if (sDrawTriangleOutlines)631{632struct Visitor633{634JPH_INLINE Visitor(DebugRenderer *inRenderer, RMat44Arg inTransform) :635mRenderer(inRenderer),636mTransform(inTransform)637{638}639640JPH_INLINE bool ShouldAbort() const641{642return false;643}644645JPH_INLINE bool ShouldVisitNode(int inStackTop) const646{647return true;648}649650JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)651{652UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ));653return CountAndSortTrues(valid, ioProperties);654}655656JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID)657{658// Decode vertices and flags659JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);660Vec3 vertices[MaxTrianglesPerLeaf * 3];661uint8 flags[MaxTrianglesPerLeaf];662ioContext.Unpack(inTriangles, inNumTriangles, vertices, flags);663664// Loop through triangles665const uint8 *f = flags;666for (Vec3 *v = vertices, *v_end = vertices + inNumTriangles * 3; v < v_end; v += 3, ++f)667{668// Loop through edges669for (uint edge_idx = 0; edge_idx < 3; ++edge_idx)670{671RVec3 v1 = mTransform * v[edge_idx];672RVec3 v2 = mTransform * v[(edge_idx + 1) % 3];673674// Draw active edge as a green arrow, other edges as grey675if (*f & (1 << (edge_idx + FLAGS_ACTIVE_EGDE_SHIFT)))676mRenderer->DrawArrow(v1, v2, Color::sGreen, 0.01f);677else678mRenderer->DrawLine(v1, v2, Color::sGrey);679}680}681}682683DebugRenderer * mRenderer;684RMat44 mTransform;685};686687Visitor visitor { inRenderer, inCenterOfMassTransform.PreScaled(inScale) };688WalkTree(visitor);689}690}691#endif // JPH_DEBUG_RENDERER692693bool MeshShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const694{695JPH_PROFILE_FUNCTION();696697struct Visitor698{699JPH_INLINE explicit Visitor(RayCastResult &ioHit) :700mHit(ioHit)701{702}703704JPH_INLINE bool ShouldAbort() const705{706return mHit.mFraction <= 0.0f;707}708709JPH_INLINE bool ShouldVisitNode(int inStackTop) const710{711return mDistanceStack[inStackTop] < mHit.mFraction;712}713714JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)715{716// Test bounds of 4 children717Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);718719// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)720return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]);721}722723JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, uint32 inTriangleBlockID)724{725// Test against triangles726uint32 triangle_idx;727float fraction = ioContext.TestRay(mRayOrigin, mRayDirection, inTriangles, inNumTriangles, mHit.mFraction, triangle_idx);728if (fraction < mHit.mFraction)729{730mHit.mFraction = fraction;731mHit.mSubShapeID2 = mSubShapeIDCreator.PushID(inTriangleBlockID, mTriangleBlockIDBits).PushID(triangle_idx, NumTriangleBits).GetID();732mReturnValue = true;733}734}735736RayCastResult & mHit;737Vec3 mRayOrigin;738Vec3 mRayDirection;739RayInvDirection mRayInvDirection;740uint mTriangleBlockIDBits;741SubShapeIDCreator mSubShapeIDCreator;742bool mReturnValue = false;743float mDistanceStack[NodeCodec::StackSize];744};745746Visitor visitor(ioHit);747visitor.mRayOrigin = inRay.mOrigin;748visitor.mRayDirection = inRay.mDirection;749visitor.mRayInvDirection.Set(inRay.mDirection);750visitor.mTriangleBlockIDBits = NodeCodec::DecodingContext::sTriangleBlockIDBits(sGetNodeHeader(mTree));751visitor.mSubShapeIDCreator = inSubShapeIDCreator;752WalkTree(visitor);753754return visitor.mReturnValue;755}756757void MeshShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const758{759JPH_PROFILE_FUNCTION();760761// Test shape filter762if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))763return;764765struct Visitor766{767JPH_INLINE explicit Visitor(CastRayCollector &ioCollector) :768mCollector(ioCollector)769{770}771772JPH_INLINE bool ShouldAbort() const773{774return mCollector.ShouldEarlyOut();775}776777JPH_INLINE bool ShouldVisitNode(int inStackTop) const778{779return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction();780}781782JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)783{784// Test bounds of 4 children785Vec4 distance = RayAABox4(mRayOrigin, mRayInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);786787// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)788return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);789}790791JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, [[maybe_unused]] uint8 inActiveEdges, SubShapeID inSubShapeID2)792{793// Back facing check794if (mBackFaceMode == EBackFaceMode::IgnoreBackFaces && (inV2 - inV0).Cross(inV1 - inV0).Dot(mRayDirection) < 0)795return;796797// Check the triangle798float fraction = RayTriangle(mRayOrigin, mRayDirection, inV0, inV1, inV2);799if (fraction < mCollector.GetEarlyOutFraction())800{801RayCastResult hit;802hit.mBodyID = TransformedShape::sGetBodyID(mCollector.GetContext());803hit.mFraction = fraction;804hit.mSubShapeID2 = inSubShapeID2;805mCollector.AddHit(hit);806}807}808809CastRayCollector & mCollector;810Vec3 mRayOrigin;811Vec3 mRayDirection;812RayInvDirection mRayInvDirection;813EBackFaceMode mBackFaceMode;814float mDistanceStack[NodeCodec::StackSize];815};816817Visitor visitor(ioCollector);818visitor.mBackFaceMode = inRayCastSettings.mBackFaceModeTriangles;819visitor.mRayOrigin = inRay.mOrigin;820visitor.mRayDirection = inRay.mDirection;821visitor.mRayInvDirection.Set(inRay.mDirection);822WalkTreePerTriangle(inSubShapeIDCreator, visitor);823}824825void MeshShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const826{827sCollidePointUsingRayCast(*this, inPoint, inSubShapeIDCreator, ioCollector, inShapeFilter);828}829830void MeshShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const CollideSoftBodyVertexIterator &inVertices, uint inNumVertices, int inCollidingShapeIndex) const831{832JPH_PROFILE_FUNCTION();833834struct Visitor : public CollideSoftBodyVerticesVsTriangles835{836using CollideSoftBodyVerticesVsTriangles::CollideSoftBodyVerticesVsTriangles;837838JPH_INLINE bool ShouldAbort() const839{840return false;841}842843JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const844{845return mDistanceStack[inStackTop] < mClosestDistanceSq;846}847848JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)849{850// Get distance to vertex851Vec4 dist_sq = AABox4DistanceSqToPoint(mLocalPosition, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);852853// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)854return SortReverseAndStore(dist_sq, mClosestDistanceSq, ioProperties, &mDistanceStack[inStackTop]);855}856857JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, [[maybe_unused]] uint8 inActiveEdges, [[maybe_unused]] SubShapeID inSubShapeID2)858{859ProcessTriangle(inV0, inV1, inV2);860}861862float mDistanceStack[NodeCodec::StackSize];863};864865Visitor visitor(inCenterOfMassTransform, inScale);866867for (CollideSoftBodyVertexIterator v = inVertices, sbv_end = inVertices + inNumVertices; v != sbv_end; ++v)868if (v.GetInvMass() > 0.0f)869{870visitor.StartVertex(v);871WalkTreePerTriangle(SubShapeIDCreator(), visitor);872visitor.FinishVertex(v, inCollidingShapeIndex);873}874}875876void 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)877{878JPH_PROFILE_FUNCTION();879880struct Visitor : public CastConvexVsTriangles881{882using CastConvexVsTriangles::CastConvexVsTriangles;883884JPH_INLINE bool ShouldAbort() const885{886return mCollector.ShouldEarlyOut();887}888889JPH_INLINE bool ShouldVisitNode(int inStackTop) const890{891return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction();892}893894JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)895{896// Scale the bounding boxes of this node897Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;898AABox4Scale(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);899900// Enlarge them by the casted shape's box extents901AABox4EnlargeWithExtent(mBoxExtent, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);902903// Test bounds of 4 children904Vec4 distance = RayAABox4(mBoxCenter, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);905906// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)907return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);908}909910JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)911{912Cast(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);913}914915RayInvDirection mInvDirection;916Vec3 mBoxCenter;917Vec3 mBoxExtent;918float mDistanceStack[NodeCodec::StackSize];919};920921JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Mesh);922const MeshShape *shape = static_cast<const MeshShape *>(inShape);923924Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector);925visitor.mInvDirection.Set(inShapeCast.mDirection);926visitor.mBoxCenter = inShapeCast.mShapeWorldBounds.GetCenter();927visitor.mBoxExtent = inShapeCast.mShapeWorldBounds.GetExtent();928shape->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);929}930931void 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)932{933JPH_PROFILE_FUNCTION();934935struct Visitor : public CastSphereVsTriangles936{937using CastSphereVsTriangles::CastSphereVsTriangles;938939JPH_INLINE bool ShouldAbort() const940{941return mCollector.ShouldEarlyOut();942}943944JPH_INLINE bool ShouldVisitNode(int inStackTop) const945{946return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction();947}948949JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)950{951// Scale the bounding boxes of this node952Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;953AABox4Scale(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);954955// Enlarge them by the radius of the sphere956AABox4EnlargeWithExtent(Vec3::sReplicate(mRadius), bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);957958// Test bounds of 4 children959Vec4 distance = RayAABox4(mStart, mInvDirection, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);960961// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)962return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);963}964965JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)966{967Cast(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);968}969970RayInvDirection mInvDirection;971float mDistanceStack[NodeCodec::StackSize];972};973974JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Mesh);975const MeshShape *shape = static_cast<const MeshShape *>(inShape);976977Visitor visitor(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector);978visitor.mInvDirection.Set(inShapeCast.mDirection);979shape->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);980}981982struct MeshShape::MSGetTrianglesContext983{984JPH_INLINE MSGetTrianglesContext(const MeshShape *inShape, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) :985mDecodeCtx(sGetNodeHeader(inShape->mTree)),986mShape(inShape),987mLocalBox(Mat44::sInverseRotationTranslation(inRotation, inPositionCOM), inBox),988mMeshScale(inScale),989mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale)),990mIsInsideOut(ScaleHelpers::IsInsideOut(inScale))991{992}993994JPH_INLINE bool ShouldAbort() const995{996return mShouldAbort;997}998999JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const1000{1001return true;1002}10031004JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const1005{1006// Scale the bounding boxes of this node1007Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;1008AABox4Scale(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);10091010// Test which nodes collide1011UVec4 collides = AABox4VsBox(mLocalBox, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);1012return CountAndSortTrues(collides, ioProperties);1013}10141015JPH_INLINE void VisitTriangles(const TriangleCodec::DecodingContext &ioContext, const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID)1016{1017// 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.1018if (mNumTrianglesFound + inNumTriangles > mMaxTrianglesRequested)1019{1020mShouldAbort = true;1021return;1022}10231024// Decode vertices1025JPH_ASSERT(inNumTriangles <= MaxTrianglesPerLeaf);1026Vec3 vertices[MaxTrianglesPerLeaf * 3];1027ioContext.Unpack(inTriangles, inNumTriangles, vertices);10281029// Store vertices as Float31030if (mIsInsideOut)1031{1032// Scaled inside out, flip the triangles1033for (const Vec3 *v = vertices, *v_end = v + 3 * inNumTriangles; v < v_end; v += 3)1034{1035(mLocalToWorld * v[0]).StoreFloat3(mTriangleVertices++);1036(mLocalToWorld * v[2]).StoreFloat3(mTriangleVertices++);1037(mLocalToWorld * v[1]).StoreFloat3(mTriangleVertices++);1038}1039}1040else1041{1042// Normal scale1043for (const Vec3 *v = vertices, *v_end = v + 3 * inNumTriangles; v < v_end; ++v)1044(mLocalToWorld * *v).StoreFloat3(mTriangleVertices++);1045}10461047if (mMaterials != nullptr)1048{1049if (mShape->mMaterials.empty())1050{1051// No materials, output default1052const PhysicsMaterial *default_material = PhysicsMaterial::sDefault;1053for (int m = 0; m < inNumTriangles; ++m)1054*mMaterials++ = default_material;1055}1056else1057{1058// Decode triangle flags1059uint8 flags[MaxTrianglesPerLeaf];1060TriangleCodec::DecodingContext::sGetFlags(inTriangles, inNumTriangles, flags);10611062// Store materials1063for (const uint8 *f = flags, *f_end = f + inNumTriangles; f < f_end; ++f)1064*mMaterials++ = mShape->mMaterials[*f & FLAGS_MATERIAL_MASK].GetPtr();1065}1066}10671068// Accumulate triangles found1069mNumTrianglesFound += inNumTriangles;1070}10711072NodeCodec::DecodingContext mDecodeCtx;1073const MeshShape * mShape;1074OrientedBox mLocalBox;1075Vec3 mMeshScale;1076Mat44 mLocalToWorld;1077int mMaxTrianglesRequested;1078Float3 * mTriangleVertices;1079int mNumTrianglesFound;1080const PhysicsMaterial ** mMaterials;1081bool mShouldAbort;1082bool mIsInsideOut;1083};10841085void MeshShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const1086{1087static_assert(sizeof(MSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small");1088JPH_ASSERT(IsAligned(&ioContext, alignof(MSGetTrianglesContext)));10891090new (&ioContext) MSGetTrianglesContext(this, inBox, inPositionCOM, inRotation, inScale);1091}10921093int MeshShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const1094{1095static_assert(cGetTrianglesMinTrianglesRequested >= MaxTrianglesPerLeaf, "cGetTrianglesMinTrianglesRequested is too small");1096JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested);10971098// Check if we're done1099MSGetTrianglesContext &context = (MSGetTrianglesContext &)ioContext;1100if (context.mDecodeCtx.IsDoneWalking())1101return 0;11021103// Store parameters on context1104context.mMaxTrianglesRequested = inMaxTrianglesRequested;1105context.mTriangleVertices = outTriangleVertices;1106context.mMaterials = outMaterials;1107context.mShouldAbort = false; // Reset the abort flag1108context.mNumTrianglesFound = 0;11091110// Continue (or start) walking the tree1111const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));1112const uint8 *buffer_start = &mTree[0];1113context.mDecodeCtx.WalkTree(buffer_start, triangle_ctx, context);1114return context.mNumTrianglesFound;1115}11161117void 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)1118{1119JPH_PROFILE_FUNCTION();11201121// Get the shapes1122JPH_ASSERT(inShape1->GetType() == EShapeType::Convex);1123JPH_ASSERT(inShape2->GetType() == EShapeType::Mesh);1124const ConvexShape *shape1 = static_cast<const ConvexShape *>(inShape1);1125const MeshShape *shape2 = static_cast<const MeshShape *>(inShape2);11261127struct Visitor : public CollideConvexVsTriangles1128{1129using CollideConvexVsTriangles::CollideConvexVsTriangles;11301131JPH_INLINE bool ShouldAbort() const1132{1133return mCollector.ShouldEarlyOut();1134}11351136JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const1137{1138return true;1139}11401141JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const1142{1143// Scale the bounding boxes of this node1144Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;1145AABox4Scale(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);11461147// Test which nodes collide1148UVec4 collides = AABox4VsBox(mBoundsOf1InSpaceOf2, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);1149return CountAndSortTrues(collides, ioProperties);1150}11511152JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)1153{1154Collide(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);1155}1156};11571158Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector);1159shape2->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);1160}11611162void 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)1163{1164JPH_PROFILE_FUNCTION();11651166// Get the shapes1167JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Sphere);1168JPH_ASSERT(inShape2->GetType() == EShapeType::Mesh);1169const SphereShape *shape1 = static_cast<const SphereShape *>(inShape1);1170const MeshShape *shape2 = static_cast<const MeshShape *>(inShape2);11711172struct Visitor : public CollideSphereVsTriangles1173{1174using CollideSphereVsTriangles::CollideSphereVsTriangles;11751176JPH_INLINE bool ShouldAbort() const1177{1178return mCollector.ShouldEarlyOut();1179}11801181JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const1182{1183return true;1184}11851186JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const1187{1188// Scale the bounding boxes of this node1189Vec4 bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z;1190AABox4Scale(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);11911192// Test which nodes collide1193UVec4 collides = AABox4VsSphere(mSphereCenterIn2, mRadiusPlusMaxSeparationSq, bounds_min_x, bounds_min_y, bounds_min_z, bounds_max_x, bounds_max_y, bounds_max_z);1194return CountAndSortTrues(collides, ioProperties);1195}11961197JPH_INLINE void VisitTriangle(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, uint8 inActiveEdges, SubShapeID inSubShapeID2)1198{1199Collide(inV0, inV1, inV2, inActiveEdges, inSubShapeID2);1200}1201};12021203Visitor visitor(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector);1204shape2->WalkTreePerTriangle(inSubShapeIDCreator2, visitor);1205}12061207void MeshShape::SaveBinaryState(StreamOut &inStream) const1208{1209Shape::SaveBinaryState(inStream);12101211inStream.Write(static_cast<const ByteBufferVector &>(mTree)); // Make sure we use the Array<> overload1212}12131214void MeshShape::RestoreBinaryState(StreamIn &inStream)1215{1216Shape::RestoreBinaryState(inStream);12171218inStream.Read(static_cast<ByteBufferVector &>(mTree)); // Make sure we use the Array<> overload1219}12201221void MeshShape::SaveMaterialState(PhysicsMaterialList &outMaterials) const1222{1223outMaterials = mMaterials;1224}12251226void MeshShape::RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials)1227{1228mMaterials.assign(inMaterials, inMaterials + inNumMaterials);1229}12301231Shape::Stats MeshShape::GetStats() const1232{1233// Walk the tree to count the triangles1234struct Visitor1235{1236JPH_INLINE bool ShouldAbort() const1237{1238return false;1239}12401241JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const1242{1243return true;1244}12451246JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const1247{1248// Visit all valid children1249UVec4 valid = UVec4::sOr(UVec4::sOr(Vec4::sLess(inBoundsMinX, inBoundsMaxX), Vec4::sLess(inBoundsMinY, inBoundsMaxY)), Vec4::sLess(inBoundsMinZ, inBoundsMaxZ));1250return CountAndSortTrues(valid, ioProperties);1251}12521253JPH_INLINE void VisitTriangles([[maybe_unused]] const TriangleCodec::DecodingContext &ioContext, [[maybe_unused]] const void *inTriangles, int inNumTriangles, [[maybe_unused]] uint32 inTriangleBlockID)1254{1255mNumTriangles += inNumTriangles;1256}12571258uint mNumTriangles = 0;1259};12601261Visitor visitor;1262WalkTree(visitor);12631264return Stats(sizeof(*this) + mMaterials.size() * sizeof(Ref<PhysicsMaterial>) + mTree.size() * sizeof(uint8), visitor.mNumTriangles);1265}12661267uint32 MeshShape::GetTriangleUserData(const SubShapeID &inSubShapeID) const1268{1269// Decode ID1270const void *block_start;1271uint32 triangle_idx;1272DecodeSubShapeID(inSubShapeID, block_start, triangle_idx);12731274// Decode triangle1275const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));1276return triangle_ctx.GetUserData(block_start, triangle_idx);1277}12781279void MeshShape::sRegister()1280{1281ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Mesh);1282f.mConstruct = []() -> Shape * { return new MeshShape; };1283f.mColor = Color::sRed;12841285for (EShapeSubType s : sConvexSubShapeTypes)1286{1287CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::Mesh, sCollideConvexVsMesh);1288CollisionDispatch::sRegisterCastShape(s, EShapeSubType::Mesh, sCastConvexVsMesh);12891290CollisionDispatch::sRegisterCastShape(EShapeSubType::Mesh, s, CollisionDispatch::sReversedCastShape);1291CollisionDispatch::sRegisterCollideShape(EShapeSubType::Mesh, s, CollisionDispatch::sReversedCollideShape);1292}12931294// Specialized collision functions1295CollisionDispatch::sRegisterCollideShape(EShapeSubType::Sphere, EShapeSubType::Mesh, sCollideSphereVsMesh);1296CollisionDispatch::sRegisterCastShape(EShapeSubType::Sphere, EShapeSubType::Mesh, sCastSphereVsMesh);1297}12981299JPH_NAMESPACE_END130013011302