Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/Shape/TriangleShape.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/TriangleShape.h>7#include <Jolt/Physics/Collision/Shape/ScaleHelpers.h>8#include <Jolt/Physics/Collision/Shape/GetTrianglesContext.h>9#include <Jolt/Physics/Collision/RayCast.h>10#include <Jolt/Physics/Collision/ShapeCast.h>11#include <Jolt/Physics/Collision/CastResult.h>12#include <Jolt/Physics/Collision/CollidePointResult.h>13#include <Jolt/Physics/Collision/TransformedShape.h>14#include <Jolt/Physics/Collision/CastConvexVsTriangles.h>15#include <Jolt/Physics/Collision/CastSphereVsTriangles.h>16#include <Jolt/Physics/Collision/CollideConvexVsTriangles.h>17#include <Jolt/Physics/Collision/CollideSphereVsTriangles.h>18#include <Jolt/Physics/Collision/CollisionDispatch.h>19#include <Jolt/Physics/Collision/CollideSoftBodyVerticesVsTriangles.h>20#include <Jolt/Geometry/ConvexSupport.h>21#include <Jolt/Geometry/RayTriangle.h>22#include <Jolt/Geometry/ClosestPoint.h>23#include <Jolt/ObjectStream/TypeDeclarations.h>24#include <Jolt/Core/StreamIn.h>25#include <Jolt/Core/StreamOut.h>26#ifdef JPH_DEBUG_RENDERER27#include <Jolt/Renderer/DebugRenderer.h>28#endif // JPH_DEBUG_RENDERER2930JPH_NAMESPACE_BEGIN3132JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(TriangleShapeSettings)33{34JPH_ADD_BASE_CLASS(TriangleShapeSettings, ConvexShapeSettings)3536JPH_ADD_ATTRIBUTE(TriangleShapeSettings, mV1)37JPH_ADD_ATTRIBUTE(TriangleShapeSettings, mV2)38JPH_ADD_ATTRIBUTE(TriangleShapeSettings, mV3)39JPH_ADD_ATTRIBUTE(TriangleShapeSettings, mConvexRadius)40}4142ShapeSettings::ShapeResult TriangleShapeSettings::Create() const43{44if (mCachedResult.IsEmpty())45Ref<Shape> shape = new TriangleShape(*this, mCachedResult);46return mCachedResult;47}4849TriangleShape::TriangleShape(const TriangleShapeSettings &inSettings, ShapeResult &outResult) :50ConvexShape(EShapeSubType::Triangle, inSettings, outResult),51mV1(inSettings.mV1),52mV2(inSettings.mV2),53mV3(inSettings.mV3),54mConvexRadius(inSettings.mConvexRadius)55{56if (inSettings.mConvexRadius < 0.0f)57{58outResult.SetError("Invalid convex radius");59return;60}6162outResult.Set(this);63}6465AABox TriangleShape::GetLocalBounds() const66{67AABox bounds(mV1, mV1);68bounds.Encapsulate(mV2);69bounds.Encapsulate(mV3);70bounds.ExpandBy(Vec3::sReplicate(mConvexRadius));71return bounds;72}7374AABox TriangleShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const75{76JPH_ASSERT(IsValidScale(inScale));7778Vec3 v1 = inCenterOfMassTransform * (inScale * mV1);79Vec3 v2 = inCenterOfMassTransform * (inScale * mV2);80Vec3 v3 = inCenterOfMassTransform * (inScale * mV3);8182AABox bounds(v1, v1);83bounds.Encapsulate(v2);84bounds.Encapsulate(v3);85bounds.ExpandBy(inScale * mConvexRadius);86return bounds;87}8889class TriangleShape::TriangleNoConvex final : public Support90{91public:92TriangleNoConvex(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) :93mTriangleSupport(inV1, inV2, inV3)94{95static_assert(sizeof(TriangleNoConvex) <= sizeof(SupportBuffer), "Buffer size too small");96JPH_ASSERT(IsAligned(this, alignof(TriangleNoConvex)));97}9899virtual Vec3 GetSupport(Vec3Arg inDirection) const override100{101return mTriangleSupport.GetSupport(inDirection);102}103104virtual float GetConvexRadius() const override105{106return 0.0f;107}108109private:110TriangleConvexSupport mTriangleSupport;111};112113class TriangleShape::TriangleWithConvex final : public Support114{115public:116TriangleWithConvex(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, float inConvexRadius) :117mConvexRadius(inConvexRadius),118mTriangleSupport(inV1, inV2, inV3)119{120static_assert(sizeof(TriangleWithConvex) <= sizeof(SupportBuffer), "Buffer size too small");121JPH_ASSERT(IsAligned(this, alignof(TriangleWithConvex)));122}123124virtual Vec3 GetSupport(Vec3Arg inDirection) const override125{126Vec3 support = mTriangleSupport.GetSupport(inDirection);127float len = inDirection.Length();128if (len > 0.0f)129support += (mConvexRadius / len) * inDirection;130return support;131}132133virtual float GetConvexRadius() const override134{135return mConvexRadius;136}137138private:139float mConvexRadius;140TriangleConvexSupport mTriangleSupport;141};142143const ConvexShape::Support *TriangleShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const144{145switch (inMode)146{147case ESupportMode::IncludeConvexRadius:148case ESupportMode::Default:149if (mConvexRadius > 0.0f)150return new (&inBuffer) TriangleWithConvex(inScale * mV1, inScale * mV2, inScale * mV3, mConvexRadius);151[[fallthrough]];152153case ESupportMode::ExcludeConvexRadius:154return new (&inBuffer) TriangleNoConvex(inScale * mV1, inScale * mV2, inScale * mV3);155}156157JPH_ASSERT(false);158return nullptr;159}160161void TriangleShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const162{163JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID");164165// Calculate transform with scale166Mat44 transform = inCenterOfMassTransform.PreScaled(inScale);167168// Flip triangle if scaled inside out169if (ScaleHelpers::IsInsideOut(inScale))170{171outVertices.push_back(transform * mV1);172outVertices.push_back(transform * mV3);173outVertices.push_back(transform * mV2);174}175else176{177outVertices.push_back(transform * mV1);178outVertices.push_back(transform * mV2);179outVertices.push_back(transform * mV3);180}181}182183MassProperties TriangleShape::GetMassProperties() const184{185// We cannot calculate the volume for a triangle, so we return invalid mass properties.186// If you want your triangle to be dynamic, then you should provide the mass properties yourself when187// creating a Body:188//189// BodyCreationSettings::mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;190// BodyCreationSettings::mMassPropertiesOverride.SetMassAndInertiaOfSolidBox(Vec3::sOne(), 1000.0f);191//192// Note that this makes the triangle shape behave the same as a mesh shape with a single triangle.193// In practice there is very little use for a dynamic triangle shape as back side collisions will be ignored194// so if the triangle falls the wrong way it will sink through the floor.195return MassProperties();196}197198Vec3 TriangleShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const199{200JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID");201202Vec3 cross = (mV2 - mV1).Cross(mV3 - mV1);203float len = cross.Length();204return len != 0.0f? cross / len : Vec3::sAxisY();205}206207void TriangleShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const208{209// A triangle has no volume210outTotalVolume = outSubmergedVolume = 0.0f;211outCenterOfBuoyancy = Vec3::sZero();212}213214#ifdef JPH_DEBUG_RENDERER215void TriangleShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const216{217RVec3 v1 = inCenterOfMassTransform * (inScale * mV1);218RVec3 v2 = inCenterOfMassTransform * (inScale * mV2);219RVec3 v3 = inCenterOfMassTransform * (inScale * mV3);220221if (ScaleHelpers::IsInsideOut(inScale))222std::swap(v1, v2);223224if (inDrawWireframe)225inRenderer->DrawWireTriangle(v1, v2, v3, inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor);226else227inRenderer->DrawTriangle(v1, v2, v3, inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor);228}229#endif // JPH_DEBUG_RENDERER230231bool TriangleShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const232{233float fraction = RayTriangle(inRay.mOrigin, inRay.mDirection, mV1, mV2, mV3);234if (fraction < ioHit.mFraction)235{236ioHit.mFraction = fraction;237ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID();238return true;239}240return false;241}242243void TriangleShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const244{245// Test shape filter246if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))247return;248249// Back facing check250if (inRayCastSettings.mBackFaceModeTriangles == EBackFaceMode::IgnoreBackFaces && (mV2 - mV1).Cross(mV3 - mV1).Dot(inRay.mDirection) > 0.0f)251return;252253// Test ray against triangle254float fraction = RayTriangle(inRay.mOrigin, inRay.mDirection, mV1, mV2, mV3);255if (fraction < ioCollector.GetEarlyOutFraction())256{257// Better hit than the current hit258RayCastResult hit;259hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext());260hit.mFraction = fraction;261hit.mSubShapeID2 = inSubShapeIDCreator.GetID();262ioCollector.AddHit(hit);263}264}265266void TriangleShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const267{268// Can't be inside a triangle269}270271void TriangleShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const CollideSoftBodyVertexIterator &inVertices, uint inNumVertices, int inCollidingShapeIndex) const272{273CollideSoftBodyVerticesVsTriangles collider(inCenterOfMassTransform, inScale);274275for (CollideSoftBodyVertexIterator v = inVertices, sbv_end = inVertices + inNumVertices; v != sbv_end; ++v)276if (v.GetInvMass() > 0.0f)277{278collider.StartVertex(v);279collider.ProcessTriangle(mV1, mV2, mV3);280collider.FinishVertex(v, inCollidingShapeIndex);281}282}283284void TriangleShape::sCollideConvexVsTriangle(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)285{286JPH_ASSERT(inShape1->GetType() == EShapeType::Convex);287const ConvexShape *shape1 = static_cast<const ConvexShape *>(inShape1);288JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::Triangle);289const TriangleShape *shape2 = static_cast<const TriangleShape *>(inShape2);290291CollideConvexVsTriangles collider(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector);292collider.Collide(shape2->mV1, shape2->mV2, shape2->mV3, 0b111, inSubShapeIDCreator2.GetID());293}294295void TriangleShape::sCollideSphereVsTriangle(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)296{297JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Sphere);298const SphereShape *shape1 = static_cast<const SphereShape *>(inShape1);299JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::Triangle);300const TriangleShape *shape2 = static_cast<const TriangleShape *>(inShape2);301302CollideSphereVsTriangles collider(shape1, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1.GetID(), inCollideShapeSettings, ioCollector);303collider.Collide(shape2->mV1, shape2->mV2, shape2->mV3, 0b111, inSubShapeIDCreator2.GetID());304}305306void TriangleShape::sCastConvexVsTriangle(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)307{308JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Triangle);309const TriangleShape *shape = static_cast<const TriangleShape *>(inShape);310311CastConvexVsTriangles caster(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector);312caster.Cast(shape->mV1, shape->mV2, shape->mV3, 0b111, inSubShapeIDCreator2.GetID());313}314315void TriangleShape::sCastSphereVsTriangle(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)316{317JPH_ASSERT(inShape->GetSubType() == EShapeSubType::Triangle);318const TriangleShape *shape = static_cast<const TriangleShape *>(inShape);319320CastSphereVsTriangles caster(inShapeCast, inShapeCastSettings, inScale, inCenterOfMassTransform2, inSubShapeIDCreator1, ioCollector);321caster.Cast(shape->mV1, shape->mV2, shape->mV3, 0b111, inSubShapeIDCreator2.GetID());322}323324class TriangleShape::TSGetTrianglesContext325{326public:327TSGetTrianglesContext(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) : mV1(inV1), mV2(inV2), mV3(inV3) { }328329Vec3 mV1;330Vec3 mV2;331Vec3 mV3;332333bool mIsDone = false;334};335336void TriangleShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const337{338static_assert(sizeof(TSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small");339JPH_ASSERT(IsAligned(&ioContext, alignof(TSGetTrianglesContext)));340341Mat44 m = Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale);342343new (&ioContext) TSGetTrianglesContext(m * mV1, m * mV2, m * mV3);344}345346int TriangleShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const347{348static_assert(cGetTrianglesMinTrianglesRequested >= 3, "cGetTrianglesMinTrianglesRequested is too small");349JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested);350351TSGetTrianglesContext &context = (TSGetTrianglesContext &)ioContext;352353// Only return the triangle the 1st time354if (context.mIsDone)355return 0;356context.mIsDone = true;357358// Store triangle359context.mV1.StoreFloat3(outTriangleVertices);360context.mV2.StoreFloat3(outTriangleVertices + 1);361context.mV3.StoreFloat3(outTriangleVertices + 2);362363// Store material364if (outMaterials != nullptr)365*outMaterials = GetMaterial();366367return 1;368}369370void TriangleShape::SaveBinaryState(StreamOut &inStream) const371{372ConvexShape::SaveBinaryState(inStream);373374inStream.Write(mV1);375inStream.Write(mV2);376inStream.Write(mV3);377inStream.Write(mConvexRadius);378}379380void TriangleShape::RestoreBinaryState(StreamIn &inStream)381{382ConvexShape::RestoreBinaryState(inStream);383384inStream.Read(mV1);385inStream.Read(mV2);386inStream.Read(mV3);387inStream.Read(mConvexRadius);388}389390bool TriangleShape::IsValidScale(Vec3Arg inScale) const391{392return ConvexShape::IsValidScale(inScale) && (mConvexRadius == 0.0f || ScaleHelpers::IsUniformScale(inScale.Abs()));393}394395Vec3 TriangleShape::MakeScaleValid(Vec3Arg inScale) const396{397Vec3 scale = ScaleHelpers::MakeNonZeroScale(inScale);398399if (mConvexRadius == 0.0f)400return scale;401402return scale.GetSign() * ScaleHelpers::MakeUniformScale(scale.Abs());403}404405void TriangleShape::sRegister()406{407ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Triangle);408f.mConstruct = []() -> Shape * { return new TriangleShape; };409f.mColor = Color::sGreen;410411for (EShapeSubType s : sConvexSubShapeTypes)412{413CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::Triangle, sCollideConvexVsTriangle);414CollisionDispatch::sRegisterCastShape(s, EShapeSubType::Triangle, sCastConvexVsTriangle);415416CollisionDispatch::sRegisterCollideShape(EShapeSubType::Triangle, s, CollisionDispatch::sReversedCollideShape);417CollisionDispatch::sRegisterCastShape(EShapeSubType::Triangle, s, CollisionDispatch::sReversedCastShape);418}419420// Specialized collision functions421CollisionDispatch::sRegisterCollideShape(EShapeSubType::Sphere, EShapeSubType::Triangle, sCollideSphereVsTriangle);422CollisionDispatch::sRegisterCastShape(EShapeSubType::Sphere, EShapeSubType::Triangle, sCastSphereVsTriangle);423}424425JPH_NAMESPACE_END426427428