PhysX icon indicating copy to clipboard operation
PhysX copied to clipboard

Sweep query penetrates triangle mesh

Open nikita-one opened this issue 3 years ago • 15 comments

Hi, again! I have noticed a strange behavior of sweep queries that I struggle to understand. The setup is simple: I perform a capsule sweep against a scene with a single static one-sided triangle mesh. The capsule is vertically aligned, and the cast direction is down, aka (0, -1, 0): image However the blocking hit reported by sweep query does not look correct. If I move the capsule at reported distance, the capsule penetrates the mesh considerably. On image below the shortest depenetration distance is ~0.06m, and if i try to depenetrate parallel to the cast direction, it would take a good ~0.5m (= the capsule's radius): image

I've checked that normals are correct, tried using a double sided mesh, tried precise sweep option, tried all combinations of cooking options, nothing seemed to help with the issue.

What did help is tilting the mesh like 0.01 degrees around X axis. Apparently doing that helps PhysX detect the edge correctly and results in no penetration: image

So my questions are:

  1. Is this a know/expected behavior, or am I doing something wrong?
  2. If this is a known limitation, and I want to guarantee no penetration, what are my options? Do I have to basically validate every sweep with subsequent overlap query, and then handle penetration manually?
  3. What causes this? It feels like some combination of capsule size, mesh triangle size and their relative orientation plays a role, but it is hard for me to tell. Maybe this issue can be avoided, if I know how to generalize the case in my example.

nikita-one avatar Nov 22 '21 09:11 nikita-one

Here is the mesh's wireframe. I don't see anything special about it, but maybe it helps: image

nikita-one avatar Nov 22 '21 09:11 nikita-one

That's unexpected and it looks like a bug. Would you have a repro by any chance? Like maybe that mesh as a OBJ file?

PierreTerdiman avatar Nov 23 '21 10:11 PierreTerdiman

That's unexpected and it looks like a bug. Would you have a repro by any chance? Like maybe that mesh as a OBJ file?

Sure. Here it is. The capsule has 1 height and 0.5 radius, and is algned with Y axis.

nikita-one avatar Nov 23 '21 16:11 nikita-one

Hmm this is what I get in my viewer. Maybe the winding is inconsistent in that mesh. I know you said you tried using a double-sided mesh but I don't know what happens in Unity when you do that. Anyway I'll investigate further asap.

image

PierreTerdiman avatar Nov 23 '21 20:11 PierreTerdiman

Oh, sorry, I think I forgot to triangulate the mesh before exporting. >_< In my simulation I use triangulated version, I have uploaded it . Checked it in Blender, I think it should be ok now. Some vertices are duplicated, but that should be fixed during welding I assume, I have it enabled.

P.S. To clarify regarding Unity: I am using Unity for visualization only in this case, the physics simulation is running in a separate process and uses native physx. That being said, I can reproduce the same behaviour with default Unity physics (which uses Physx internally as I am sure you are well aware :) ).

nikita-one avatar Nov 24 '21 01:11 nikita-one

I cannot reproduce this so far. I can translate the object with the mouse and no matter what I do the swept capsules seem to be properly located.

Can you tell me: a) which PhysX version are you using? b) the exact dimensions of the capsule? (radius / half height)

image

PierreTerdiman avatar Nov 24 '21 08:11 PierreTerdiman

I am using 4.1.2 (the latest commit in 4.1 branch). Here is what I have:

