Sphere Trace for Sphere primitives in Skeletal Meshes

Hiya!

We’ve ran into a fairly interesting issue to do with sphere traces and sphere colliders in Skeletal Meshes’ physics assets. The issue at hand is that the sphere trace seems to not be detecting the blocking events correctly, sometimes doing it inconsistently and other times pretty much ignoring them. The most interesting thing about it is that using Line Trace By Channel seems to work completely fine which is quite odd..

It’s worth mentioning this issue seems to have started happening after our upgrade to 5.7 from 5.6 (which we merged to our codebase, so it’s possible this issue was caused by this merge, however we suspect we would have had code conflicts if that were the case). We’ve also successfully reproduced the issue in editor.

We’re using a custom trace channel with the following parameters (DefaultEngine.ini) and adjusted a Skeletal Mesh Actor’s trace collision responses so it blocks any events from this channel:

[/Script/Engine.CollisionProfile]

+DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Ignore,bTraceType=True,bStaticObject=False,Name=“MouseTargeting”)

What we’re observing is that if the physics asset is set with spheres, when using the following Sphere Trace parameters most times the events will NOT register as blocking events which. CCD is set to Disabled for the Physics Asset and the Skeletal Mesh, and for reference we’ve provided the part we’ve added to a player controller blueprint which we’re using in the given map as well as a screenshot of a skeletal mesh actor with the TPP tutorial mesh interacting with the sphere trace while having spheres as colliders and another screenshot with capsules.

[Image Removed]

Using Spheres in the Skeletal Mesh, only some of them seem to be registered

[Image Removed]

[Image Removed]

Using Capsules in the Skeletal Mesh, all of them appeared to have registered as hits

[Image Removed]

[Image Removed]

Looking forward to hear any thoughts on the matter!

Adrian

[Attachment Removed]

Steps to Reproduce

We cannot provide a repro project for this at the moment but as far as we are aware you should be able to repro in any version after 5.7 as long as it contains the additional trace channel as well as the correct setup in the physics assets. Steps required:

  • Sample project that contains 1 skeletal mesh and a physics asset assigned to that skeletal mesh
  • CCD Disabled
  • Choosing one collider in that physics asset and ensuring it’s using a sphere collider
  • Adding the Trace Channel to the engine’s settings (which we’ve provide in the description)
  • Adding a bit of code or blueprint functionality to cast a line trace by channel, making sure it intersects with the Sphere colliders in the Skeletal Mesh (also provided in the issue’s description)
    [Attachment Removed]

Hi Adrian,

We haven’t heard anything similar that I’m aware of. Would you be able to repro this in a vanilla version of 5.7 and see if it happens there? If so - we can dig into that project and find out what has regressed.

Thanks

Geoff Stacey

Developer Relations

EPIC Games

[Attachment Removed]

Hi Alex,

The Vanilla sample will help a lot. Essentially what we won’t have (and can affect outcomes a lot) is the config and setup - this is hidden information which can at time be critical to finding a timely answer - it also helps reduce lots of follow up questions!

If you folks can send that across, we can investigate.

Thanks

Geoff

[Attachment Removed]

Hi Adrián

Thanks for the repro project, really helps. I can see the issue in vanilla 5.7 - not with the regular sphere collider, just with spherical colliders on the skeletal mesh.

It looks like it has been fixed in 5.8 - can you confirm? (the preview is out now)

I’ll try to narrow down the fix to a specific CL, but I thought I’d ask what the motivation is for the move to sphere colliders. They represent the legs pretty crudely - that doesn’t throw up any issue for you?

Chris

[Attachment Removed]

Hi Adrián

Finally tracked this down. It wasn’t so mysterious in the end.

It’s due to an assumption made in the SweepSphereVsSphere function. There’s even a comment there which I should have paid more attention to (line 75 in GeometryQueries.cpp):

// Transform to the test shape's local space. Note: We can skip rotation which is much fasterThat’s usually true as rotating a sphere does nothing so most spheres will have a 0,0,0 center, but this optimisation is incorrect when either sphere has a non-zero local center offset (i.e. GetCenterf() != [0,0,0]). The local center offset lives in the body’s local space and must be rotated into world space before the position arithmetic is done. Skipping this step produces a LocalSweepStart that is in the wrong position, causing the discriminant in the underlying RaySphere quadratic to come out negative — reporting a miss despite the geometries visually overlapping.

