Page 1 of 1

SimpleCharacterController and StaticTriangleGroup issues

Posted: Sun Dec 12, 2010 4:12 pm
by Genstein
Hey there,

First of all, I'm very impressed with BEPUphysics. It works exceedingly well, is staggeringly easy to integrate and is nicely documented. And the multithreading is a breeze, very well thought out. Top stuff.

I'm getting good results from the SimpleCharacterController included in the demo source. I had to make a small fix to it: it appears that surface normals are not being rotated by the supporting entity's transform. In my test level this meant that the player could walk on crates (Box entities) if they had the default orientation, but otherwise the player would "get stuck" as the controller thought the character was on a slope.

I changed the end of the FindSupport() method from:

Code: Select all

    supportNormal.Normalize();
    return supportDistance < float.MaxValue;
to:

Code: Select all

    supportNormal.Normalize();
    if (supportDistance < float.MaxValue)
    {
        supportNormal = Vector3.Transform(supportNormal, supportEntity.InternalOrientationMatrix);
        return true;
    }
    return false;
I also found the single ray cast from the centre of the player done by FindSupport() can easily miss geometry (such as when the player is standing on the corner of a crate). I've gotten better results by doing multiple ray casts, changing the code from:

Code: Select all

    if (candidate.RayTest(rayOrigin, Vector3.Down, maximumDistance, false, out hitLocation, out hitNormal, out distance))
    {
        ...
    }
to:

Code: Select all

    var offset = Body.Radius;
    var delta = Body.Radius / 5f;
    for (var ox = -offset; ox <= offset; ox += delta)
    {
        for (var oz = -offset; oz <= offset; oz += delta)
        {
            var newRayOrigin = new Vector3(rayOrigin.X + ox, rayOrigin.Y, rayOrigin.Z + oz);
            if (candidate.RayTest(newRayOrigin, Vector3.Down, maximumDistance, false, out hitLocation, out hitNormal, out distance))
            {
                ...
            }
        }
    }
which is perhaps a few too many rays, but seems happier.