===MESH INFO=== ACTOR GLOBAL POSE: [(0 0 0), (0 0 0 1)] SHAPE LOCAL POSE: [(16 0 -17), (-0 -0 -0 1)] SCALE: (1 1 1) (0 0 0 1) MESH GEOMETRY FLAGS: 0 LOCAL BOUNDS: (0 0 -4) .. (8 0.75 0) ===SWEEP INFO=== SWEEP ORIGIN POSE: [(22.7555 4.433 -21.43088), (0 0 0.70710677 0.70710677)] SWEEP DIR: (0 -1 0) SWEEP DISTANCE: 4 CAPSULE RADIUS: 0.5 CAPSULE HALF HEIGHT: 0.5 ===SWEEP RESULT=== IS BLOCKED: True BLOCK DISTANCE: 3.6154413 BLOCK POINT: ((23 0.25 -21) BLOCK NORMAL: ((-0.4889972 0.13511719 -0.86175704) BLOCK FACE INDEX: 117 IS PENETRATED: True

nikita-one avatar Nov 24 '21 17:11 nikita-one

Same code but with very small rotation added to mesh actor produces correct block distance:

===MESH INFO=== ACTOR GLOBAL POSE: [(0 0 0), (5E-05 0 0 1)] SHAPE LOCAL POSE: [(16 0 -17), (-0 -0 -0 1)] SCALE: (1 1 1) (0 0 0 1) MESH GEOMETRY FLAGS: 0 LOCAL BOUNDS: (0 0 -4) .. (8 0.75 0) ===SWEEP INFO=== SWEEP ORIGIN POSE: [(22.7555 4.433 -21.43088), (0 0 0.70710677 0.70710677)] SWEEP DIR: (0 -1 0) SWEEP DISTANCE: 4 CAPSULE RADIUS: 0.5 CAPSULE HALF HEIGHT: 0.5 ===SWEEP RESULT=== IS BLOCKED: True BLOCK DISTANCE: 3.077834 BLOCK POINT: ((22.576239 0.67586136 -20.999933) BLOCK NORMAL: ((0.3585244 0.35860977 -0.8618929) BLOCK FACE INDEX: 98 IS PENETRATED: False

nikita-one avatar Nov 24 '21 17:11 nikita-one

I figured out how to use pvd, so I have also uploaded a pxd2 recording, maybe that one would help with repro.

nikita-one avatar Nov 25 '21 01:11 nikita-one

I can reproduce this now. I'll need some time to investigate what's going on, as the issue is not immediately clear.

PierreTerdiman avatar Nov 25 '21 14:11 PierreTerdiman

Good to hear! This bug is not a show stopper for us, but it would be nice to learn what causes this, so we can come up with decent workaround (if not straight up fix the issue). Let me know if you find anything. And thanks.

nikita-one avatar Nov 25 '21 15:11 nikita-one

FYI I fixed this one in PhysX 5 now. Not sure if/when the fix would be back-ported to PhysX 4 though.

PierreTerdiman avatar Jan 20 '22 07:01 PierreTerdiman

Good news. Sadly, we can not migrate any time soon. Any chance you can share some details about what caused this problem and what fixed it? Maybe it will help us come up with a workaround.

nikita-one avatar Jan 20 '22 09:01 nikita-one

This approach where we raycast against either a sphere or a capsule doesn't seem to work all the time: https://github.com/NVIDIAGameWorks/PhysX/blob/4.1/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp#L119

I tried several ways to improve it, nothing really worked. In the end I had to ditch it and use up to 2 raycasts-vs-capsule instead. It made the sweeps slower so I also rewrote the ray-vs-capsule code to recover lost perf.

PierreTerdiman avatar Jan 20 '22 10:01 PierreTerdiman

I think with the sweep direction, only 1 raycasts-vs-capsule needed. The code below show this.

static PX_FORCE_INLINE void chooseEdgeTest(const PxVec3& planeIntersectPoint, const PxVec3& dir, const PxVec3* PX_RESTRICT tri, PxU32 vertIntersectCandidate, PxU32 vert0, PxU32 vert1, PxU32& secondEdgeVert)
{
	const PxVec3 edge0 = tri[vertIntersectCandidate] - tri[vert0];
	{
		const PxReal edge0LengthSqr = edge0.dot(edge0);

		const PxVec3 diff = planeIntersectPoint - tri[vert0];

		if (edge0.dot(diff) < edge0LengthSqr)  // If the squared edge length is used for comparison, the edge vector does not need to be normalized
		{
			secondEdgeVert = vert0;
			return;
		}
	}

	const PxVec3 edge1 = tri[vertIntersectCandidate] - tri[vert1];
	{
		const PxReal edge1LengthSqr = edge1.dot(edge1);

		const PxVec3 diff = planeIntersectPoint - tri[vert1];

		if (edge1.dot(diff) < edge1LengthSqr)
		{
			secondEdgeVert = vert1;
			return;
		}
	}

	const PxVec3 diff = planeIntersectPoint - tri[vertIntersectCandidate];
	if (edge0.cross(edge1).cross(diff).dot(dir) >= 0.f)
	{
		secondEdgeVert = vert0;
	}
	else
	{
		secondEdgeVert = vert1;
	}
}

This approach where we raycast against either a sphere or a capsule doesn't seem to work all the time: https://github.com/NVIDIAGameWorks/PhysX/blob/4.1/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp#L119

I tried several ways to improve it, nothing really worked. In the end I had to ditch it and use up to 2 raycasts-vs-capsule instead. It made the sweeps slower so I also rewrote the ray-vs-capsule code to recover lost perf.

bleston avatar Aug 25 '22 07:08 bleston