For static sphere components the center is typically at the body’s local origin, so GetCenterf() is zero and the original code is correct. Physics asset sphere bodies however use the bone transform, meaning GetCenterf() returns a non-zero local offset that requires the rotation to be applied.

I’ve suggested a fix below which checks if the center is non-zero and performs the transformation if required. Note we don’t then pass the (potentially non-zero) TestSphere center into the sweep function as we’ve already accounted for that in the transformation. I pass in a zero vector instead.

This will have a slight performance hit which I guess could be an issue for a game making heavy use of this functionality. It’ll still be more performant than the capsule colliders for you though.

bool SweepSphereVsSphere(const FSphere& SweptSphere, const FRigidTransform3& SweptSphereTM, const FSphere& TestSphere, const FRigidTransform3& TestSphereTM,
	const FVec3& SweepDir, const FReal Length, const FReal Thickness, const bool bComputeMTD,
	FReal& OutTime, FVec3& OutPosition, FVec3& OutNormal, int32& OutFaceIndex, FVec3& OutFaceNormal)
{
	const Sweeps::ESweepFlags Flags = bComputeMTD ? Sweeps::ESweepFlags::MTD : Sweeps::ESweepFlags::None;
	// Transform to the test shape's local space. 
	const FVec3 TestGeomLocalToWorld = TestSphereTM.GetLocation();
	const FVec3 SweptShapeLocalToWorld = SweptSphereTM.GetLocation();
	const FVec3 LocalSweepDir = SweepDir;
 
	FVec3 LocalSweepStart;
	if (TestSphere.GetCenterf().IsNearlyZero() && SweptSphere.GetCenterf().IsNearlyZero())
	{
		LocalSweepStart = SweptShapeLocalToWorld - TestGeomLocalToWorld;
	}
	else
	{
		const FVec3 WorldTestSphereCenter = TestSphereTM.TransformPosition(FVector(TestSphere.GetCenterf()));
		const FVec3 WorldSweptSphereCenter = SweptSphereTM.TransformPosition(FVector(SweptSphere.GetCenterf()));
		LocalSweepStart = WorldSweptSphereCenter - WorldTestSphereCenter;
	}
 
	const FReal SweepRadius = Thickness + SweptSphere.GetRadiusf();
 
	const bool bResult = Sweeps::SweepSphereVsSphere(LocalSweepStart, LocalSweepDir, Length, SweepRadius, FVec3(0), TestSphere.GetRadiusf(), Flags, OutTime, OutPosition, OutNormal);
	TransformSweepResultsToWorld(bResult, OutTime, bComputeMTD, TestSphere, TestGeomLocalToWorld, LocalSweepDir, OutPosition, OutNormal, OutFaceIndex, OutPosition, OutNormal, OutFaceNormal);
	return bResult;
}

Let me know if that works for you.

Chris

[Attachment Removed]

Actually…

I think we need to change the call to TransformSweepResultsToWorld as well. We’re passing in TestGeomLocalToWorld which is used to calculate world-space hit position:

OutWorldPosition = TestGeomLocation + LocalPosition;TestGeomLocation is just TestSphereTM.GetLocation() — the body’s world position without accounting for the sphere’s local center offset. This was correct in the original code since the center was always zero, but with the fix in place the working space origin is now WorldTestSphereCenter (the full world-space center including the rotated offset). Using TestGeomLocation instead produces a hit position that is offset by the same error as the original bug.

The fix is to compute WorldTestSphereCenter in both code paths and pass it as the origin to TransformSweepResultsToWorld rather than TestGeomLocalToWorld.

FVec3 LocalSweepStart;
FVec3 WorldTestSphereCenter;
 
if (TestSphere.GetCenterf().IsNearlyZero() && SweptSphere.GetCenterf().IsNearlyZero())
{
	LocalSweepStart = SweptShapeLocalToWorld - TestGeomLocalToWorld;
	WorldTestSphereCenter = TestGeomLocalToWorld;
}
else
{
	WorldTestSphereCenter = TestSphereTM.TransformPosition(FVector(TestSphere.GetCenterf()));
	const FVec3 WorldSweptSphereCenter = SweptSphereTM.TransformPosition(FVector(SweptSphere.GetCenterf()));
	LocalSweepStart = WorldSweptSphereCenter - WorldTestSphereCenter;
}
 