I have a couple of issues. As background, my level consists of a StaticTriangleGroup built from a TriangleMesh, as recommended in the getting started guide (I didn't have much success using kinematic boxes as physics slowed to a crawl once there were a few hundred of them). The level is pretty much square and axis aligned, so generally the player is walking on a grid made up of pairs of 2m square triangles. The player is 0.5m across and 1m tall and crates are 0.5m cubes. As noted I'm using the SimpleCharacterController to move the player around and jump.

The first issue is that when the player is pushing crates around the level, the ground seems to be "bumpy"; crates seem to flip over periodically, I'm guessing as they cross triangle boundaries, even though their neighbours have the same normal, are the same size in a regular grid, etc. This didn't happen when using kinematic boxes. Is there anything I can do to prevent this?

The second issue is that jumping (at 5m/sec) seems to quite frequently get the player "stuck" in the ceiling, which is a grid of 2m triangles just like the floor. It only happens about one time in four, depending on where on the grid I jump. I'm guessing it's related to the floor bumpiness problem, something to do with the handling of triangle meshes. Is there any way I can avoid this?

All the best,
-eg.

Re: SimpleCharacterController and StaticTriangleGroup issues

Posted: Sun Dec 12, 2010 6:03 pm
by Norbo
First of all, I'm very impressed with BEPUphysics. It works exceedingly well, is staggeringly easy to integrate and is nicely documented. And the multithreading is a breeze, very well thought out. Top stuff.
Thanks, glad you like it!
it appears that surface normals are not being rotated by the supporting entity's transform.
Woops, that appears to be a bug in the Box's raycast special case. It's been fixed for v0.15.0.
I also found the single ray cast from the centre of the player done by FindSupport() can easily miss geometry
That's true; using a bunch of rays is a simple and valid way of addressing the issue. There's also fancier options that use convex-casts, like the one in the non-simple character controller.
The first issue is that when the player is pushing crates around the level, the ground seems to be "bumpy"; crates seem to flip over periodically, I'm guessing as they cross triangle boundaries
The bumps do indeed come from the mesh boundaries. Each triangle produces contacts with the sliding box in isolation, so the box can hit the 'side' of the triangle despite it being an internal edge of the mesh. One way to lessen the bumps is to force the system to to use triangle normals more often. This can have some side effects, but they are rare.

Code: Select all

            group.TryToUseTriangleNormals = true;
            group.UseFaceNormalWithinAngle = MathHelper.PiOver2;
One of the next couple of versions is also going to include a mesh-edge collision filtering system that solves the problem more completely.
The second issue is that jumping (at 5m/sec) seems to quite frequently get the player "stuck" in the ceiling, which is a grid of 2m triangles just like the floor.
Forcing the system to use the triangle normals may help a bit, depending on what is actually causing it. If it's going fast enough that it gets lodged between two triangles and then fails to slide out, it will help. v0.15.0's one-sided triangles option will help with that too.

Enabling continuous collision detection (Space.SimulationSettings.CollisionDetection.CollisionDetectionType = Linear or FullyContinuous) would help too, but it has a small performance/behavior impact.

Re: SimpleCharacterController and StaticTriangleGroup issues

Posted: Wed Dec 15, 2010 2:49 pm
by Genstein
Thanks so much for the fast response - despite this rather delayed reply I put your answers to use immediately and they were very helpful.

Re the ray cast bug, that's good to know - have made a note in the relevant code. Now I just have to remember to take out the fudge when I upgrade :)

TryToUseTriangleNormals has helped a lot, it's substantially smoothed out the floor, so thanks for that. The odd little bounce remains but I can live with it until the next version.

I've made some headway w.r.t. getting stuck in the ceiling, but I'm still confused. I tried switching the SimpleCharacterController.Body from a capsule to a cylinder; that made the problem occur 100% of the time. It turns out that the body was being deactivated by something at the peak of the jump. I'm not sure why; I'm moderatey convinced this isn't something I've done (I have virtually no physics code other than the SimpleCharacterController), but it's always possible. Setting Body.IsActive = true in UpdateAtEndOfUpdate() "fixes" the problem, as does setting IsAlwaysActive once. Any suggestions as to why this might occur are appreciated!

I have another side question: now that I'm casting multiple rays to detect the edges of crates, etc. I find that the player can step up onto a surface of any height with no restrictions, and so will step onto a crate rather than push it around. Is there a way to reliably constrain this? In terms of wishful thinking I'd ideally like a "maximum step up height" setting on SimpleCharacterController.

I'm also finding that jumping onto a bunch of crates is ending up with the character getting easily stuck, with its collision pair collector box wedged partly inside a crate. Is there a way around this?

I'm wondering if I should switch to the non-simple controller and whether it'd resolve these issues. All advice gratefully received.

All the best,
-eg.

Re: SimpleCharacterController and StaticTriangleGroup issues

Posted: Wed Dec 15, 2010 5:11 pm
by Norbo
I tried switching the SimpleCharacterController.Body from a capsule to a cylinder; that made the problem occur 100% of the time. It turns out that the body was being deactivated by something at the peak of the jump.
Going from capsule to cylinder should not have any effect on the deactivation, unless the deactivation is somehow related to a collision. If the entity is wedging itself between triangles in the ceiling, there is a change that it could go to sleep before it makes its way out due to low velocities. However, even if the entity were always active, the 'wedging' would still be noticeable. If it's not actually being wedged between triangles but is just going inactive by itself, make sure the linear/angular clamping in space.SimulationSettings.Deactivation have not been changed (I doubt they have).

A video/repro case/picture might help me understand the situation better. In the mean time, if IsAlwaysActive 'fixes' it, its a reasonable workaround.
I have another side question: now that I'm casting multiple rays to detect the edges of crates, etc. I find that the player can step up onto a surface of any height with no restrictions, and so will step onto a crate rather than push it around. Is there a way to reliably constrain this? In terms of wishful thinking I'd ideally like a "maximum step up height" setting on SimpleCharacterController.
The reason why it's happening is that the rays are 'exposed' such that the ray can detect things before the body has a chance to obstruct them. The default implementation of the Simple character just has a single ray in the middle, making it very unlikely that it will step on something that shouldn't be stepped on. Its 'maximum step height' is defined by the length of the ray that is exposed at the bottom of the character.
I'm also finding that jumping onto a bunch of crates is ending up with the character getting easily stuck, with its collision pair collector box wedged partly inside a crate. Is there a way around this?
It sounds like the collector box and raycast origins need to be shifted so that they are originating in the body. If the rays/collector do not start in the body, it will miss supports that are closer to the body than the ray origins.
I'm wondering if I should switch to the non-simple controller and whether it'd resolve these issues. All advice gratefully received.
It would resolve some of the issues. It automatically tries to handle maximum step heights and other behaviors. However, it is more complicated, and relies on a convex cast which is slightly less numerically stable than a simple raycast which can constrain geometry dimensions in an environment more than usual. There are also occasional issues with mesh edge collisions and false supports.

Character controllers can be pretty complicated, particularly if you try to get fancy like the non-simple one. v0.15.0 is going to make a few changes which will require some changes to the non-simple character controller, so I'll probably go ahead and improve it at that point too (a little after v0.15.0's release).

