UPrimitiveComponent::ComputePenetration returns wrong MTD when the shape is a Box

Turns out UPrimitiveComponent::ComputePenetration returns wrong values when the UPrimitiveComponent is a box. It’s weird because, for example, checking a Capsule or a Sphere against a Box works fine, but checking the same Box against the Capsule/Sphere returns worng values. Boxes against other boxes also returns wrong values.

Doesn’t happen always (depends on the position and the size of the shapes), but in a lot of cases.
I can share a small project that reproduces the issue.

Here’s a screenshot that shows the issue:

The highlighted log shows the MTD that the Box is returning, the next MTD in the log is what the capsule returns against the same collision, wich makes more sense.

Hi TS100101,

Please post the small project here. If you’re not comfortable posting it on a public site, feel free to private message me a link to download it on the Forums.

https://forums.unrealengine.com/

Hi TJ, thanks for your answer.

Here’s the project :

I’ve been looking into the source code and seems like the problem is that the function assumes that the CollisionShape is always a capsule.

I’ve written my own modified ComputePenetration function and seems to work fine in all cases. Here’s the code in case someone wants to take a look :

bool UPrimitiveComponent::ComputePenetrationCustom(FMTDResult & OutMTD, UPrimitiveComponent& CollisionPrimitive)
{
#if WITH_PHYSX
	const PxRigidActor* PRigidBody0 = CollisionPrimitive.BodyInstance.GetPxRigidActor();
	if (PRigidBody0 == NULL || PRigidBody0->getNbShapes() == 0)
	{
		return false;
	}
	const PxTransform PGlobalPose0 = PRigidBody0->getGlobalPose();

	const PxRigidActor* PRigidBody1 = BodyInstance.GetPxRigidActor();
	if (PRigidBody1 == NULL || PRigidBody1->getNbShapes() == 0)
	{
		return false;
	}
	const PxTransform PGlobalPose1 = PRigidBody1->getGlobalPose();

	// Get all the shapes from the actors
	// TODO: we should really pass the shape from the overlap info since doing it this way we do an overlap test twice
	TArray<PxShape*> PShapes0;
	PShapes0.AddZeroed(PRigidBody0->getNbShapes());
	int32 NumTargetShapes0 = PRigidBody0->getShapes(PShapes0.GetData(), PShapes0.Num());

	TArray<PxShape*> PShapes1;
	PShapes1.AddZeroed(PRigidBody1->getNbShapes());
	int32 NumTargetShapes1 = PRigidBody1->getShapes(PShapes1.GetData(), PShapes1.Num());

	for (int32 PShapeIdx0 = 0; PShapeIdx0 < PShapes0.Num(); ++PShapeIdx0)
	{
		const PxShape * PShape0 = PShapes0[PShapeIdx0];
		check(PShape0);

		// Calc shape global pose
		PxTransform PGeomPose0 = PGlobalPose0.transform(PShape0->getLocalPose());
		GeometryFromShapeStorage GeomStorage0;
		PxGeometry * PGeom0 = GetGeometryFromShape(GeomStorage0, PShape0, true);

		if (PGeom0)
		{
			for (int32 PShapeIdx1 = 0; PShapeIdx1 < PShapes1.Num(); ++PShapeIdx1)
			{
				const PxShape * PShape1 = PShapes1[PShapeIdx1];
				check(PShape1);

				// Calc shape global pose
				PxTransform PGeomPose1 = PGlobalPose1.transform(PShape1->getLocalPose());
				GeometryFromShapeStorage GeomStorage1;
				PxGeometry * PGeom1 = GetGeometryFromShape(GeomStorage1, PShape1, true);

				if (PGeom1)
				{
					PxVec3 POutDirection;
					bool bSuccess = PxGeometryQuery::computePenetration(POutDirection, OutMTD.Distance, *PGeom0, PGeomPose0, *PGeom1, PGeomPose1);
					if (bSuccess)
					{
						if (POutDirection.isFinite())
						{
							OutMTD.Direction = P2UVector(POutDirection);
							return true;
						}
						else
						{
							UE_LOG(LogPhysics, Warning, TEXT("UPrimitiveComponent::ComputePenetration: MTD returned NaN"));
							return false;
						}
					}
				}
			}
		}
	}
#endif

	return false;
}