const FReal SweepRadius = Thickness + SweptSphere.GetRadiusf();
 
const bool bResult = Sweeps::SweepSphereVsSphere(LocalSweepStart, LocalSweepDir, Length, SweepRadius, FVec3(0), TestSphere.GetRadiusf(), Flags, OutTime, OutPosition, OutNormal);
TransformSweepResultsToWorld(bResult, OutTime, bComputeMTD, TestSphere, WorldTestSphereCenter, LocalSweepDir, OutPosition, OutNormal, OutFaceIndex, OutPosition, OutNormal, OutFaceNormal);
return bResult;

Honestly the difference is pretty hard to judge in the test project - I need to set up a skeletal mesh with a large offset to check properly.

[Attachment Removed]

Hi all, closing this off since it appears all resolved :slight_smile:

Geoff

[Attachment Removed]

Hi Geoff, Adrian is on holiday this week but I can confirm he reproduced the bug in a vanilla sample. The repro steps he’s provided should showcase the issue, will that suffice?

Thanks,

Alex

[Attachment Removed]

Hey Geoff!

Thanks for the responses - as Alex has mentioned I’ve been on holiday for a bit. I’ve created a mini project in 5.7.4 (downloaded directly from the Epic Games Launcher) and managed to reproduce the issue.

The entire project with the map, meshes and custom collision channel should be included in SphereCollisionRepro but please let me know if there’s any issues - the gist of it is that it seems like the sphere traces fail to register a collision at the top-most part of the sphere. I’ve also attached an image showing the registered hits when the sphere traces are pointed towards the center of the sphere rather than the top.

[Image Removed]^ The image above is using sphere trace, and the image below is using line trace on the same skeletal mesh.

[Image Removed]

The project also includes a bp’d player controller to shoot the traces with left click, and right click changes between sphere trace and line trace. CCD is enabled for both meshes too, in case that’s relevant.

Thanks!

Adrian

[Attachment Removed]

Looks like the project didn’t get attached to the first reply, sorry!

[Attachment Removed]

Hey Chris, thanks for your reply!

The sample project was only meant to serve as a representation of the issue and isn’t really what we’re using in our actual project - the project itself uses capsule colliders for most parts of the mesh(es) but happens to be using spheres for some of them (and those parts never register the hits). We can modify the physics assets to use capsules and that would fix the issues (in the same way that we could use line traces instead sphere traces) but we decided not to given the nature of the problem (it being that we suspected it was a code issue).

It’s great to know it’s been fixed in 5.8 - I’ve not tested locally but I can fetch the preview and give it a try. We don’t have any plans on upgrading to 5.8 so would be great to know the CL where it was fixed.

Thanks!

Adrian

[Attachment Removed]

I’ve just re-made the same project in 5.8 and it seems to have very similar problems to register the hits so I’ll wait until a CL is provided to verify the fix.

Thanks!

Adrian

[Attachment Removed]

Ah sorry, rechecking that I see the same issue. I should have trusted Geoff when he said we hadn’t seen anything similar.

I see the “crown of thorns” type pattern below, maybe I set up with too low an angle previously.

So obviously no CL, but I’ll get more info.

[Image Removed]

[Attachment Removed]

Hey Chris, sorry for the late reply!

Just giving this thread a polite bump and checking if there’s any more info related to it? Right now we’re not in a massive rush of getting this fixed (and as we mentioned we’d be able to just change the physics asset to use capsules if push comes to shove) but we’d like to understand why this issue started happening in the first place.

Thanks!

Adrian

[Attachment Removed]

Hi Adrian,

I’m taking a look into this at the moment. I’ve tracked down where the end result is diverging (ie the RaySphere function), but that could just be reacting to some other imprecision. So far it _looks_ like it could be a floating point error - but that isn’t confirmed yet.

Best

Geoff

[Attachment Removed]

Hey Chris, thanks for your response!

I see what you mean, if it never accounts for the offset it probably expects the sphere to be in a completely different position so that makes sense. Some of the cases in our project actually have a pretty large offset so that has been a good test for it, glad to say it seemed to fix the issue :). I’ve integrated into our version of the engine.

Thanks a lot for the support!

Adrian

[Attachment Removed]