Re: SimpleCharacterController and StaticTriangleGroup issues

Posted: Thu Dec 16, 2010 12:21 am
by Genstein
That's very enlightening, thanks! I begin to grasp how the simple character controller works.

I'll try and put together a useful repro case for the ceiling problem, but for now IsAlwaysActive is doing the job. I now note that the non-simple character controller uses it too.

You're right, bringing all the rays' origins into inside the controller's body have prevented that step up effect. Stupid of me not to have noticed that. In the code I've moved the collector box origin up into the body as well (shifted it up by half the "support height" parameter). This made things a little better.

It seems I can still easily get stuck on crates though. I've further "improved" the rays tests by casting rays down all around the edge of the body cylinder, and again at half the cylinder's radius. Rather more rays, but there is less getting stuck; I'm going to need quite a lot of rays to avoid any problems though (I'm guessing one every few degrees around the circumference).

However there's a more fundamental problem: if the rays are nicely within the body's circumference, it's easy to "get stuck" on the edges of crates simply because the rays will miss them entirely. If I move the rays outside the body I get the step up effect. If I place them as exactly on the perimeter as possible, I get a mixture of both problems.

I suspect I need to "L2P" in some more notable way. I'm wondering if anyone else has had such issues, how they got around them, and indeed what approaches other games take. I may have to invest in gaining more clue, which is unfortunate but not unsurprising.

All the best,
-eg.

Re: SimpleCharacterController and StaticTriangleGroup issues

Posted: Thu Dec 16, 2010 2:43 am
by Norbo
Using a cylinder pretty much requires that a convex cast is used to avoid getting stuck on the edges. Otherwise, you can use enough rays to simulate a convex cast :) Either way, the problem of 'stepping' up walls or high obstacles once again returns.

Using a capsule simplifies the situation a little. You can create a convex cast or multiple rays and, if they fail to hit something, the capsule's rounded shape will let it slide off the obstacle with no significant harm done. Sometimes, when using that triangle normal hack, you will see the capsule stick on a right-angle mesh edge. This is relatively rare and can be combated by allowing the user to have 'air control'- the ability to adjust its velocity slightly when its rays can't find something. Combined with no friction on the player capsule, the occasional mesh-edge catch will go unnoticed.
I suspect I need to "L2P" in some more notable way. I'm wondering if anyone else has had such issues, how they got around them, and indeed what approaches other games take.
It's not an 'easy' problem, and it's not just you ;)

The solution is not that bad, though. The general idea is:
-Find a valid (not intersecting geometry) start location within the stepping area.
-Convex cast down from that start location. For a cylinder, sweeping a disc (0-height cylinder) would work.

There's additional complications in the details, like how preventing the character from stepping into the ceiling (valid step location located, tries to step up on it, ceiling isn't tall enough, head gets stuck). These extra problems can be solved with additional testing (like a sweep above the head).

The non-simple character controller implements these ideas, but there are other possibilities than what is shown in the demos. Some of its issues could be solved by a little more 'brute force'- more tests to handle the awkward cases.

The 'use a single raycast and capsule' approach is a lot less work if it works for the intended purpose, though. :)