jrouwe/JoltPhysics

`CylinderShape` vs `MeshShape` yields odd collision data

Closed this issue · 4 comments

mihe commented

Relates to godot-jolt/godot-jolt#789.

The scenario is pretty straight-forward. There's a CollideShape call with a CylinderShape that's placed right at the edge of a MeshShape, and just from looking at it I feel like it shouldn't result in a penetration depth of more than a couple of millimeters, if that, but the actual reported penetration depth is instead 0.397210, meaning almost 80% of the cylinder's entire diameter.

In usual fashion I've hijacked SimpleTest.

The whole scene is offset a bit, but you should see it if you look around.

I also added drawing of the hit itself, much like what you do in samples like CapsuleVsBoxTest, but for some odd reason they end up being drawn way below the actual collision, which I can't quite figure out the reason for. It might be that it's related to the issue itself, or perhaps I'm just not doing the drawing correctly.

#pragma once

#include <Tests/Test.h>

class SimpleTest : public Test
{
public:
	JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, SimpleTest)

	void				Initialize() override;
	void				PrePhysicsUpdate(const PreUpdateParams &inParams) override;

private:
	RefConst<Shape>		mMeshShape;
	RefConst<Shape>		mCylinderShape;
};
#include <TestFramework.h>

#include <Tests/General/SimpleTest.h>
#include <Jolt/Physics/Collision/CollideShape.h>
#include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
#include <Jolt/Physics/Collision/CollisionDispatch.h>
#include <Jolt/Physics/Collision/Shape/CylinderShape.h>
#include <Jolt/Physics/Collision/Shape/MeshShape.h>
#include <Utils/DebugRendererSP.h>
#include <Utils/Log.h>
#include <Layers.h>

JPH_IMPLEMENT_RTTI_VIRTUAL(SimpleTest)
{
	JPH_ADD_BASE_CLASS(SimpleTest, Test)
}

void SimpleTest::Initialize()
{
	mMeshShape = MeshShapeSettings({
		Triangle(
			Vec3(-48.8670234680176, -5.23126983642578, 14.3259973526001),
			Vec3(-48.8670234680176, 0, 14.3259973526001),
			Vec3(-43.001579284668, -5.23126983642578, 12.422794342041)
		),
		Triangle(
			Vec3(-48.8670234680176, 0, 14.3259973526001),
			Vec3(-43.001579284668, 0, 12.422794342041),
			Vec3(-43.001579284668, -5.23126983642578, 12.422794342041)
		),
		Triangle(
			Vec3(-42.7954330444336, -4.86970233917236, 12.422794342041),
			Vec3(-29.9111347198486, -4.86970233917236, 12.422794342041),
			Vec3(-43.001579284668, -5.23126983642578, 12.422794342041)
		),
		Triangle(
			Vec3(-29.9111347198486, -4.86970233917236, 12.422794342041),
			Vec3(-29.6869583129883, -5.23126983642578, 12.422794342041),
			Vec3(-43.001579284668, -5.23126983642578, 12.422794342041)
		),
		Triangle(
			Vec3(-42.7954330444336, -0.64731895923615, 12.422794342041),
			Vec3(-42.7954330444336, -4.86970233917236, 12.422794342041),
			Vec3(-43.001579284668, 0, 12.422794342041)
		),
		Triangle(
			Vec3(-42.7954330444336, -4.86970233917236, 12.422794342041),
			Vec3(-43.001579284668, -5.23126983642578, 12.422794342041),
			Vec3(-43.001579284668, 0, 12.422794342041)
		),
		Triangle(
			Vec3(-42.7954330444336, -0.64731895923615, 12.422794342041),
			Vec3(-29.9111347198486, -0.64731895923615, 12.422794342041),
			Vec3(-42.7954330444336, -4.86970233917236, 12.422794342041)
		),
		Triangle(
			Vec3(-29.9111347198486, -0.64731895923615, 12.422794342041),
			Vec3(-29.9111347198486, -4.86970233917236, 12.422794342041),
			Vec3(-42.7954330444336, -4.86970233917236, 12.422794342041)
		)
	}).Create().Get();

	mCylinderShape = new CylinderShape(0.85f, 0.25f, 0.02f);
}

void SimpleTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
{
	const RMat44 mesh_transform = Mat44::sIdentity();
	const RMat44 cylinder_transform = Mat44::sTranslation(Vec3(-42.8155518f, -4.32299995f, 12.1734285f));

	CollideShapeSettings settings;
	settings.mMaxSeparationDistance = 0.001f;

	ClosestHitCollisionCollector<CollideShapeCollector> collector;

	CollisionDispatch::sCollideShapeVsShape(mCylinderShape, mMeshShape, Vec3::sReplicate(1.0f), Vec3::sReplicate(1.0f), cylinder_transform, mesh_transform, SubShapeIDCreator(), SubShapeIDCreator(), settings, collector);

#ifdef JPH_DEBUG_RENDERER
	mMeshShape->Draw(mDebugRenderer, mesh_transform, Vec3::sReplicate(1.0f), Color::sGrey, false, false);
	mCylinderShape->Draw(mDebugRenderer, cylinder_transform, Vec3::sReplicate(1.0f), Color::sBlue, false, false);
#endif // JPH_DEBUG_RENDERER

	if (!collector.HadHit())
	{
		Trace("No hit?");
		return;
	}

	const CollideShapeResult &hit = collector.mHit;

	DrawMarkerSP(mDebugRenderer, hit.mContactPointOn1, Color::sRed, 0.2f);
	DrawMarkerSP(mDebugRenderer, hit.mContactPointOn2, Color::sGreen, 0.2f);

	Vec3 pen_axis = hit.mPenetrationAxis;
	const float pen_axis_len = pen_axis.Length();

	if (pen_axis_len > 0.0f)
	{
		pen_axis *= hit.mPenetrationDepth / pen_axis_len;
		DrawArrowSP(mDebugRenderer, hit.mContactPointOn2, hit.mContactPointOn2 + pen_axis, Color::sYellow, 0.01f);
	}

	Trace("Penetration depth: %f", hit.mPenetrationDepth);
}

Thanks, I will investigate!

Can you try #1024?

Because the cylinder and the triangle are parallel, GJK iterates until it produces a degenerate simplex (triangle in this case) and GetBaryCentricCoordinates returned values way outside the triangle due to floating point precision issues so the hit positions were also outside of the shape.

mihe commented

That does seem to fix the issue in the original repro, as far as I can tell.

Thank you!

Thanks for testing!