Raycast against cone uses margin, other primitives do not

Discuss any questions about BEPUphysics or problems encountered.
Post Reply
Frooxius
Posts: 12
Joined: Tue Aug 16, 2016 4:01 am

Raycast against cone uses margin, other primitives do not

Post by Frooxius » Tue Aug 23, 2016 9:08 pm

Hello, it's me again!

I've encountered another relatively minor issue. I noticed that all raycasts against a Cone seem to hit above its surface, while for all the other primitives its hits right at the surface.

When I went through the code, I noticed that ConeShape is the only one which doesn't have its RayTest method overridden and instead uses the generic method in MPRToolBox instead, which seems to take into account the margin.

I found that a quick (and dirty?) way to remove the inconsistency is to override the RayTest method, only to set the collisionMargin to zero before running the MPRToolBox method and returning it to original afterwards, like so:

Code: Select all

public override bool RayTest(ref Ray ray, ref RigidTransform transform, float maximumLength, out RayHit hit)
        {
            var _tempMargin = collisionMargin;
            collisionMargin = 0f;

            var result =  base.RayTest(ref ray, ref transform, maximumLength, out hit);

            collisionMargin = _tempMargin;
            return result;
        }
Could this cause some significant problems? How does this compare to writing a dedicated method for a cone as the other primitives have? (I suspect it has suboptimal performance, but other than that I don't know)

Norbo
Site Admin
Posts: 4895
Joined: Tue Jul 04, 2006 4:45 am

Re: Raycast against cone uses margin, other primitives do no

Post by Norbo » Tue Aug 23, 2016 10:26 pm

The use of margins in ray tests is mostly driven by whether or not the collision profile of the shape uses an internal or external collision margin.

Boxes, spheres, capsules, and cylinders all have internal margins. So, during collisions that involve general convex-convex tests, no part of their contact generating surface extends beyond the shape definition. Due to the hard edges in their definition, boxes and cylinders actually have their corners shaved off in those margin-involved tests (technically an inconsistency with how boxes behave in special cases like box-box and box-sphere).

Cones, convex hulls, transformable shapes, minkowski sums, triangles, and wrapped shapes all have external margins. In collisions, all of them generate contacts according to a surface which extends beyond the raw definition by the collision margin.

Cones in particular have external margins because giving them an internal margin would drastically change the shape- just like a box's corners and edges get shaved, a cone's tip and edges would get shaved. The difference between boxes and cones is that cones often have acute angles which magnify the shaved volume. An internal margin would produce pretty surprising results for highly acute cones.

Accordingly, the box, sphere, capsule, and cylinder all use ray tests which match the shape definition exactly, while the rest use a margin-expanded version.

One notable exception to the above is triangles. They're a bit special- while an individual TriangleShape does technically expand by the margin during collision, they tend to be used exclusively by meshes which have zero margin of their own. Their ray cast includes no margin.
Could this cause some significant problems? How does this compare to writing a dedicated method for a cone as the other primitives have? (I suspect it has suboptimal performance, but other than that I don't know)
If you want the cone to have zero margin and a ray test consistent with its collision behavior, then just setting its collision margin to 0 at initialization would work. I wouldn't recommend modifying the collision margin within the ray test, though- multithreaded ray tests would cause race conditions. (The character controller is one system that uses multithreaded ray tests.) A dedicated cone ray test with zero margin would indeed be faster, but it would be inconsistent with the collision behavior.

The primary consequence of zero margin is worse performance during regular collision detection. Shallow margin contacts use a faster GJK implementation, while deep contact uses multiple iterations of MPR. You may get slightly worse contact stability sometimes too. Nothing too concerning.

(Sidenote: margins are something I'd like to get rid of as a concept in v2, but I haven't yet found an option that fully replaces the shallow GJK phase with the same numerical quality and performance characteristics. I might be able to hammer on my MPR implementation and its contact generation to make it good enough, or come up with some crazy new thing, but no promises.)

Frooxius
Posts: 12
Joined: Tue Aug 16, 2016 4:01 am

Re: Raycast against cone uses margin, other primitives do no

Post by Frooxius » Wed Aug 24, 2016 7:22 pm

Thanks for the detailed explanation!

I see. I was worried that setting the collision margin to 0 would cause problems in other bits, but if they aren't significant, I'll just do that.

Post Reply